From fb7ef7f309401bf886508da371be338d745ccc6a Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 14 Jan 2025 20:12:24 +0100 Subject: [PATCH 01/30] Plugin created --- docker/osd-dev/dev.yml | 1 + plugins/wazuh-security-policies/.i18nrc.json | 7 ++ plugins/wazuh-security-policies/README.md | 22 ++++ .../wazuh-security-policies/common/index.ts | 2 + .../opensearch_dashboards.json | 9 ++ plugins/wazuh-security-policies/package.json | 10 ++ .../public/application.tsx | 23 ++++ .../public/components/app.tsx | 116 ++++++++++++++++++ .../wazuh-security-policies/public/index.scss | 1 + .../wazuh-security-policies/public/index.ts | 13 ++ .../wazuh-security-policies/public/plugin.ts | 64 ++++++++++ .../wazuh-security-policies/public/types.ts | 11 ++ .../wazuh-security-policies/server/index.ts | 14 +++ .../wazuh-security-policies/server/plugin.ts | 41 +++++++ .../server/routes/index.ts | 16 +++ .../wazuh-security-policies/server/types.ts | 5 + .../translations/ja-JP.json | 81 ++++++++++++ plugins/wazuh-security-policies/tsconfig.json | 16 +++ plugins/wazuh-security-policies/yarn.lock | 4 + 19 files changed, 456 insertions(+) create mode 100644 plugins/wazuh-security-policies/.i18nrc.json create mode 100755 plugins/wazuh-security-policies/README.md create mode 100644 plugins/wazuh-security-policies/common/index.ts create mode 100644 plugins/wazuh-security-policies/opensearch_dashboards.json create mode 100644 plugins/wazuh-security-policies/package.json create mode 100644 plugins/wazuh-security-policies/public/application.tsx create mode 100644 plugins/wazuh-security-policies/public/components/app.tsx create mode 100644 plugins/wazuh-security-policies/public/index.scss create mode 100644 plugins/wazuh-security-policies/public/index.ts create mode 100644 plugins/wazuh-security-policies/public/plugin.ts create mode 100644 plugins/wazuh-security-policies/public/types.ts create mode 100644 plugins/wazuh-security-policies/server/index.ts create mode 100644 plugins/wazuh-security-policies/server/plugin.ts create mode 100644 plugins/wazuh-security-policies/server/routes/index.ts create mode 100644 plugins/wazuh-security-policies/server/types.ts create mode 100644 plugins/wazuh-security-policies/translations/ja-JP.json create mode 100644 plugins/wazuh-security-policies/tsconfig.json create mode 100644 plugins/wazuh-security-policies/yarn.lock diff --git a/docker/osd-dev/dev.yml b/docker/osd-dev/dev.yml index c07d2b6c6d..fdf249c453 100755 --- a/docker/osd-dev/dev.yml +++ b/docker/osd-dev/dev.yml @@ -244,6 +244,7 @@ services: - '${SRC}/main:/home/node/kbn/plugins/main' - '${SRC}/wazuh-core:/home/node/kbn/plugins/wazuh-core' - '${SRC}/wazuh-check-updates:/home/node/kbn/plugins/wazuh-check-updates' + - '${SRC}/wazuh-security-policies:/home/node/kbn/plugins/wazuh-security-policies' - '${SRC}/wazuh-engine:/home/node/kbn/plugins/wazuh-engine' - '${SRC}/wazuh-fleet:/home/node/kbn/plugins/wazuh-fleet' - wd_certs:/home/node/kbn/certs/ diff --git a/plugins/wazuh-security-policies/.i18nrc.json b/plugins/wazuh-security-policies/.i18nrc.json new file mode 100644 index 0000000000..5e344386a4 --- /dev/null +++ b/plugins/wazuh-security-policies/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "wazuhSecurityPolicies", + "paths": { + "wazuhSecurityPolicies": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/plugins/wazuh-security-policies/README.md b/plugins/wazuh-security-policies/README.md new file mode 100755 index 0000000000..4e213dc5f0 --- /dev/null +++ b/plugins/wazuh-security-policies/README.md @@ -0,0 +1,22 @@ +# wazuhSecurityPolicies + +A OpenSearch Dashboards plugin + +--- + +## Development + +See the [OpenSearch Dashboards contributing +guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/CONTRIBUTING.md) for instructions +setting up your development environment. + + ## Scripts +
+
yarn osd bootstrap
+
Execute this to install node_modules and setup the dependencies in your plugin and in OpenSearch Dashboards +
+ +
yarn plugin-helpers build
+
Execute this to create a distributable version of this plugin that can be installed in OpenSearch Dashboards +
+
diff --git a/plugins/wazuh-security-policies/common/index.ts b/plugins/wazuh-security-policies/common/index.ts new file mode 100644 index 0000000000..2ede64bf71 --- /dev/null +++ b/plugins/wazuh-security-policies/common/index.ts @@ -0,0 +1,2 @@ +export const PLUGIN_ID = 'wazuhSecurityPolicies'; +export const PLUGIN_NAME = 'Ruleset'; diff --git a/plugins/wazuh-security-policies/opensearch_dashboards.json b/plugins/wazuh-security-policies/opensearch_dashboards.json new file mode 100644 index 0000000000..2637c18a03 --- /dev/null +++ b/plugins/wazuh-security-policies/opensearch_dashboards.json @@ -0,0 +1,9 @@ +{ + "id": "wazuhSecurityPolicies", + "version": "1.0.0", + "opensearchDashboardsVersion": "opensearchDashboards", + "server": true, + "ui": true, + "requiredPlugins": ["navigation"], + "optionalPlugins": [] +} diff --git a/plugins/wazuh-security-policies/package.json b/plugins/wazuh-security-policies/package.json new file mode 100644 index 0000000000..e0dd122d44 --- /dev/null +++ b/plugins/wazuh-security-policies/package.json @@ -0,0 +1,10 @@ +{ + "name": "wazuhSecurityPolicies", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "../../scripts/use_node ../../scripts/plugin_helpers", + "osd": "../../scripts/use_node ../../scripts/osd" + } +} diff --git a/plugins/wazuh-security-policies/public/application.tsx b/plugins/wazuh-security-policies/public/application.tsx new file mode 100644 index 0000000000..803dd71fdb --- /dev/null +++ b/plugins/wazuh-security-policies/public/application.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters, CoreStart } from '../../../src/core/public'; +import { AppPluginStartDependencies } from './types'; +import { WazuhSecurityPoliciesApp } from './components/app'; + +export const renderApp = ( + { notifications, http }: CoreStart, + { navigation }: AppPluginStartDependencies, + { appBasePath, element }: AppMountParameters, +) => { + ReactDOM.render( + , + element, + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx new file mode 100644 index 0000000000..526b7dd2a7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -0,0 +1,116 @@ +import React, { useState } from 'react'; +import { i18n } from '@osd/i18n'; +import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { + EuiHorizontalRule, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageHeader, + EuiTitle, + EuiText, + EuiButton, +} from '@elastic/eui'; +import { CoreStart } from '../../../../src/core/public'; +import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; +import { PLUGIN_ID, PLUGIN_NAME } from '../../common'; + +interface WazuhSecurityPoliciesAppDeps { + basename: string; + notifications: CoreStart['notifications']; + http: CoreStart['http']; + navigation: NavigationPublicPluginStart; +} + +export const WazuhSecurityPoliciesApp = ({ + basename, + notifications, + http, + navigation, +}: WazuhSecurityPoliciesAppDeps) => { + // Use React hooks to manage state. + const [timestamp, setTimestamp] = useState(); + + const onClickHandler = () => { + // Use the core http service to make a response to the server API. + http.get('/api/wazuh_security_policies/example').then(res => { + setTimestamp(res.time); + // Use the core notifications service to display a success message. + notifications.toasts.addSuccess( + i18n.translate('wazuhSecurityPolicies.dataUpdated', { + defaultMessage: 'Data updated', + }), + ); + }); + }; + + // Render the application DOM. + // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. + return ( + + + <> + + + + + +

+ +

+
+
+ + + +

+ +

+
+
+ + +

+ +

+ +

+ +

+ + + +
+
+
+
+
+ +
+
+ ); +}; diff --git a/plugins/wazuh-security-policies/public/index.scss b/plugins/wazuh-security-policies/public/index.scss new file mode 100644 index 0000000000..ff7112406e --- /dev/null +++ b/plugins/wazuh-security-policies/public/index.scss @@ -0,0 +1 @@ +/* stylelint-disable no-empty-source */ diff --git a/plugins/wazuh-security-policies/public/index.ts b/plugins/wazuh-security-policies/public/index.ts new file mode 100644 index 0000000000..1c695634f4 --- /dev/null +++ b/plugins/wazuh-security-policies/public/index.ts @@ -0,0 +1,13 @@ +import './index.scss'; +import { WazuhSecurityPoliciesPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, OpenSearch Dashboards Platform `plugin()` initializer. +export function plugin() { + return new WazuhSecurityPoliciesPlugin(); +} + +export { + WazuhSecurityPoliciesPluginSetup, + WazuhSecurityPoliciesPluginStart, +} from './types'; diff --git a/plugins/wazuh-security-policies/public/plugin.ts b/plugins/wazuh-security-policies/public/plugin.ts new file mode 100644 index 0000000000..8b6bbd6023 --- /dev/null +++ b/plugins/wazuh-security-policies/public/plugin.ts @@ -0,0 +1,64 @@ +import { i18n } from '@osd/i18n'; +import { + AppMountParameters, + CoreSetup, + Plugin, +} from '../../../src/core/public'; +import { PLUGIN_NAME } from '../common'; +import { + WazuhSecurityPoliciesPluginSetup, + WazuhSecurityPoliciesPluginStart, + AppPluginStartDependencies, +} from './types'; + +export class WazuhSecurityPoliciesPlugin + implements + Plugin +{ + public setup(core: CoreSetup): WazuhSecurityPoliciesPluginSetup { + // Register an application into the side navigation menu + core.application.register({ + id: 'wazuhSecurityPolicies', + title: PLUGIN_NAME, + category: { + id: 'wz-category-security-policies', + label: i18n.translate('wz-app-category-security-policies', { + defaultMessage: 'Security Policies', + }), + order: 200, + euiIconType: 'indexRollupApp', + }, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services as specified in opensearch_dashboards.json + const [coreStart, depsStart] = await core.getStartServices(); + + // Render the application + return renderApp( + coreStart, + depsStart as AppPluginStartDependencies, + params, + ); + }, + }); + + // Return methods that should be available to other plugins + return { + getGreeting() { + return i18n.translate('wazuhSecurityPolicies.greetingText', { + defaultMessage: 'Hello from {name}!', + values: { + name: PLUGIN_NAME, + }, + }); + }, + }; + } + + public start(): WazuhSecurityPoliciesPluginStart { + return {}; + } + + public stop() {} +} diff --git a/plugins/wazuh-security-policies/public/types.ts b/plugins/wazuh-security-policies/public/types.ts new file mode 100644 index 0000000000..54154e6226 --- /dev/null +++ b/plugins/wazuh-security-policies/public/types.ts @@ -0,0 +1,11 @@ +import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; + +export interface WazuhSecurityPoliciesPluginSetup { + getGreeting: () => string; +} +// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type +export interface WazuhSecurityPoliciesPluginStart {} + +export interface AppPluginStartDependencies { + navigation: NavigationPublicPluginStart; +} diff --git a/plugins/wazuh-security-policies/server/index.ts b/plugins/wazuh-security-policies/server/index.ts new file mode 100644 index 0000000000..2f7147a9b1 --- /dev/null +++ b/plugins/wazuh-security-policies/server/index.ts @@ -0,0 +1,14 @@ +import { PluginInitializerContext } from '../../../src/core/server'; +import { WazuhSecurityPoliciesPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, OpenSearch Dashboards Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new WazuhSecurityPoliciesPlugin(initializerContext); +} + +export { + WazuhSecurityPoliciesPluginSetup, + WazuhSecurityPoliciesPluginStart, +} from './types'; diff --git a/plugins/wazuh-security-policies/server/plugin.ts b/plugins/wazuh-security-policies/server/plugin.ts new file mode 100644 index 0000000000..c5537b5113 --- /dev/null +++ b/plugins/wazuh-security-policies/server/plugin.ts @@ -0,0 +1,41 @@ +import { + PluginInitializerContext, + CoreSetup, + Plugin, + Logger, +} from '../../../src/core/server'; +import { + WazuhSecurityPoliciesPluginSetup, + WazuhSecurityPoliciesPluginStart, +} from './types'; +import { defineRoutes } from './routes'; + +export class WazuhSecurityPoliciesPlugin + implements + Plugin +{ + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('wazuhSecurityPolicies: Setup'); + + const router = core.http.createRouter(); + + // Register server side APIs + defineRoutes(router); + + return {}; + } + + public start() { + this.logger.debug('wazuhSecurityPolicies: Started'); + + return {}; + } + + public stop() {} +} diff --git a/plugins/wazuh-security-policies/server/routes/index.ts b/plugins/wazuh-security-policies/server/routes/index.ts new file mode 100644 index 0000000000..920b406851 --- /dev/null +++ b/plugins/wazuh-security-policies/server/routes/index.ts @@ -0,0 +1,16 @@ +import { IRouter } from '../../../../src/core/server'; + +export function defineRoutes(router: IRouter) { + router.get( + { + path: '/api/wazuh_security_policies/example', + validate: false, + }, + async (context, request, response) => + response.ok({ + body: { + time: new Date().toISOString(), + }, + }), + ); +} diff --git a/plugins/wazuh-security-policies/server/types.ts b/plugins/wazuh-security-policies/server/types.ts new file mode 100644 index 0000000000..4bdc1abf41 --- /dev/null +++ b/plugins/wazuh-security-policies/server/types.ts @@ -0,0 +1,5 @@ +/* eslint-disable @typescript-eslint/no-empty-object-type */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type +export interface WazuhSecurityPoliciesPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface WazuhSecurityPoliciesPluginStart {} diff --git a/plugins/wazuh-security-policies/translations/ja-JP.json b/plugins/wazuh-security-policies/translations/ja-JP.json new file mode 100644 index 0000000000..60cb8f9ed6 --- /dev/null +++ b/plugins/wazuh-security-policies/translations/ja-JP.json @@ -0,0 +1,81 @@ +{ + "formats": { + "number": { + "currency": { + "style": "currency" + }, + "percent": { + "style": "percent" + } + }, + "date": { + "short": { + "month": "numeric", + "day": "numeric", + "year": "2-digit" + }, + "medium": { + "month": "short", + "day": "numeric", + "year": "numeric" + }, + "long": { + "month": "long", + "day": "numeric", + "year": "numeric" + }, + "full": { + "weekday": "long", + "month": "long", + "day": "numeric", + "year": "numeric" + } + }, + "time": { + "short": { + "hour": "numeric", + "minute": "numeric" + }, + "medium": { + "hour": "numeric", + "minute": "numeric", + "second": "numeric" + }, + "long": { + "hour": "numeric", + "minute": "numeric", + "second": "numeric", + "timeZoneName": "short" + }, + "full": { + "hour": "numeric", + "minute": "numeric", + "second": "numeric", + "timeZoneName": "short" + } + }, + "relative": { + "years": { + "units": "year" + }, + "months": { + "units": "month" + }, + "days": { + "units": "day" + }, + "hours": { + "units": "hour" + }, + "minutes": { + "units": "minute" + }, + "seconds": { + "units": "second" + } + } + }, + "messages": { + "wazuhSecurityPolicies.buttonText": "Translate me to Japanese" + } +} diff --git a/plugins/wazuh-security-policies/tsconfig.json b/plugins/wazuh-security-policies/tsconfig.json new file mode 100644 index 0000000000..7fa0373911 --- /dev/null +++ b/plugins/wazuh-security-policies/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": [] +} diff --git a/plugins/wazuh-security-policies/yarn.lock b/plugins/wazuh-security-policies/yarn.lock new file mode 100644 index 0000000000..fb57ccd13a --- /dev/null +++ b/plugins/wazuh-security-policies/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 3236353f21429e7889fe5fcbbb2692367cf10616 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:19:37 +0100 Subject: [PATCH 02/30] Add navigation --- .../opensearch_dashboards.json | 2 +- .../public/components/app.tsx | 184 +++++++++--------- .../public/plugin-services.tsx | 5 + .../wazuh-security-policies/public/plugin.ts | 8 +- 4 files changed, 105 insertions(+), 94 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/plugin-services.tsx diff --git a/plugins/wazuh-security-policies/opensearch_dashboards.json b/plugins/wazuh-security-policies/opensearch_dashboards.json index 2637c18a03..77ac702ffd 100644 --- a/plugins/wazuh-security-policies/opensearch_dashboards.json +++ b/plugins/wazuh-security-policies/opensearch_dashboards.json @@ -4,6 +4,6 @@ "opensearchDashboardsVersion": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": ["navigation"], + "requiredPlugins": ["navigation", "opensearchDashboardsUtils"], "optionalPlugins": [] } diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 526b7dd2a7..14549c69cf 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -1,112 +1,112 @@ -import React, { useState } from 'react'; -import { i18n } from '@osd/i18n'; +import React, { useState, useEffect } from 'react'; import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { BrowserRouter as Router } from 'react-router-dom'; import { - EuiHorizontalRule, EuiPage, EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageHeader, - EuiTitle, - EuiText, - EuiButton, + EuiSideNav, + EuiPageSideBar, + EuiPanel, } from '@elastic/eui'; -import { CoreStart } from '../../../../src/core/public'; -import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; -import { PLUGIN_ID, PLUGIN_NAME } from '../../common'; +import { Router, Route, Switch, Redirect } from 'react-router-dom'; +import { getCore, getHistory } from '../plugin-services'; -interface WazuhSecurityPoliciesAppDeps { - basename: string; - notifications: CoreStart['notifications']; - http: CoreStart['http']; - navigation: NavigationPublicPluginStart; +interface ViewInterface { + name: string; + id: string; + render: () => React.ReactNode; } -export const WazuhSecurityPoliciesApp = ({ - basename, - notifications, - http, - navigation, -}: WazuhSecurityPoliciesAppDeps) => { - // Use React hooks to manage state. - const [timestamp, setTimestamp] = useState(); +const views: ViewInterface[] = [ + { + name: 'Integrations', + id: 'integrations', + render: () =>
Integrations
, + }, + { + name: 'Rules', + id: 'rules', + render: () =>
Rules
, + }, + { + name: 'Decoders', + id: 'decoders', + render: () =>
Decoders
, + }, + { + name: 'KVDB', + id: 'kvdb', + render: () =>
KVDBs
, + }, +]; - const onClickHandler = () => { - // Use the core http service to make a response to the server API. - http.get('/api/wazuh_security_policies/example').then(res => { - setTimestamp(res.time); - // Use the core notifications service to display a success message. - notifications.toasts.addSuccess( - i18n.translate('wazuhSecurityPolicies.dataUpdated', { - defaultMessage: 'Data updated', - }), - ); - }); - }; +export const WazuhSecurityPoliciesApp = () => { + const history = getHistory(); + const [currentTab, setCurrentTab] = useState(''); + + useEffect(() => { + setCurrentTab(history.location.pathname); + }, []); + + const sideNav = [ + { + name: 'Ruleset', + id: 'wazuhRuleset', + items: views.map(item => ({ + id: item.id, + name: item.name, + onClick: () => { + history.push(`${item.id}`); + setCurrentTab(item.id); + }, + isSelected: + item.id === currentTab || history.location.pathname === `/${item.id}`, + })), + }, + ]; // Render the application DOM. // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. return ( - + <> - - + {/* */} + + + + - - -

- + + {views.map(view => ( + { + getCore().chrome.setBreadcrumbs([ + { + className: 'osdBreadcrumbs', + text: ( + + ), + }, + ]); + + return view.render(); + }} /> -

-
-
- - - -

- -

-
-
- - -

- -

- -

- -

- - - -
-
-
+ ))} + + +
diff --git a/plugins/wazuh-security-policies/public/plugin-services.tsx b/plugins/wazuh-security-policies/public/plugin-services.tsx new file mode 100644 index 0000000000..c95d9cca28 --- /dev/null +++ b/plugins/wazuh-security-policies/public/plugin-services.tsx @@ -0,0 +1,5 @@ +import { CoreStart } from 'opensearch-dashboards/public'; +import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/common'; + +export const [getCore, setCore] = createGetterSetter('Core'); +export const [getHistory, setHistory] = createGetterSetter('History'); diff --git a/plugins/wazuh-security-policies/public/plugin.ts b/plugins/wazuh-security-policies/public/plugin.ts index 8b6bbd6023..4d689f7bbc 100644 --- a/plugins/wazuh-security-policies/public/plugin.ts +++ b/plugins/wazuh-security-policies/public/plugin.ts @@ -1,7 +1,9 @@ import { i18n } from '@osd/i18n'; +import { createHashHistory } from 'history'; import { AppMountParameters, CoreSetup, + CoreStart, Plugin, } from '../../../src/core/public'; import { PLUGIN_NAME } from '../common'; @@ -10,6 +12,7 @@ import { WazuhSecurityPoliciesPluginStart, AppPluginStartDependencies, } from './types'; +import { setCore, setHistory } from './plugin-services'; export class WazuhSecurityPoliciesPlugin implements @@ -34,6 +37,8 @@ export class WazuhSecurityPoliciesPlugin // Get start services as specified in opensearch_dashboards.json const [coreStart, depsStart] = await core.getStartServices(); + setHistory(createHashHistory()); + // Render the application return renderApp( coreStart, @@ -56,7 +61,8 @@ export class WazuhSecurityPoliciesPlugin }; } - public start(): WazuhSecurityPoliciesPluginStart { + public start(core: CoreStart): WazuhSecurityPoliciesPluginStart { + setCore(core); return {}; } From 1d43c48145554f0fb49e699d79cb600402b155ad Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:20:09 +0100 Subject: [PATCH 03/30] Fix lint --- plugins/wazuh-security-policies/public/plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/wazuh-security-policies/public/plugin.ts b/plugins/wazuh-security-policies/public/plugin.ts index 4d689f7bbc..20597720f5 100644 --- a/plugins/wazuh-security-policies/public/plugin.ts +++ b/plugins/wazuh-security-policies/public/plugin.ts @@ -63,6 +63,7 @@ export class WazuhSecurityPoliciesPlugin public start(core: CoreStart): WazuhSecurityPoliciesPluginStart { setCore(core); + return {}; } From 39cd36bed70f132eed3e4c553114c7a5a2a420b6 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:08:39 +0100 Subject: [PATCH 04/30] Add integrations view --- .../public/components/app.tsx | 8 +- .../public/components/common/popover.tsx | 34 +++ .../components/card-integration.tsx | 72 ++++++ .../components/integretions/integrations.scss | 4 + .../components/integretions/overview.tsx | 212 ++++++++++++++++++ 5 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/popover.tsx create mode 100644 plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx create mode 100644 plugins/wazuh-security-policies/public/components/integretions/integrations.scss create mode 100644 plugins/wazuh-security-policies/public/components/integretions/overview.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 14549c69cf..63e4e3c08f 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -9,6 +9,7 @@ import { } from '@elastic/eui'; import { Router, Route, Switch, Redirect } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; +import { IntegrationOverview } from './integretions/overview'; interface ViewInterface { name: string; @@ -20,7 +21,7 @@ const views: ViewInterface[] = [ { name: 'Integrations', id: 'integrations', - render: () =>
Integrations
, + render: () => , }, { name: 'Rules', @@ -70,14 +71,13 @@ export const WazuhSecurityPoliciesApp = () => { <> - {/* */} - + { + const { children, styles, color = 'text' } = props; + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen(isPopoverOpen => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + return ( + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + > + {children} + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx new file mode 100644 index 0000000000..17dc7d4201 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { + EuiCard, + EuiIcon, + EuiButtonEmpty, + EuiHorizontalRule, +} from '@elastic/eui'; +import { PopoverIconButton } from '../../common/popover'; + +interface CardIntegrationProps { + image: string; + title: string; + description: string; + isEnable: boolean; +} + +export const CardIntegration = (props: CardIntegrationProps) => { + const { image = 'logoOpenSearch', title, description, isEnable } = props; + const buttonIntegrations = [ + { + id: 'goToDecoder', + label: 'Go to Decoder', + color: 'text', + }, + { + id: 'goToRules', + label: 'Go to Rules', + color: 'text', + }, + { + id: 'goToKVDB', + label: 'Go to KVDB', + color: 'text', + }, + { + id: 'enable/disable', + label: isEnable ? 'Disable' : 'Enable', + color: isEnable ? 'danger' : 'primary', + }, + ]; + + return ( +
+ } + paddingSize='m' + /> + +
+ {buttonIntegrations.map((button, index) => ( + + + {button.label} + + {index < buttonIntegrations.length - 1 && ( + + )} + + ))} +
+
+
+ ); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/integrations.scss b/plugins/wazuh-security-policies/public/components/integretions/integrations.scss new file mode 100644 index 0000000000..7bac981f40 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/integrations.scss @@ -0,0 +1,4 @@ +.integration-title-header { + display: flex; + align-items: center; +} diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx new file mode 100644 index 0000000000..ac7cc590e7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -0,0 +1,212 @@ +import React, { useState } from 'react'; +import { + EuiPageHeader, + EuiLink, + EuiButton, + EuiHealth, + EuiFlexGroup, + EuiFlexItem, + EuiSearchBar, + EuiCallOut, + EuiText, +} from '@elastic/eui'; +import './integrations.scss'; +import { CardIntegration } from './components/card-integration'; + +const integrations = [ + { + image: 'advancedSettingsApp', + title: 'Integration 1', + description: 'Description for integration 1', + isEnable: true, + }, + { + image: 'grokApp', + title: 'Integration 2', + description: 'Description for integration 2', + isEnable: false, + }, + { + image: 'grokApp', + title: 'Integration 3', + description: 'Description for integration 3', + isEnable: true, + }, + { + image: 'reportingApp', + title: 'Integration 4', + description: 'Description for integration 4', + isEnable: false, + }, + { + image: 'heartbeatApp', + title: 'Integration 5', + description: 'Description for integration 5', + isEnable: true, + }, + { + image: 'appSearchApp', + title: 'Integration 6', + description: 'Description for integration 6', + isEnable: false, + }, + { + image: 'indexRollupApp', + title: 'Integration 7', + description: 'Description for integration 7', + isEnable: true, + }, + { + image: 'canvasApp', + title: 'Integration 8', + description: 'Description for integration 8', + isEnable: false, + }, + { + image: 'securityApp', + title: 'Integration 9', + description: 'Description for integration 9', + isEnable: true, + }, + { + image: 'lensApp', + title: 'Integration 10', + description: 'Description for integration 10', + isEnable: false, + }, +]; + +export const IntegrationOverview = () => { + const [lastUpdate, setLastUpdate] = useState({ + lastUpdateDate: '12/18/2024', + status: 'Success', + }); + const titleHeader = ( + +

Integrations

+ + Updated + +
+ ); + + const updateContentManager = () => { + const currentDate = new Date().toLocaleString(); + + setLastUpdate({ + lastUpdateDate: currentDate, + status: 'Success', + }); + }; + + // Search bar + + const initialQuery = EuiSearchBar.Query.MATCH_ALL; + const [query, setQuery] = useState(initialQuery); + const [error, setError] = useState(null); + + const onChange = ({ query, error }) => { + if (error) { + setError(error); + } else { + setError(null); + setQuery(query); + } + }; + + const filters = [ + { + type: 'field_value_selection', + field: 'integration', + name: 'Integrations', + multiSelect: 'and', + operator: 'exact', + cache: 10000, // will cache the loaded tags for 10 sec + options: integrations.map(integration => ({ + value: integration.title, + view: {integration.title}, + })), + }, + ]; + const schema = { + strict: true, + fields: { + integration: { + type: 'string', + }, + }, + }; + + const renderError = () => { + if (!error) { + return; + } + + return ( + <> + + + ); + }; + + return ( + <> + + Last update of the content manager was {lastUpdate.lastUpdateDate} ( + {lastUpdate.status}).{' '} + + Learn more + + + } + rightSideItems={[ + + Update + , + ]} + /> +
+ +
+ {renderError()} + + {query.text === '' + ? integrations.map((integration, index) => ( + + + + )) + : integrations + .filter(integration => + query.text + .toLocaleLowerCase() + .includes(integration.title.toLocaleLowerCase()), + ) + .map((integration, index) => ( + + + + ))} + + + ); +}; From 7cf84e619a2fb3a9d19b1a52e5914f2b51976d5e Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:54:23 +0100 Subject: [PATCH 05/30] Add navigation integration --- .../public/components/app.tsx | 44 ++++++++++++++++--- .../components/card-integration.tsx | 7 +++ .../components/integretions/integration.tsx | 7 +++ 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/integretions/integration.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 63e4e3c08f..30b7d6e256 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -7,7 +7,7 @@ import { EuiPageSideBar, EuiPanel, } from '@elastic/eui'; -import { Router, Route, Switch, Redirect } from 'react-router-dom'; +import { Router, Route, Switch, Redirect, useParams } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; import { IntegrationOverview } from './integretions/overview'; @@ -15,6 +15,7 @@ interface ViewInterface { name: string; id: string; render: () => React.ReactNode; + renderDetails?: () => React.ReactNode; } const views: ViewInterface[] = [ @@ -22,6 +23,7 @@ const views: ViewInterface[] = [ name: 'Integrations', id: 'integrations', render: () => , + renderDetails: () =>
Details
, }, { name: 'Rules', @@ -56,7 +58,7 @@ export const WazuhSecurityPoliciesApp = () => { id: item.id, name: item.name, onClick: () => { - history.push(`${item.id}`); + history.push(`/${item.id}`); setCurrentTab(item.id); }, isSelected: @@ -83,7 +85,39 @@ export const WazuhSecurityPoliciesApp = () => { hasBorder={false} > - {views.map(view => ( + {views.map(view => [ + view.renderDetails && ( + { + const { id } = useParams(); + + getCore().chrome.setBreadcrumbs([ + { + text: ( + + ), + href: getCore().application.getUrlForApp( + 'wazuhSecurityPolicies', + { + path: `#/${view.id}`, + }, + ), + }, + { + className: 'osdBreadcrumbs', + text: id, + }, + ]); + + return view.renderDetails(); + }} + /> + ), { return view.render(); }} - /> - ))} + />, + ])}
diff --git a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx index 17dc7d4201..2a8a0976a4 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx @@ -6,6 +6,7 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { PopoverIconButton } from '../../common/popover'; +import { getHistory } from '../../../plugin-services'; interface CardIntegrationProps { image: string; @@ -15,6 +16,7 @@ interface CardIntegrationProps { } export const CardIntegration = (props: CardIntegrationProps) => { + const history = getHistory(); const { image = 'logoOpenSearch', title, description, isEnable } = props; const buttonIntegrations = [ { @@ -39,6 +41,10 @@ export const CardIntegration = (props: CardIntegrationProps) => { }, ]; + const handleNavigation = (path: string) => { + history.push(path); + }; + return (
{ description={description} icon={} paddingSize='m' + onClick={() => handleNavigation(`/integrations/${title}`)} /> ( +
+

Integration

+
+); From 70c36c14b72188d24f1eea54d5d8f05b0c443f7d Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:32:32 +0100 Subject: [PATCH 06/30] Add integration view --- .../public/components/app.tsx | 3 +- .../public/components/common/header-page.tsx | 20 ++ .../public/components/common/searchbar.tsx | 43 ++++ .../components/card-integration.tsx | 4 +- .../components/integration-description.tsx | 45 ++++ .../components/integretions/integration.tsx | 141 +++++++++++- .../components/integretions/integrations.scss | 10 + .../integretions/mock-data-integrations.tsx | 214 ++++++++++++++++++ .../components/integretions/overview.tsx | 177 +++++---------- 9 files changed, 528 insertions(+), 129 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/header-page.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/searchbar.tsx create mode 100644 plugins/wazuh-security-policies/public/components/integretions/components/integration-description.tsx create mode 100644 plugins/wazuh-security-policies/public/components/integretions/mock-data-integrations.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 30b7d6e256..f5663fe469 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -10,6 +10,7 @@ import { import { Router, Route, Switch, Redirect, useParams } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; import { IntegrationOverview } from './integretions/overview'; +import { IntegrationView } from './integretions/integration'; interface ViewInterface { name: string; @@ -23,7 +24,7 @@ const views: ViewInterface[] = [ name: 'Integrations', id: 'integrations', render: () => , - renderDetails: () =>
Details
, + renderDetails: () => , }, { name: 'Rules', diff --git a/plugins/wazuh-security-policies/public/components/common/header-page.tsx b/plugins/wazuh-security-policies/public/components/common/header-page.tsx new file mode 100644 index 0000000000..44f32dccb7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/header-page.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { EuiPageHeader } from '@elastic/eui'; + +interface HeaderPageProps { + titleHeader: React.ReactNode | string; + descriptionHeader: React.ReactNode | string; + rightSideItems?: React.ReactNode[]; +} + +export const HeaderPage = (props: HeaderPageProps) => { + const { titleHeader, descriptionHeader, rightSideItems } = props; + + return ( + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/searchbar.tsx b/plugins/wazuh-security-policies/public/components/common/searchbar.tsx new file mode 100644 index 0000000000..8a296fe861 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/searchbar.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { EuiCallOut, EuiSearchBar, EuiSearchBarProps } from '@elastic/eui'; + +const initialQuery = EuiSearchBar.Query.MATCH_ALL; + +export const SearchBar = (props: EuiSearchBarProps) => { + const { schema, filters, onChange, error } = props; + + const renderError = () => { + if (!error) { + return; + } + + return ( + <> + + + ); + }; + + return ( + <> +
+ +
+ {renderError()} + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx index 2a8a0976a4..43bd2a043a 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx @@ -46,7 +46,7 @@ export const CardIntegration = (props: CardIntegrationProps) => { }; return ( -
+ <> { ))}
-
+ ); }; diff --git a/plugins/wazuh-security-policies/public/components/integretions/components/integration-description.tsx b/plugins/wazuh-security-policies/public/components/integretions/components/integration-description.tsx new file mode 100644 index 0000000000..61e9eb256b --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/components/integration-description.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; + +interface IntegrationDescriptionProps { + keyValue: string; + value: any; +} + +export const IntegrationDescription = (props: IntegrationDescriptionProps) => { + const { keyValue, value } = props; + const renderTitleDescription = (key: string, value: string) => ( +
+ {key} + + {value} + +
+ ); + + const renderObjectValue = (keyObject: string, valueObject: object) => { + const subList = Object.entries(valueObject).map(([key, value]) => ({ + key: `${keyObject}.${key}`, + value: value, + })); + + return subList.map(item => renderTitleDescription(item.key, item.value)); + }; + + const renderValue = (key: string, value: any) => { + if (Array.isArray(value)) { + return renderTitleDescription(key, value.join(', ')); + } else if (typeof value === 'object') { + return renderObjectValue(key, value); + } else if (typeof value === 'boolean') { + return renderTitleDescription(key, value ? 'Enable' : 'Disable'); + } else { + return renderTitleDescription(key, value); + } + }; + + return renderValue(keyValue, value); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration.tsx index bf3b6f2f94..37852da382 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integration.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/integration.tsx @@ -1,7 +1,136 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { + EuiHealth, + EuiIcon, + EuiLink, + EuiButton, + EuiDescriptionList, + EuiPanel, +} from '@elastic/eui'; +import { useParams } from 'react-router-dom'; +import { HeaderPage } from '../common/header-page'; +import { integrations } from './mock-data-integrations'; +import './integrations.scss'; +import { IntegrationDescription } from './components/integration-description'; -export const Integration = () => ( -
-

Integration

-
-); +export const IntegrationView = () => { + const id = useParams().id; + const [integrationData, setIntegrationData] = useState<{ + image: string; + title: string; + description: string; + isEnable: boolean; + lastUpdate: { + lastUpdateDate: string; + status: string; + }; + versions: string[]; + compatibility: string; + author: { + name: string; + date: string; + }; + references: string[]; + }>({ + image: '', + title: '', + description: '', + isEnable: false, + lastUpdate: { lastUpdateDate: '', status: '' }, + versions: [], + compatibility: '', + author: { + name: '', + date: '', + }, + references: [], + }); + + useEffect(() => { + const integration = integrations.find( + integration => integration.title === id, + ); + + if (integration) { + setIntegrationData(integration); + } + }, [id]); + + // Header page start + + const headerTitle = ( + + +

{integrationData?.title}

+ + {integrationData.isEnable ? 'Enabled' : 'Disabled'} + +
+ ); + const descriptionHeader = ( + <> + Last update of the content manager was{' '} + {integrationData.lastUpdate.lastUpdateDate} ( + {integrationData.lastUpdate.status}).{' '} + + Learn more + + + ); + + const toggleEnableOrDisable = () => { + setIntegrationData({ + ...integrationData, + isEnable: !integrationData.isEnable, + }); + }; + + const rightSideItems = [ + + {integrationData.isEnable ? 'Disable' : 'Enable'} + , + ]; + // Header page end + // Description list start + const list = Object.entries(integrationData).map(([key, value]) => ({ + key, + value, + })); + + // Description list end + + return ( + <> + + + + {list + .filter(item => item.key !== 'image') + .map(item => ( + + ))} + + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/integrations.scss b/plugins/wazuh-security-policies/public/components/integretions/integrations.scss index 7bac981f40..585394ae7c 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integrations.scss +++ b/plugins/wazuh-security-policies/public/components/integretions/integrations.scss @@ -2,3 +2,13 @@ display: flex; align-items: center; } + +.integration-icon-header { + margin-right: 10px; +} + +.data-integration-card { + display: flex; + margin: 10px; + flex-wrap: wrap; +} diff --git a/plugins/wazuh-security-policies/public/components/integretions/mock-data-integrations.tsx b/plugins/wazuh-security-policies/public/components/integretions/mock-data-integrations.tsx new file mode 100644 index 0000000000..602465befd --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/mock-data-integrations.tsx @@ -0,0 +1,214 @@ +export const integrations = [ + { + image: 'advancedSettingsApp', + module: 'syslog', + title: 'Linux Doas Conf File Creation', + description: + 'Detects the creation of doas.conf file in linux host platform', + isEnable: true, + versions: ['1.3.1', '1.3.2', '1.3.3'], + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'grokApp', + title: 'Integration 2', + description: 'Description for integration 2', + isEnable: false, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'grokApp', + title: 'Integration 3', + description: 'Description for integration 3', + isEnable: true, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'reportingApp', + title: 'Integration 4', + description: 'Description for integration 4', + isEnable: false, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'heartbeatApp', + title: 'Integration 5', + description: 'Description for integration 5', + isEnable: true, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'appSearchApp', + title: 'Integration 6', + description: 'Description for integration 6', + isEnable: false, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'indexRollupApp', + title: 'Integration 7', + description: 'Description for integration 7', + isEnable: true, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'canvasApp', + title: 'Integration 8', + description: 'Description for integration 8', + isEnable: false, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'securityApp', + title: 'Integration 9', + description: 'Description for integration 9', + isEnable: true, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, + { + image: 'lensApp', + title: 'Integration 10', + description: 'Description for integration 10', + isEnable: false, + lastUpdate: { + lastUpdateDate: '12/18/2024', + status: 'Success', + }, + versions: ['1.3.1', '1.3.2', '1.3.3'], + compatibility: 'This integration was tested on Ubuntu 20.04', + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + 'https://research.splunk.com/endpoint/linux_doas_conf_file_creation/', + 'https://www.makeuseof.com/how-to-install-and-use-doas/', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/file_event/file_event_lnx_doas_conf_creation.yml', + ], + }, +]; diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx index ac7cc590e7..e3f06cfe6b 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -1,86 +1,27 @@ import React, { useState } from 'react'; import { - EuiPageHeader, EuiLink, EuiButton, EuiHealth, EuiFlexGroup, EuiFlexItem, - EuiSearchBar, - EuiCallOut, EuiText, + Query, } from '@elastic/eui'; import './integrations.scss'; +import { SearchBar } from '../common/searchbar'; +import { HeaderPage } from '../common/header-page'; import { CardIntegration } from './components/card-integration'; - -const integrations = [ - { - image: 'advancedSettingsApp', - title: 'Integration 1', - description: 'Description for integration 1', - isEnable: true, - }, - { - image: 'grokApp', - title: 'Integration 2', - description: 'Description for integration 2', - isEnable: false, - }, - { - image: 'grokApp', - title: 'Integration 3', - description: 'Description for integration 3', - isEnable: true, - }, - { - image: 'reportingApp', - title: 'Integration 4', - description: 'Description for integration 4', - isEnable: false, - }, - { - image: 'heartbeatApp', - title: 'Integration 5', - description: 'Description for integration 5', - isEnable: true, - }, - { - image: 'appSearchApp', - title: 'Integration 6', - description: 'Description for integration 6', - isEnable: false, - }, - { - image: 'indexRollupApp', - title: 'Integration 7', - description: 'Description for integration 7', - isEnable: true, - }, - { - image: 'canvasApp', - title: 'Integration 8', - description: 'Description for integration 8', - isEnable: false, - }, - { - image: 'securityApp', - title: 'Integration 9', - description: 'Description for integration 9', - isEnable: true, - }, - { - image: 'lensApp', - title: 'Integration 10', - description: 'Description for integration 10', - isEnable: false, - }, -]; +import { integrations } from './mock-data-integrations'; export const IntegrationOverview = () => { + const [query, setQuery] = useState({ text: '' }); + const [error, setError] = useState(null); const [lastUpdate, setLastUpdate] = useState({ lastUpdateDate: '12/18/2024', status: 'Success', }); + // Header page start const titleHeader = (

Integrations

@@ -99,13 +40,29 @@ export const IntegrationOverview = () => { }); }; - // Search bar + const descriptionHeader = ( + <> + Last update of the content manager was {lastUpdate.lastUpdateDate} ( + {lastUpdate.status}).{' '} + + Learn more + + + ); + const rightSideItems = [ + + Update + , + ]; - const initialQuery = EuiSearchBar.Query.MATCH_ALL; - const [query, setQuery] = useState(initialQuery); - const [error, setError] = useState(null); + // Header page end + // Search bar start - const onChange = ({ query, error }) => { + const onChange = ({ query, error }: { query: Query; error: Error }) => { if (error) { setError(error); } else { @@ -137,61 +94,33 @@ export const IntegrationOverview = () => { }, }; - const renderError = () => { - if (!error) { - return; - } - - return ( - <> - - - ); - }; + // Search bar end return ( <> - - Last update of the content manager was {lastUpdate.lastUpdateDate} ( - {lastUpdate.status}).{' '} - - Learn more - - - } - rightSideItems={[ - - Update - , - ]} + + -
- -
- {renderError()} {query.text === '' ? integrations.map((integration, index) => ( - + )) @@ -202,7 +131,15 @@ export const IntegrationOverview = () => { .includes(integration.title.toLocaleLowerCase()), ) .map((integration, index) => ( - + ))} From 1143229c75f110d57521acf2b761318d4697dae9 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:48:54 +0100 Subject: [PATCH 07/30] Add tty:true on osd service dev.yml --- docker/osd-dev/dev.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/osd-dev/dev.yml b/docker/osd-dev/dev.yml index fdf249c453..2dcc8a56d5 100755 --- a/docker/osd-dev/dev.yml +++ b/docker/osd-dev/dev.yml @@ -239,6 +239,7 @@ services: - ${OSD_PORT}:5601 environment: - 'LOGS=/proc/1/fd/1' + tty: true volumes: - osd_cache:/home/node/.cache - '${SRC}/main:/home/node/kbn/plugins/main' From 7ec4a22fb9f2395eb2fcd563016f83083ac3ecc1 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:19:52 +0100 Subject: [PATCH 08/30] Add no-results.tsx --- .../public/components/common/no-results.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plugins/wazuh-security-policies/public/components/common/no-results.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/no-results.tsx b/plugins/wazuh-security-policies/public/components/common/no-results.tsx new file mode 100644 index 0000000000..8e8f5dbce7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/no-results.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +export const NoResultsData = (props: { query: { text: string } }) => { + const { query } = props; + + return ( + No results found} + body={ + <> +

+ No results found for the search with the value '{query.text} + '. +

+ + } + /> + ); +}; From 2c59f77030aaad2c61b88a057c53be22f52249e7 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:22:22 +0100 Subject: [PATCH 09/30] Add category new menu --- .../wazuh-security-policies/public/plugin.ts | 32 ++++++++++++------- .../wazuh-security-policies/public/types.ts | 5 ++- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/plugins/wazuh-security-policies/public/plugin.ts b/plugins/wazuh-security-policies/public/plugin.ts index 20597720f5..98039ebe47 100644 --- a/plugins/wazuh-security-policies/public/plugin.ts +++ b/plugins/wazuh-security-policies/public/plugin.ts @@ -4,6 +4,7 @@ import { AppMountParameters, CoreSetup, CoreStart, + DEFAULT_NAV_GROUPS, Plugin, } from '../../../src/core/public'; import { PLUGIN_NAME } from '../common'; @@ -19,16 +20,18 @@ export class WazuhSecurityPoliciesPlugin Plugin { public setup(core: CoreSetup): WazuhSecurityPoliciesPluginSetup { + const pluginId = 'wazuhSecurityPolicies'; + // Register an application into the side navigation menu core.application.register({ - id: 'wazuhSecurityPolicies', + id: pluginId, title: PLUGIN_NAME, category: { id: 'wz-category-security-policies', label: i18n.translate('wz-app-category-security-policies', { defaultMessage: 'Security Policies', }), - order: 200, + order: 1000, euiIconType: 'indexRollupApp', }, async mount(params: AppMountParameters) { @@ -48,17 +51,22 @@ export class WazuhSecurityPoliciesPlugin }, }); - // Return methods that should be available to other plugins - return { - getGreeting() { - return i18n.translate('wazuhSecurityPolicies.greetingText', { - defaultMessage: 'Hello from {name}!', - values: { - name: PLUGIN_NAME, - }, - }); + core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.all, [ + { + id: pluginId, + category: { + id: 'wz-category-security-policies', + label: i18n.translate('wz-app-category-security-policies', { + defaultMessage: 'Security Policies', + }), + order: 1000, + euiIconType: 'indexRollupApp', + }, }, - }; + ]); + + // Return methods that should be available to other plugins + return {}; } public start(core: CoreStart): WazuhSecurityPoliciesPluginStart { diff --git a/plugins/wazuh-security-policies/public/types.ts b/plugins/wazuh-security-policies/public/types.ts index 54154e6226..1ea2b0bf0f 100644 --- a/plugins/wazuh-security-policies/public/types.ts +++ b/plugins/wazuh-security-policies/public/types.ts @@ -1,8 +1,7 @@ import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; -export interface WazuhSecurityPoliciesPluginSetup { - getGreeting: () => string; -} +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface WazuhSecurityPoliciesPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type export interface WazuhSecurityPoliciesPluginStart {} From b7185393a98481a5d778c6104138e70171fcc367 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:26:54 +0100 Subject: [PATCH 10/30] Refactors --- .../last-update-content-manager-text.tsx.tsx | 15 +++ .../public/components/common/searchbar.tsx | 21 +++- ...ntegration.tsx => integration-details.tsx} | 15 +-- .../components/integretions/overview.tsx | 105 +++++++----------- 4 files changed, 81 insertions(+), 75 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx rename plugins/wazuh-security-policies/public/components/integretions/{integration.tsx => integration-details.tsx} (90%) diff --git a/plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx b/plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx new file mode 100644 index 0000000000..44733ff087 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { EuiLink } from '@elastic/eui'; + +export const LastUpdateContentManagerText = (lastUpdate: { + lastUpdateDate: string; + status: string; +}) => ( + <> + Last update of the content manager was {lastUpdate.lastUpdateDate} ( + {lastUpdate.status}).{' '} + + Learn more + + +); diff --git a/plugins/wazuh-security-policies/public/components/common/searchbar.tsx b/plugins/wazuh-security-policies/public/components/common/searchbar.tsx index 8a296fe861..7b5f858290 100644 --- a/plugins/wazuh-security-policies/public/components/common/searchbar.tsx +++ b/plugins/wazuh-security-policies/public/components/common/searchbar.tsx @@ -1,10 +1,25 @@ -import React from 'react'; -import { EuiCallOut, EuiSearchBar, EuiSearchBarProps } from '@elastic/eui'; +import React, { useState } from 'react'; +import { + EuiCallOut, + EuiSearchBar, + EuiSearchBarProps, + Query, +} from '@elastic/eui'; const initialQuery = EuiSearchBar.Query.MATCH_ALL; export const SearchBar = (props: EuiSearchBarProps) => { - const { schema, filters, onChange, error } = props; + const { schema, filters, setQuery } = props; + const [error, setError] = useState(null); + + const onChange = ({ query, error }: { query: Query; error: Error }) => { + if (error) { + setError(error); + } else { + setError(null); + setQuery(query); + } + }; const renderError = () => { if (!error) { diff --git a/plugins/wazuh-security-policies/public/components/integretions/integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx similarity index 90% rename from plugins/wazuh-security-policies/public/components/integretions/integration.tsx rename to plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx index 37852da382..656cf442dc 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integration.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx @@ -2,13 +2,13 @@ import React, { useEffect, useState } from 'react'; import { EuiHealth, EuiIcon, - EuiLink, EuiButton, EuiDescriptionList, EuiPanel, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; import { HeaderPage } from '../common/header-page'; +import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; import { integrations } from './mock-data-integrations'; import './integrations.scss'; import { IntegrationDescription } from './components/integration-description'; @@ -31,9 +31,11 @@ export const IntegrationView = () => { date: string; }; references: string[]; + module: string; }>({ image: '', title: '', + module: '', description: '', isEnable: false, lastUpdate: { lastUpdateDate: '', status: '' }, @@ -74,15 +76,8 @@ export const IntegrationView = () => {
); - const descriptionHeader = ( - <> - Last update of the content manager was{' '} - {integrationData.lastUpdate.lastUpdateDate} ( - {integrationData.lastUpdate.status}).{' '} - - Learn more - - + const descriptionHeader = LastUpdateContentManagerText( + integrationData.lastUpdate, ); const toggleEnableOrDisable = () => { diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx index e3f06cfe6b..5f3c4d3a33 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -1,22 +1,21 @@ import React, { useState } from 'react'; import { - EuiLink, EuiButton, EuiHealth, EuiFlexGroup, EuiFlexItem, EuiText, - Query, } from '@elastic/eui'; import './integrations.scss'; import { SearchBar } from '../common/searchbar'; import { HeaderPage } from '../common/header-page'; +import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; +import { NoResultsData } from '../common/no-results'; import { CardIntegration } from './components/card-integration'; import { integrations } from './mock-data-integrations'; export const IntegrationOverview = () => { const [query, setQuery] = useState({ text: '' }); - const [error, setError] = useState(null); const [lastUpdate, setLastUpdate] = useState({ lastUpdateDate: '12/18/2024', status: 'Success', @@ -40,14 +39,8 @@ export const IntegrationOverview = () => { }); }; - const descriptionHeader = ( - <> - Last update of the content manager was {lastUpdate.lastUpdateDate} ( - {lastUpdate.status}).{' '} - - Learn more - - + const descriptionHeader = LastUpdateContentManagerText( + integrations[0].lastUpdate, ); const rightSideItems = [ { Update , ]; - // Header page end // Search bar start - - const onChange = ({ query, error }: { query: Query; error: Error }) => { - if (error) { - setError(error); - } else { - setError(null); - setQuery(query); - } - }; - const filters = [ { type: 'field_value_selection', @@ -93,8 +75,39 @@ export const IntegrationOverview = () => { }, }, }; - // Search bar end + const listAllIntegrationsComponent = integrations.map( + (integration, index) => ( + + + + ), + ); + const integrationFilter = integrations + .filter(integration => + query.text + .toLocaleLowerCase() + .includes(integration.title.toLocaleLowerCase()), + ) + .map((integration, index) => ( + + + + )); return ( <> @@ -103,46 +116,14 @@ export const IntegrationOverview = () => { descriptionHeader={descriptionHeader} rightSideItems={rightSideItems} /> - + - {query.text === '' - ? integrations.map((integration, index) => ( - - - - )) - : integrations - .filter(integration => - query.text - .toLocaleLowerCase() - .includes(integration.title.toLocaleLowerCase()), - ) - .map((integration, index) => ( - - - - ))} + {!query.text && listAllIntegrationsComponent} + {query.text && integrationFilter.length === 0 ? ( + + ) : ( + integrationFilter + )} ); From c78abd97ddf69159936c0f075442aac99f044e8a Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:28:54 +0100 Subject: [PATCH 11/30] Add rules views --- .../public/components/app.tsx | 20 +- .../integretions/mock-data-rules.tsx | 746 ++++++++++++++++++ .../public/components/rules/overview.tsx | 90 +++ .../public/components/rules/rule-details.tsx | 7 + 4 files changed, 860 insertions(+), 3 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/integretions/mock-data-rules.tsx create mode 100644 plugins/wazuh-security-policies/public/components/rules/overview.tsx create mode 100644 plugins/wazuh-security-policies/public/components/rules/rule-details.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index f5663fe469..135546d068 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -10,7 +10,9 @@ import { import { Router, Route, Switch, Redirect, useParams } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; import { IntegrationOverview } from './integretions/overview'; -import { IntegrationView } from './integretions/integration'; +import { IntegrationView } from './integretions/integration-details'; +import { RulesOverview } from './rules/overview'; +import { RuleDetails } from './rules/rule-details'; interface ViewInterface { name: string; @@ -29,7 +31,8 @@ const views: ViewInterface[] = [ { name: 'Rules', id: 'rules', - render: () =>
Rules
, + render: () => , + renderDetails: () => , }, { name: 'Decoders', @@ -46,6 +49,11 @@ const views: ViewInterface[] = [ export const WazuhSecurityPoliciesApp = () => { const history = getHistory(); const [currentTab, setCurrentTab] = useState(''); + const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false); + + const toggleOpenOnMobile = () => { + setIsSideNavOpenOnMobile(!isSideNavOpenOnMobile); + }; useEffect(() => { setCurrentTab(history.location.pathname); @@ -76,7 +84,13 @@ export const WazuhSecurityPoliciesApp = () => { <> - + toggleOpenOnMobile()} + isOpenOnMobile={isSideNavOpenOnMobile} + aria-label='Ruleset' + items={sideNav} + /> []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/1', + provider: 'native', + status: 'disable', + metadata: { + module: 'syslog2', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/2', + provider: 'native', + status: 'disable', + metadata: { + module: 'syslog3', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/0', + provider: 'native', + status: 'disable', + metadata: { + module: 'syslog4', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/1', + provider: 'native', + status: 'disable', + metadata: { + module: 'syslog5', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/2', + provider: 'native', + status: 'draft', + metadata: { + module: 'syslog6', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/0', + provider: 'custom', + status: 'enable', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/1', + provider: 'custom', + status: 'enable', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/2', + provider: 'custom', + status: 'enable', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/0', + provider: 'custom', + status: 'draft', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/1', + provider: 'custom', + status: 'draft', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, + { + name: 'decoder/syslog/2', + provider: 'custom', + status: 'draft', + metadata: { + module: 'syslog', + title: 'Syslog Decoder event', + description: 'Syslog header', + compatibility: 'This decoder has been tested on Wazuh version 4.3', + author: { + name: 'Wazuh, Inc.', + url: 'https://wazuh.com', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + 'https://www.ietf.org/rfc/rfc5424.txt', + ], + }, + 'parse|event.original': [ + { + pattern: + ' []:<~/ignore/ >', + description: 'BSD Syslog RFC 3164 standard', + }, + { + pattern: + ' :<~/ignore/ >', + description: 'BSD Syslog RFC 3164 no pid', + }, + { + pattern: + ' []: ', + description: 'BSD Syslog RFC 3164 standard ISO8601', + }, + { + pattern: + ' : ', + description: 'BSD Syslog RFC 3164 no pid ISO8601', + }, + { + pattern: ' ', + description: 'RFC3164 example 2 section 5.4', + }, + { + pattern: + ' []:<~/ignore/ >', + description: 'RFC3164 example 4 section 5.4', + }, + ], + normalize: [ + { + map: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, + ], + }, + ], + }, +]; diff --git a/plugins/wazuh-security-policies/public/components/rules/overview.tsx b/plugins/wazuh-security-policies/public/components/rules/overview.tsx new file mode 100644 index 0000000000..29db0bc7cd --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/overview.tsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import { EuiPanel, EuiButton, EuiText } from '@elastic/eui'; +import { HeaderPage } from '../common/header-page'; +import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; +import { SearchBar } from '../common/searchbar'; +import { decoder } from '../integretions/mock-data-rules'; + +export const RulesOverview = () => { + const [query, setQuery] = useState({ text: '' }); + // Header start + const titleHeader = 'Rules'; + const descriptionHeader = LastUpdateContentManagerText({ + status: 'Updated', + lastUpdateDate: '31/01/2025', + }); + const rightSideItems = [ + console.log('clicked')} fill> + Create + , + ]; + // Header end + // Searchbar start + const isActiveOption = [ + { value: 'enable', name: 'Enabla' }, + { value: 'disable', name: 'Disable' }, + { value: 'draft', name: 'Draft' }, + ]; + const integrationOption: string[] = []; + const nativeOrCustomOption = [ + { value: 'native', name: 'Native' }, + { value: 'custom', name: 'Custom' }, + ]; + + for (const item of decoder) { + if (!integrationOption[item.metadata.module]) { + integrationOption.push(item.metadata.module); + } + } + + const filters = [ + { + type: 'field_value_toggle_group', + field: 'status', + name: 'Status', + multiSelect: 'or', + items: isActiveOption, + }, + { + type: 'field_value_toggle_group', + field: 'provider', + name: 'Provider', + multiSelect: 'or', + items: nativeOrCustomOption, + }, + { + type: 'field_value_selection', + field: 'metadata.module', + name: 'Integrations', + multiSelect: 'and', + operator: 'exact', + cache: 10000, // will cache the loaded tags for 10 sec + options: integrationOption.map(integration => ({ + value: integration, + view: {integration}, + })), + }, + ]; + const schema = { + strict: true, + fields: { + status: { + type: 'string', + }, + 'metadata.module': { + type: 'string', + }, + }, + }; + + return ( + + + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx new file mode 100644 index 0000000000..ec9036ef67 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const RuleDetails = () => ( + <> +

RuleDetails

+ +); From 51a37642513c521e101901fa7f13c2a133fe02d5 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:52:52 +0100 Subject: [PATCH 12/30] Add table and refactoring for use in decoders and kvdb --- .../components/common/overview-template.tsx | 271 ++++++++++++++++++ .../mock-data-rules.tsx | 12 + .../public/components/rules/overview.tsx | 90 +----- 3 files changed, 287 insertions(+), 86 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/overview-template.tsx rename plugins/wazuh-security-policies/public/components/{integretions => rules}/mock-data-rules.tsx (99%) diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx new file mode 100644 index 0000000000..05afd46314 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx @@ -0,0 +1,271 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiPanel, + EuiButton, + EuiText, + EuiBasicTable, + EuiHealth, +} from '@elastic/eui'; +import { decoder } from '../rules/mock-data-rules'; +import { getHistory } from '../../plugin-services'; +import { HeaderPage } from './header-page'; +import { LastUpdateContentManagerText } from './last-update-content-manager-text.tsx'; +import { SearchBar } from './searchbar'; +import { NoResultsData } from './no-results'; + +interface OverviewTemplateProps { + view: 'rules' | 'decoders' | 'kvdb'; +} + +export const OverviewTemplate = (props: OverviewTemplateProps) => { + const { view } = props; + const history = getHistory(); + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(5); + const [decoderList, setDecoderList] = useState(decoder); + const [query, setQuery] = useState({ text: '' }); + // Header start + const titleHeader = view; + const descriptionHeader = LastUpdateContentManagerText({ + status: 'Updated', + lastUpdateDate: '31/01/2025', + }); + const rightSideItems = [ + console.log('clicked')} fill> + Create + , + ]; + // Header end + // Searchbar start + const isActiveOption = [ + { value: 'enable', name: 'Enabla' }, + { value: 'disable', name: 'Disable' }, + { value: 'draft', name: 'Draft' }, + ]; + const integrationOption: string[] = []; + const nativeOrCustomOption = [ + { value: 'native', name: 'Native' }, + { value: 'custom', name: 'Custom' }, + ]; + + for (const item of decoderList) { + if (!integrationOption[item.metadata.module]) { + integrationOption.push(item.metadata.module); + } + } + + const filters = [ + { + type: 'field_value_toggle_group', + field: 'status', + multiSelect: 'or', + items: isActiveOption, + }, + { + type: 'field_value_toggle_group', + field: 'provider', + name: 'Provider', + multiSelect: 'or', + items: nativeOrCustomOption, + }, + { + type: 'field_value_selection', + field: 'metadata.module', + name: 'Integrations', + multiSelect: 'and', + cache: 10000, // will cache the loaded tags for 10 sec + options: integrationOption.map(integration => ({ + value: integration, + view: {integration}, + })), + }, + ]; + const schema = { + strict: true, + fields: { + status: { + type: 'string', + }, + 'metadata.module': { + type: 'string', + }, + provider: { + type: 'string', + }, + }, + }; + // Search bar end + // Table start + + useEffect(() => { + if (query.text === '') { + setDecoderList(decoder); + + return; + } + + const decoderFilter = decoderList.filter(decoder => + Object.values(decoder).includes('enable'), + ); + + setDecoderList(decoderFilter); + }, [query]); + + const renderStatus = (status: string) => { + let color: string; + let label: string; + + switch (status) { + case 'enable': { + color = 'success'; + label = 'Enable'; + break; + } + + case 'disable': { + color = 'danger'; + label = 'Disable'; + break; + } + + case 'draft': { + color = 'warning'; + label = 'Draft'; + break; + } + + default: { + color = 'text'; + label = '-141'; + break; + } + } + + return {label}; + }; + + const columns = [ + { + field: 'name', + name: 'Name', + sortable: true, + truncateText: true, + }, + { + field: 'metadata.module', + name: 'Integration', + sortable: true, + truncateText: true, + }, + { + field: 'metadata.description', + name: 'Description', + truncateText: true, + }, + { + field: 'provider', + name: 'Provider', + sortable: true, + truncateText: true, + render: (item: string) => ( + {item} + ), + }, + { + field: 'status', + name: 'Status', + dataType: 'boolean', + render: (status: string) => renderStatus(status), + sortable: true, + mobileOptions: { + show: false, + }, + }, + { + name: 'Actions', + actions: [ + { + name: 'Edit', + isPrimary: true, + description: 'Edit this user', + icon: 'pencil', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-edit', + }, + { + name: 'Remove', + description: 'Remove this element', + isPrimary: true, + icon: 'trash', + color: 'danger', + type: 'icon', + onClick: () => {}, + }, + { + name: 'Clone', + description: 'Clone this user', + icon: 'copy', + onClick: () => {}, + }, + { + name: 'Share', + description: 'Share this user', + icon: 'share', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-share', + }, + ], + }, + ]; + + const onTableChange = ({ + page, + }: { + page: { index: number; size: number }; + }) => { + const { index: pageIndex, size: pageSize } = page; + + setPageIndex(pageIndex); + setPageSize(pageSize); + }; + + const pagination = { + pageIndex: pageIndex, + pageSize: pageSize, + totalItemCount: decoderList.length, + pageSizeOptions: [3, 5, 8], + }; + + const handleNavigation = (path: string) => { + history.push(path); + }; + + const getRowProps = item => ({ + onClick: () => + handleNavigation(`/${titleHeader}/${encodeURIComponent(item.name)}`), + }); + + // Table end + + return ( + + + + + {decoder.length <= 0 && } + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/integretions/mock-data-rules.tsx b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx similarity index 99% rename from plugins/wazuh-security-policies/public/components/integretions/mock-data-rules.tsx rename to plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx index 2faffaff16..7aefaab9d0 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/mock-data-rules.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -1,5 +1,6 @@ export const decoder = [ { + id: 1, name: 'decoder/syslog/0', provider: 'native', status: 'enable', @@ -62,6 +63,7 @@ export const decoder = [ ], }, { + id: 2, name: 'decoder/syslog/1', provider: 'native', status: 'disable', @@ -124,6 +126,7 @@ export const decoder = [ ], }, { + id: 3, name: 'decoder/syslog/2', provider: 'native', status: 'disable', @@ -186,6 +189,7 @@ export const decoder = [ ], }, { + id: 4, name: 'decoder/syslog/0', provider: 'native', status: 'disable', @@ -248,6 +252,7 @@ export const decoder = [ ], }, { + id: 5, name: 'decoder/syslog/1', provider: 'native', status: 'disable', @@ -310,6 +315,7 @@ export const decoder = [ ], }, { + id: 6, name: 'decoder/syslog/2', provider: 'native', status: 'draft', @@ -372,6 +378,7 @@ export const decoder = [ ], }, { + id: 7, name: 'decoder/syslog/0', provider: 'custom', status: 'enable', @@ -434,6 +441,7 @@ export const decoder = [ ], }, { + id: 8, name: 'decoder/syslog/1', provider: 'custom', status: 'enable', @@ -496,6 +504,7 @@ export const decoder = [ ], }, { + id: 9, name: 'decoder/syslog/2', provider: 'custom', status: 'enable', @@ -558,6 +567,7 @@ export const decoder = [ ], }, { + id: 10, name: 'decoder/syslog/0', provider: 'custom', status: 'draft', @@ -620,6 +630,7 @@ export const decoder = [ ], }, { + id: 11, name: 'decoder/syslog/1', provider: 'custom', status: 'draft', @@ -682,6 +693,7 @@ export const decoder = [ ], }, { + id: 12, name: 'decoder/syslog/2', provider: 'custom', status: 'draft', diff --git a/plugins/wazuh-security-policies/public/components/rules/overview.tsx b/plugins/wazuh-security-policies/public/components/rules/overview.tsx index 29db0bc7cd..d8e82b2732 100644 --- a/plugins/wazuh-security-policies/public/components/rules/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/overview.tsx @@ -1,90 +1,8 @@ -import React, { useState } from 'react'; -import { EuiPanel, EuiButton, EuiText } from '@elastic/eui'; -import { HeaderPage } from '../common/header-page'; -import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; -import { SearchBar } from '../common/searchbar'; -import { decoder } from '../integretions/mock-data-rules'; +import React from 'react'; +import { OverviewTemplate } from '../common/overview-template'; export const RulesOverview = () => { - const [query, setQuery] = useState({ text: '' }); - // Header start - const titleHeader = 'Rules'; - const descriptionHeader = LastUpdateContentManagerText({ - status: 'Updated', - lastUpdateDate: '31/01/2025', - }); - const rightSideItems = [ - console.log('clicked')} fill> - Create - , - ]; - // Header end - // Searchbar start - const isActiveOption = [ - { value: 'enable', name: 'Enabla' }, - { value: 'disable', name: 'Disable' }, - { value: 'draft', name: 'Draft' }, - ]; - const integrationOption: string[] = []; - const nativeOrCustomOption = [ - { value: 'native', name: 'Native' }, - { value: 'custom', name: 'Custom' }, - ]; + const view = 'rules'; - for (const item of decoder) { - if (!integrationOption[item.metadata.module]) { - integrationOption.push(item.metadata.module); - } - } - - const filters = [ - { - type: 'field_value_toggle_group', - field: 'status', - name: 'Status', - multiSelect: 'or', - items: isActiveOption, - }, - { - type: 'field_value_toggle_group', - field: 'provider', - name: 'Provider', - multiSelect: 'or', - items: nativeOrCustomOption, - }, - { - type: 'field_value_selection', - field: 'metadata.module', - name: 'Integrations', - multiSelect: 'and', - operator: 'exact', - cache: 10000, // will cache the loaded tags for 10 sec - options: integrationOption.map(integration => ({ - value: integration, - view: {integration}, - })), - }, - ]; - const schema = { - strict: true, - fields: { - status: { - type: 'string', - }, - 'metadata.module': { - type: 'string', - }, - }, - }; - - return ( - - - - - ); + return ; }; From b5f0acea7861d828ac2f62d4b1aaeddf6af7fc95 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:53:31 +0100 Subject: [PATCH 13/30] Add decoders and kvdb views --- .../public/components/app.tsx | 12 +++++++++--- .../public/components/decoders/decoder-details.tsx | 7 +++++++ .../public/components/decoders/overview.tsx | 8 ++++++++ .../public/components/kvdb/kvdb-details.tsx | 7 +++++++ .../public/components/kvdb/overview.tsx | 8 ++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx create mode 100644 plugins/wazuh-security-policies/public/components/decoders/overview.tsx create mode 100644 plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx create mode 100644 plugins/wazuh-security-policies/public/components/kvdb/overview.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 135546d068..677939036f 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -13,6 +13,10 @@ import { IntegrationOverview } from './integretions/overview'; import { IntegrationView } from './integretions/integration-details'; import { RulesOverview } from './rules/overview'; import { RuleDetails } from './rules/rule-details'; +import { DecodersOverview } from './decoders/overview'; +import { DecoderDetails } from './decoders/decoder-details'; +import { KVDBOverview } from './kvdb/overview'; +import { KVDBDetails } from './kvdb/kvdb-details'; interface ViewInterface { name: string; @@ -37,12 +41,14 @@ const views: ViewInterface[] = [ { name: 'Decoders', id: 'decoders', - render: () =>
Decoders
, + render: () => , + renderDetails: () => , }, { name: 'KVDB', id: 'kvdb', - render: () =>
KVDBs
, + render: () => , + renderDetails: () => , }, ]; @@ -125,7 +131,7 @@ export const WazuhSecurityPoliciesApp = () => { }, { className: 'osdBreadcrumbs', - text: id, + text: decodeURIComponent(id), }, ]); diff --git a/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx b/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx new file mode 100644 index 0000000000..a93a11c531 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const DecoderDetails = () => ( + <> +

DecoderDetails

+ +); diff --git a/plugins/wazuh-security-policies/public/components/decoders/overview.tsx b/plugins/wazuh-security-policies/public/components/decoders/overview.tsx new file mode 100644 index 0000000000..d4b1e8bf97 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/decoders/overview.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { OverviewTemplate } from '../common/overview-template'; + +export const DecodersOverview = () => { + const view = 'decoders'; + + return ; +}; diff --git a/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx b/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx new file mode 100644 index 0000000000..a0660a9172 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const KVDBDetails = () => ( + <> +

KVDBDetails

+ +); diff --git a/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx b/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx new file mode 100644 index 0000000000..16dec6c83e --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { OverviewTemplate } from '../common/overview-template'; + +export const KVDBOverview = () => { + const view = 'kvdb'; + + return ; +}; From f97305e49bc570315401f24e152453511430b1f7 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:43:52 +0100 Subject: [PATCH 14/30] Refactor route --- .../public/components/app.tsx | 126 +++------- .../components/common/overview-template.tsx | 6 +- .../public/components/common/views.tsx | 221 ++++++++++++++++++ 3 files changed, 252 insertions(+), 101 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/views.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 677939036f..365b3629e4 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; +import { I18nProvider } from '@osd/i18n/react'; import { EuiPage, EuiPageBody, @@ -9,48 +9,7 @@ import { } from '@elastic/eui'; import { Router, Route, Switch, Redirect, useParams } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; -import { IntegrationOverview } from './integretions/overview'; -import { IntegrationView } from './integretions/integration-details'; -import { RulesOverview } from './rules/overview'; -import { RuleDetails } from './rules/rule-details'; -import { DecodersOverview } from './decoders/overview'; -import { DecoderDetails } from './decoders/decoder-details'; -import { KVDBOverview } from './kvdb/overview'; -import { KVDBDetails } from './kvdb/kvdb-details'; - -interface ViewInterface { - name: string; - id: string; - render: () => React.ReactNode; - renderDetails?: () => React.ReactNode; -} - -const views: ViewInterface[] = [ - { - name: 'Integrations', - id: 'integrations', - render: () => , - renderDetails: () => , - }, - { - name: 'Rules', - id: 'rules', - render: () => , - renderDetails: () => , - }, - { - name: 'Decoders', - id: 'decoders', - render: () => , - renderDetails: () => , - }, - { - name: 'KVDB', - id: 'kvdb', - render: () => , - renderDetails: () => , - }, -]; +import { views } from './common/views'; export const WazuhSecurityPoliciesApp = () => { const history = getHistory(); @@ -69,16 +28,19 @@ export const WazuhSecurityPoliciesApp = () => { { name: 'Ruleset', id: 'wazuhRuleset', - items: views.map(item => ({ - id: item.id, - name: item.name, - onClick: () => { - history.push(`/${item.id}`); - setCurrentTab(item.id); - }, - isSelected: - item.id === currentTab || history.location.pathname === `/${item.id}`, - })), + items: views + .filter(view => view.renderOnMenu) + .map(item => ({ + id: item.id, + name: item.name, + onClick: () => { + history.push(`${item.path}`); + setCurrentTab(item.id); + }, + isSelected: + item.id === currentTab || + history.location.pathname === `/${item.id}`, + })), }, ]; @@ -90,6 +52,7 @@ export const WazuhSecurityPoliciesApp = () => { <> + {} toggleOpenOnMobile()} @@ -107,59 +70,26 @@ export const WazuhSecurityPoliciesApp = () => { > {views.map(view => [ - view.renderDetails && ( - { - const { id } = useParams(); - - getCore().chrome.setBreadcrumbs([ - { - text: ( - - ), - href: getCore().application.getUrlForApp( - 'wazuhSecurityPolicies', - { - path: `#/${view.id}`, - }, - ), - }, - { - className: 'osdBreadcrumbs', - text: decodeURIComponent(id), - }, - ]); - - return view.renderDetails(); - }} - /> - ), { - getCore().chrome.setBreadcrumbs([ - { - className: 'osdBreadcrumbs', - text: ( - - ), - }, - ]); + const { id } = useParams() || null; + + if (id) { + getCore().chrome.setBreadcrumbs( + view.breadcrumb(decodeURIComponent(id)), + ); + } else { + getCore().chrome.setBreadcrumbs(view.breadcrumb()); + } return view.render(); }} />, ])} - +
diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx index 05afd46314..c0e877b3ff 100644 --- a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx @@ -25,7 +25,7 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { const [decoderList, setDecoderList] = useState(decoder); const [query, setQuery] = useState({ text: '' }); // Header start - const titleHeader = view; + const titleHeader =

{view}

; const descriptionHeader = LastUpdateContentManagerText({ status: 'Updated', lastUpdateDate: '31/01/2025', @@ -38,7 +38,7 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { // Header end // Searchbar start const isActiveOption = [ - { value: 'enable', name: 'Enabla' }, + { value: 'enable', name: 'Enable' }, { value: 'disable', name: 'Disable' }, { value: 'draft', name: 'Draft' }, ]; @@ -244,7 +244,7 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { const getRowProps = item => ({ onClick: () => - handleNavigation(`/${titleHeader}/${encodeURIComponent(item.name)}`), + handleNavigation(`/${view}/${encodeURIComponent(item.name)}`), }); // Table end diff --git a/plugins/wazuh-security-policies/public/components/common/views.tsx b/plugins/wazuh-security-policies/public/components/common/views.tsx new file mode 100644 index 0000000000..5f872ad471 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/views.tsx @@ -0,0 +1,221 @@ +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { FormattedMessage } from '@osd/i18n/react'; +import { IntegrationOverview } from '../integretions/overview'; +import { IntegrationView } from '../integretions/integration-details'; +import { RulesOverview } from '../rules/overview'; +import { RuleDetails } from '../rules/rule-details'; +import { DecodersOverview } from '../decoders/overview'; +import { DecoderDetails } from '../decoders/decoder-details'; +import { KVDBOverview } from '../kvdb/overview'; +import { KVDBDetails } from '../kvdb/kvdb-details'; +import { getCore } from '../../plugin-services'; + +interface ViewInterface { + name: string; + id: string; + path: string; + renderOnMenu: boolean; + renderMenu: boolean; + render: () => React.ReactNode; + breadcrumb: ( + name?: string, + ) => { text: string | React.ReactNode; className?: string }[]; +} + +export const views: ViewInterface[] = [ + { + name: i18n.translate('wz-security-policies-integrations', { + defaultMessage: 'Integrations', + }), + id: 'integrations', + path: '/integrations', + renderOnMenu: true, + renderMenu: true, + render: () => , + breadcrumb: () => [ + { + className: 'osdBreadcrumbs', + text: ( + + ), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-integration-details', { + defaultMessage: 'Integrations details', + }), + id: 'integrationsDetails', + path: '/integrations/:id', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: (name?: string) => [ + { + text: ( + view.id === 'integrations')?.name + } + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'integrations')?.path}`, + }), + }, + { + className: 'osdBreadcrumbs', + text: decodeURIComponent(name || ''), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-rules', { + defaultMessage: 'Rules', + }), + id: 'rules', + path: '/rules', + renderOnMenu: true, + renderMenu: true, + render: () => , + breadcrumb: () => [ + { + className: 'osdBreadcrumbs', + text: ( + + ), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-rule-details', { + defaultMessage: 'Rule details', + }), + id: 'rulesDetails', + path: '/rules/:id', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: (name?: string) => [ + { + text: ( + view.id === 'rules')?.name} + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'rules')?.path}`, + }), + }, + { + className: 'osdBreadcrumbs', + text: decodeURIComponent(name || ''), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-decoders', { + defaultMessage: 'Decoders', + }), + id: 'decoders', + path: '/decoders', + renderOnMenu: true, + renderMenu: true, + render: () => , + breadcrumb: () => [ + { + className: 'osdBreadcrumbs', + text: ( + + ), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-decoders-details', { + defaultMessage: 'Decoders details', + }), + id: 'decodersDetails', + path: '/decoders/:id', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: (name?: string) => [ + { + text: ( + view.id === 'decoders')?.name} + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'decoders')?.path}`, + }), + }, + { + className: 'osdBreadcrumbs', + text: decodeURIComponent(name || ''), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-kvdb', { + defaultMessage: 'KVDB', + }), + id: 'kvdb', + path: '/kvdb', + renderOnMenu: true, + renderMenu: true, + render: () => , + breadcrumb: () => [ + { + className: 'osdBreadcrumbs', + text: ( + + ), + }, + ], + }, + { + name: i18n.translate('wz-security-policies-kvdb-details', { + defaultMessage: 'KVDB details', + }), + id: 'kvdbDetails', + path: '/kvdb/:id', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: (name?: string) => [ + { + text: ( + view.id === 'kvdb')?.name} + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'kvdb')?.path}`, + }), + }, + { + className: 'osdBreadcrumbs', + text: decodeURIComponent(name || ''), + }, + ], + }, +]; From efbab28ef10ab70d26482e25c09d149c52ecd374 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:05:36 +0100 Subject: [PATCH 15/30] Optional render menu --- .../public/components/app.tsx | 31 ++++++++++++------- .../public/components/common/views.tsx | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 365b3629e4..7ef83437e4 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -14,6 +14,7 @@ import { views } from './common/views'; export const WazuhSecurityPoliciesApp = () => { const history = getHistory(); const [currentTab, setCurrentTab] = useState(''); + const [renderMenu, setRenderMenu] = useState(true); const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false); const toggleOpenOnMobile = () => { @@ -22,7 +23,7 @@ export const WazuhSecurityPoliciesApp = () => { useEffect(() => { setCurrentTab(history.location.pathname); - }, []); + }, [history.location.pathname]); const sideNav = [ { @@ -51,16 +52,18 @@ export const WazuhSecurityPoliciesApp = () => { <> - - {} - toggleOpenOnMobile()} - isOpenOnMobile={isSideNavOpenOnMobile} - aria-label='Ruleset' - items={sideNav} - /> - + {renderMenu && ( + + toggleOpenOnMobile()} + isOpenOnMobile={isSideNavOpenOnMobile} + aria-label='Ruleset' + items={sideNav} + /> + + )} + { getCore().chrome.setBreadcrumbs(view.breadcrumb()); } + if (view.renderMenu) { + setRenderMenu(true); + } else { + setRenderMenu(false); + } + return view.render(); }} />, diff --git a/plugins/wazuh-security-policies/public/components/common/views.tsx b/plugins/wazuh-security-policies/public/components/common/views.tsx index 5f872ad471..daa333ea6b 100644 --- a/plugins/wazuh-security-policies/public/components/common/views.tsx +++ b/plugins/wazuh-security-policies/public/components/common/views.tsx @@ -52,7 +52,7 @@ export const views: ViewInterface[] = [ id: 'integrationsDetails', path: '/integrations/:id', renderOnMenu: false, - renderMenu: false, + renderMenu: true, render: () => , breadcrumb: (name?: string) => [ { From c419cb753a3ab661dfb5e8e13fda4dc5c36f299f Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:24:54 +0100 Subject: [PATCH 16/30] Add header details --- .../public/components/common/header-page.tsx | 20 ----- .../components/common/overview-template.tsx | 6 +- .../integretions/integration-details.tsx | 6 +- .../components/integretions/overview.tsx | 6 +- .../public/components/rules/rule-details.tsx | 73 +++++++++++++++++-- 5 files changed, 77 insertions(+), 34 deletions(-) delete mode 100644 plugins/wazuh-security-policies/public/components/common/header-page.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/header-page.tsx b/plugins/wazuh-security-policies/public/components/common/header-page.tsx deleted file mode 100644 index 44f32dccb7..0000000000 --- a/plugins/wazuh-security-policies/public/components/common/header-page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { EuiPageHeader } from '@elastic/eui'; - -interface HeaderPageProps { - titleHeader: React.ReactNode | string; - descriptionHeader: React.ReactNode | string; - rightSideItems?: React.ReactNode[]; -} - -export const HeaderPage = (props: HeaderPageProps) => { - const { titleHeader, descriptionHeader, rightSideItems } = props; - - return ( - - ); -}; diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx index c0e877b3ff..7a07cf5d05 100644 --- a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx @@ -5,10 +5,10 @@ import { EuiText, EuiBasicTable, EuiHealth, + EuiPageHeader, } from '@elastic/eui'; import { decoder } from '../rules/mock-data-rules'; import { getHistory } from '../../plugin-services'; -import { HeaderPage } from './header-page'; import { LastUpdateContentManagerText } from './last-update-content-manager-text.tsx'; import { SearchBar } from './searchbar'; import { NoResultsData } from './no-results'; @@ -251,8 +251,8 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { return ( - diff --git a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx index 656cf442dc..2f88222d7e 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx @@ -5,9 +5,9 @@ import { EuiButton, EuiDescriptionList, EuiPanel, + EuiPageHeader, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { HeaderPage } from '../common/header-page'; import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; import { integrations } from './mock-data-integrations'; import './integrations.scss'; @@ -108,8 +108,8 @@ export const IntegrationView = () => { return ( <> - diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx index 5f3c4d3a33..d4dcd61b87 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -5,10 +5,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiText, + EuiPageHeader, } from '@elastic/eui'; import './integrations.scss'; import { SearchBar } from '../common/searchbar'; -import { HeaderPage } from '../common/header-page'; import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; import { NoResultsData } from '../common/no-results'; import { CardIntegration } from './components/card-integration'; @@ -111,8 +111,8 @@ export const IntegrationOverview = () => { return ( <> - diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index ec9036ef67..c84da5f972 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -1,7 +1,70 @@ import React from 'react'; +import { + EuiPageHeader, + EuiButton, + EuiButtonEmpty, + EuiText, + EuiButtonIcon, +} from '@elastic/eui'; +import { useParams } from 'react-router-dom'; +import { decoder } from './mock-data-rules'; -export const RuleDetails = () => ( - <> -

RuleDetails

- -); +export const RuleDetails = () => { + const { id: name } = useParams(); + const item = decoder.find(item => item.name === decodeURIComponent(name)); + const title = ( +
+ {item?.name} + {}} + iconType='pencil' + aria-label='Edit' + /> +
+ ); + const buttons = [ + + Edit + , + {}} + > + Test decoder + , + {}} + style={{ textTransform: 'capitalize' }} + > + {item?.status} + , + {}} + > + View in XML + , + ]; + + return ( + <> + + + ); +}; From 91b829a881806abf09fbc965f7db13dc87438eab Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:56:50 +0100 Subject: [PATCH 17/30] Add header details and start rendering inputs --- .../components/common/overview-template.tsx | 2 +- .../components/common/render-inputs.tsx | 22 + .../integretions/integration-details.tsx | 2 +- .../components/integretions/overview.tsx | 2 +- .../components/rules/mock-data-rules.tsx | 468 +++++------------- .../public/components/rules/rule-details.tsx | 66 +++ 6 files changed, 211 insertions(+), 351 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/render-inputs.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx index 7a07cf5d05..4c9e06a725 100644 --- a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx @@ -253,7 +253,7 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { diff --git a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx new file mode 100644 index 0000000000..9f0d078c97 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { EuiBasicTable } from '@elastic/eui'; + +const inputArray = (input: any) => { + const inputs = input.map((item: any) => { + Object.entries(item).map(([key, value]) => ({ key, value })); + }); + const columns = [ + { + field: 'key', + name: 'Fields', + }, + { + field: 'value', + name: 'Conditions', + }, + ]; + + return ; +}; + +export const renderInputs = input => inputArray(input); diff --git a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx index 2f88222d7e..52c139ce1e 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx @@ -110,7 +110,7 @@ export const IntegrationView = () => { <> diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx index d4dcd61b87..07b909524e 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -113,7 +113,7 @@ export const IntegrationOverview = () => { <> diff --git a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx index 7aefaab9d0..933b35cb08 100644 --- a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -20,35 +20,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -83,35 +64,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -146,35 +108,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -209,35 +152,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -272,35 +196,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -335,35 +240,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -398,35 +284,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -461,35 +328,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -524,35 +372,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -587,35 +416,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -650,35 +460,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { @@ -713,35 +504,16 @@ export const decoder = [ ], }, 'parse|event.original': [ - { - pattern: - ' []:<~/ignore/ >', - description: 'BSD Syslog RFC 3164 standard', - }, - { - pattern: - ' :<~/ignore/ >', - description: 'BSD Syslog RFC 3164 no pid', - }, - { - pattern: - ' []: ', - description: 'BSD Syslog RFC 3164 standard ISO8601', - }, - { - pattern: - ' : ', - description: 'BSD Syslog RFC 3164 no pid ISO8601', - }, - { - pattern: ' ', - description: 'RFC3164 example 2 section 5.4', - }, - { - pattern: - ' []:<~/ignore/ >', - description: 'RFC3164 example 4 section 5.4', - }, + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', ], normalize: [ { diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index c84da5f972..50e42877a8 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -5,9 +5,17 @@ import { EuiButtonEmpty, EuiText, EuiButtonIcon, + EuiSteps, + EuiPanel, + EuiAccordion, + EuiHorizontalRule, + EuiForm, + EuiFormRow, + EuiFieldText, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; import { decoder } from './mock-data-rules'; +import { renderInputs } from '../common/render-inputs'; export const RuleDetails = () => { const { id: name } = useParams(); @@ -54,6 +62,63 @@ export const RuleDetails = () => { , ]; + const step = item => { + const removeEntries = ['id', 'name', 'provider', 'status']; + const arraySteps = Object.entries(item) + .filter(([key]) => !removeEntries.includes(key)) + .map(([key, value]) => ({ + key, + value, + })); + const steps = arraySteps.map(step => ({ + title: 'Better step', + children: ( + + + + + + {Object.entries(step.value) + .map(([key, value]) => ({ key, value })) + .map((item, index) => { + if (Array.isArray(item.value)) { + return renderInputs(item.value); + } + + if (typeof item.value !== 'string') { + return null; + } + + return ( + + + + ); + })} + + + + ), + })); + + return steps; + }; + return ( <> { }} rightSideItems={buttons} /> + ); }; From 9dce3c1a660e082201a705f3399d70e1a2b92ad8 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:57:43 +0100 Subject: [PATCH 18/30] Fix lint --- .../public/components/rules/rule-details.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index 50e42877a8..bfc9583757 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -14,8 +14,8 @@ import { EuiFieldText, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { decoder } from './mock-data-rules'; import { renderInputs } from '../common/render-inputs'; +import { decoder } from './mock-data-rules'; export const RuleDetails = () => { const { id: name } = useParams(); @@ -63,9 +63,9 @@ export const RuleDetails = () => { ]; const step = item => { - const removeEntries = ['id', 'name', 'provider', 'status']; + const removeEntries = new Set(['id', 'name', 'provider', 'status']); const arraySteps = Object.entries(item) - .filter(([key]) => !removeEntries.includes(key)) + .filter(([key]) => !removeEntries.has(key)) .map(([key, value]) => ({ key, value, From 20afcdd0731d99285f77845e599411296cbcb43d Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:20:51 +0100 Subject: [PATCH 19/30] Add step title --- .../components/rules/mock-data-rules.tsx | 34 +++------ .../public/components/rules/rule-details.tsx | 76 +++++++++++++++++-- .../utils/capitalize-first-letter.ts | 2 + 3 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/utils/capitalize-first-letter.ts diff --git a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx index 933b35cb08..60bee8b997 100644 --- a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -19,6 +19,8 @@ export const decoder = [ 'https://www.ietf.org/rfc/rfc5424.txt', ], }, + check: + ' []:<~/ignore/ >', 'parse|event.original': [ ' []:<~/ignore/ >', @@ -221,7 +223,7 @@ export const decoder = [ }, { id: 6, - name: 'decoder/syslog/2', + name: 'check/string', provider: 'native', status: 'draft', metadata: { @@ -239,18 +241,8 @@ export const decoder = [ 'https://www.ietf.org/rfc/rfc5424.txt', ], }, - 'parse|event.original': [ + check: ' []:<~/ignore/ >', - - ' :<~/ignore/ >', - - ' []: ', - - ' : ', - ' ', - - ' []:<~/ignore/ >', - ], normalize: [ { map: [ @@ -265,7 +257,7 @@ export const decoder = [ }, { id: 7, - name: 'decoder/syslog/0', + name: 'check/array', provider: 'custom', status: 'enable', metadata: { @@ -295,16 +287,12 @@ export const decoder = [ ' []:<~/ignore/ >', ], - normalize: [ - { - map: [ - { 'event.kind': 'event' }, - { 'wazuh.decoders': 'array_append(syslog)' }, - { 'related.hosts': 'array_append($host.hostname)' }, - { 'process.name': 'rename($TAG)' }, - { 'host.ip': 'array_append($tmp.host_ip)' }, - ], - }, + check: [ + { 'event.kind': 'event' }, + { 'wazuh.decoders': 'array_append(syslog)' }, + { 'related.hosts': 'array_append($host.hostname)' }, + { 'process.name': 'rename($TAG)' }, + { 'host.ip': 'array_append($tmp.host_ip)' }, ], }, { diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index bfc9583757..68f331e69f 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -15,8 +15,19 @@ import { } from '@elastic/eui'; import { useParams } from 'react-router-dom'; import { renderInputs } from '../common/render-inputs'; +import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; import { decoder } from './mock-data-rules'; +const possibleSteps = { + metadata: 'metadata', + check: 'check', + parse: 'parse|', + normalize: 'normalize', + allow: 'allow', + output: 'output', + definitions: 'definitions', +}; + export const RuleDetails = () => { const { id: name } = useParams(); const item = decoder.find(item => item.name === decodeURIComponent(name)); @@ -62,7 +73,62 @@ export const RuleDetails = () => { , ]; - const step = item => { + const renderCardTitle = (stepName: string, item: any) => { + switch (true) { + case stepName === possibleSteps.metadata: { + return `${capitalizeFirstLetter(stepName)} of ${item.value.title}, from ${item.value.module}`; + } + + case stepName === possibleSteps.check: { + if (typeof item.value === 'string') { + return `${capitalizeFirstLetter(stepName)}: ${item.value}`; + } + + return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map((obj: any) => Object.keys(obj)[0]).join(', ')}`; + } + + case stepName.startsWith(possibleSteps.parse): { + return stepName.split('|')[1]; + } + + case stepName === possibleSteps.normalize: { + return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map( + (obj: any) => + Object.values( + obj.map.map((subObj: any) => Object.keys(subObj)[0]), + ).join(', '), + )}`; + } + + case stepName === possibleSteps.allow: { + return capitalizeFirstLetter(stepName); + } + + case stepName === possibleSteps.output: { + return capitalizeFirstLetter(stepName); + } + + case stepName === possibleSteps.definitions: { + return capitalizeFirstLetter(stepName); + } + + default: { + return capitalizeFirstLetter(stepName); + } + } + }; + + const renderTitleStep = (stepName: string) => { + let title = stepName; + + if (stepName.startsWith(possibleSteps.parse)) { + title = stepName.split('|')[0]; + } + + return capitalizeFirstLetter(title); + }; + + const step = (item: any) => { const removeEntries = new Set(['id', 'name', 'provider', 'status']); const arraySteps = Object.entries(item) .filter(([key]) => !removeEntries.has(key)) @@ -71,13 +137,13 @@ export const RuleDetails = () => { value, })); const steps = arraySteps.map(step => ({ - title: 'Better step', + title: renderTitleStep(step.key), children: ( @@ -89,8 +155,8 @@ export const RuleDetails = () => { return renderInputs(item.value); } - if (typeof item.value !== 'string') { - return null; + if (typeof item.value === 'object') { + return renderInputs(item.value); } return ( diff --git a/plugins/wazuh-security-policies/public/components/utils/capitalize-first-letter.ts b/plugins/wazuh-security-policies/public/components/utils/capitalize-first-letter.ts new file mode 100644 index 0000000000..d5b4692b02 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/capitalize-first-letter.ts @@ -0,0 +1,2 @@ +export const capitalizeFirstLetter = (string: string) => + string.charAt(0).toUpperCase() + string.slice(1); From 0866438a87a8fd681a5fdc342cf0c5f6c8206f56 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:46:17 +0100 Subject: [PATCH 20/30] Add render string and object inputs --- .../components/common/render-inputs.tsx | 118 +++++++++++++++--- .../components/rules/mock-data-rules.tsx | 7 +- .../public/components/rules/rule-details.tsx | 33 +---- 3 files changed, 105 insertions(+), 53 deletions(-) diff --git a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx index 9f0d078c97..464d246a1c 100644 --- a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx @@ -1,22 +1,108 @@ import React from 'react'; -import { EuiBasicTable } from '@elastic/eui'; +import { + EuiBasicTable, + EuiFormRow, + EuiFieldText, + EuiComboBox, +} from '@elastic/eui'; + +const possibleSteps = new Set([ + 'metadata', + 'check', + 'normalize', + 'allow', + 'output', + 'definitions', +]); const inputArray = (input: any) => { - const inputs = input.map((item: any) => { - Object.entries(item).map(([key, value]) => ({ key, value })); + let onTable = false; + const inputs = input.value.map((item: any) => { + if (!Number.isNaN(Number.parseInt(input.key))) { + onTable = true; + + return item; + } + + if (typeof item === 'string') { + onTable = true; + + return { label: item, value: item }; + } + + return Object.entries(item).map(([key, value]) => ({ key, value })); }); - const columns = [ - { - field: 'key', - name: 'Fields', - }, - { - field: 'value', - name: 'Conditions', - }, - ]; - - return ; + const comboBoxInput = ( + + + + ); + const tableInput = ( + + ); + + return onTable ? tableInput : comboBoxInput; +}; + +const inputString = (input: { key: string; value: any }) => ( + + + +); + +const inputObject = (input: { key: string; value: any }) => { + const inputsList = Object.entries(input.value).map(([key, value]) => ({ + key: `${input.key}.${key}`, + value, + })); + + return inputsList.map(item => inputString(item)); }; -export const renderInputs = input => inputArray(input); +export const renderInputs = (input: { key: string; value: any }) => { + if (possibleSteps.has(input.key)) { + const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ + key, + value, + })); + + return inputsSteps.map(step => renderInputs(step)); + } + + if (typeof input.value === 'string') { + return inputString(input); + } + + return Array.isArray(input.value) ? inputArray(input) : inputObject(input); +}; diff --git a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx index 60bee8b997..49d91a03fd 100644 --- a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -7,6 +7,7 @@ export const decoder = [ metadata: { module: 'syslog', title: 'Syslog Decoder event', + versions: ['4.3', '4.4'], description: 'Syslog header', compatibility: 'This decoder has been tested on Wazuh version 4.3', author: { @@ -23,14 +24,10 @@ export const decoder = [ ' []:<~/ignore/ >', 'parse|event.original': [ ' []:<~/ignore/ >', - ' :<~/ignore/ >', - ' []: ', - ' : ', ' ', - ' []:<~/ignore/ >', ], normalize: [ @@ -54,6 +51,7 @@ export const decoder = [ module: 'syslog2', title: 'Syslog Decoder event', description: 'Syslog header', + versions: ['4.3', '4.4'], compatibility: 'This decoder has been tested on Wazuh version 4.3', author: { name: 'Wazuh, Inc.', @@ -67,7 +65,6 @@ export const decoder = [ }, 'parse|event.original': [ ' []:<~/ignore/ >', - ' :<~/ignore/ >', ' []: ', diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index 68f331e69f..8b6ebb3ea6 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -10,8 +10,6 @@ import { EuiAccordion, EuiHorizontalRule, EuiForm, - EuiFormRow, - EuiFieldText, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; import { renderInputs } from '../common/render-inputs'; @@ -147,36 +145,7 @@ export const RuleDetails = () => { > - - {Object.entries(step.value) - .map(([key, value]) => ({ key, value })) - .map((item, index) => { - if (Array.isArray(item.value)) { - return renderInputs(item.value); - } - - if (typeof item.value === 'object') { - return renderInputs(item.value); - } - - return ( - - - - ); - })} - + {renderInputs(step)} ), From 643a3e30665566f42d19937a7756bb561e626f5f Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 24 Jan 2025 21:02:09 +0100 Subject: [PATCH 21/30] Update mock data rule --- .../components/rules/mock-data-rules.tsx | 347 ++++++++++++++---- 1 file changed, 268 insertions(+), 79 deletions(-) diff --git a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx index 49d91a03fd..ca7f222101 100644 --- a/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -1,131 +1,316 @@ export const decoder = [ { id: 1, - name: 'decoder/syslog/0', + name: 'rule/discovery_kernel_module_enumeration/0', provider: 'native', status: 'enable', metadata: { - module: 'syslog', - title: 'Syslog Decoder event', - versions: ['4.3', '4.4'], - description: 'Syslog header', - compatibility: 'This decoder has been tested on Wazuh version 4.3', + module: 'Linux', + title: 'System Information Discovery', + description: 'Detects System Information Discovery commands.', + compatibility: '', + versions: [''], author: { name: 'Wazuh, Inc.', - url: 'https://wazuh.com', - date: '2022/11/08', + date: '2024/09/09', }, references: [ - 'https://www.ietf.org/rfc/rfc3164.txt', - 'https://www.ietf.org/rfc/rfc5424.txt', + 'https://github.com/redcanaryco/atomic-red-team/blob/f296668303c29d3f4c07e42bdd2b28d8dd6625f9/atomics/T1082/T1082.md', + 'https://github.com/SigmaHQ/sigma/blob/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml', + ], + }, + definitions: { + excluded_process_parents_name: [ + 'mkinitramfs', + 'cryptroot', + 'framebuffer', + 'dracut', + 'jem', + 'thin-provisioning-tools', + 'readykernel', + 'lvm2', + 'vz-start', + 'iscsi', + 'mdadm', + 'ovalprobes', + 'bcache', + 'plymouth', + 'dkms', + 'overlayroot', + 'weak-modules', + 'zfs', ], }, check: - ' []:<~/ignore/ >', - 'parse|event.original': [ - ' []:<~/ignore/ >', - ' :<~/ignore/ >', - ' []: ', - ' : ', - ' ', - ' []:<~/ignore/ >', - ], + "$event.module == sysmon-linux AND array_contains($event.category, process) AND array_contains($event.type, start) AND (($process.name == lsmod OR $process.name == modinfo) OR ($process.name == kmod AND array_contains($process.args, list)) OR ($process.name == depmod AND array_contains_any($process.args, '--all', '-a'))) AND NOT match_value($process.parent.name, $excluded_process_parents_name)", normalize: [ { map: [ - { 'event.kind': 'event' }, - { 'wazuh.decoders': 'array_append(syslog)' }, - { 'related.hosts': 'array_append($host.hostname)' }, - { 'process.name': 'rename($TAG)' }, - { 'host.ip': 'array_append($tmp.host_ip)' }, + { 'event.risk_score': '47.0' }, + { + 'rule.description': + 'Detects System Information Discovery commands.', + }, + { 'rule.license': 'Wazuh Inc.' }, + { 'rule.name': 'Enumeration of Kernel Modules' }, + { 'threat.framework': 'MITRE ATT&CK' }, + { 'threat.technique.id': ['T1082'] }, + { 'threat.technique.name': ['System Information Discovery'] }, + { + 'threat.technique.reference': [ + 'https://attack.mitre.org/techniques/T1082/', + ], + }, + { 'threat.tactic.id': ['TA0005'] }, + { 'threat.tactic.name': ['Discovery'] }, + { + 'threat.tactic.reference': [ + 'https://attack.mitre.org/tactics/TA0005/', + ], + }, + { + 'wazuh.rules': 'array_append(discovery_kernel_module_enumeration)', + }, + { 'vulnerability.severity': 'medium' }, ], }, ], }, { id: 2, - name: 'decoder/syslog/1', + name: 'decoder/system-auth/0', provider: 'native', status: 'disable', metadata: { - module: 'syslog2', - title: 'Syslog Decoder event', - description: 'Syslog header', - versions: ['4.3', '4.4'], - compatibility: 'This decoder has been tested on Wazuh version 4.3', + module: 'system', + dataset: 'system-auth', + title: 'system-auth logs', + description: 'Decoder for system athenticated action logs.', + compatibility: + 'This integration was tested with logs from OS like Ubuntu 20.04, Centos 7, and macOS Sierra.', + versions: ['any'], author: { - name: 'Wazuh, Inc.', - url: 'https://wazuh.com', - date: '2022/11/08', + name: 'Wazuh Inc.', + email: 'info@wazuh.com', + date: '2023/05/15', }, references: [ - 'https://www.ietf.org/rfc/rfc3164.txt', - 'https://www.ietf.org/rfc/rfc5424.txt', + 'https://www.loggly.com/ultimate-guide/linux-logging-basics/', ], }, - 'parse|event.original': [ - ' []:<~/ignore/ >', - ' :<~/ignore/ >', - - ' []: ', - - ' : ', - ' ', - - ' []:<~/ignore/ >', - ], + parents: ['decoder/syslog/0'], + definitions: { + isAuthProcess: + '$process.name == sshd OR $process.name == sudo OR $process.name == groupadd OR $process.name == useradd OR $process.name == groupdel OR $process.name == groupmod OR $process.name == userdel OR $process.name == usermod OR $process.name == CRON', + }, + check: '$isAuthProcess', normalize: [ { map: [ + { 'event.dataset': 'system-auth' }, { 'event.kind': 'event' }, - { 'wazuh.decoders': 'array_append(syslog)' }, - { 'related.hosts': 'array_append($host.hostname)' }, - { 'process.name': 'rename($TAG)' }, - { 'host.ip': 'array_append($tmp.host_ip)' }, + { 'event.module': 'system' }, + { 'event.outcome': 'success' }, + { 'wazuh.decoders': 'array_append(system-auth)' }, + ], + }, + { + check: [{ 'process.name': 'sshd' }], + 'parse|message': [ + '<_system.auth.ssh.event> <_system.auth.ssh.method> for from port ssh2(?:<~>)', + '<_system.auth.ssh.event> user from (? port )', + 'Did not receive identification string from ', + 'subsystem request for <_system.auth.ssh.subsystem> by user ', + '<_system.auth.ssh.session.action>: Too many authentication <_system.auth.ssh.event> for [preauth]', + ' [<~>][<~>]: <_system.auth.ssh.event>: <_system.auth.ssh.session.process_id> tty<~/literal//>?<~/literal/s><_system.process.tty.char_device.major>', + '<_system.auth.ssh.event>: Read from socket failed: Connection reset by peer [preauth]', + 'Received <_system.auth.ssh.event> from : <~>: [<~>]', + ], + }, + { + check: + '$_system.auth.ssh.event == Accepted OR $_system.auth.ssh.event == USER_PROCESS', + map: [ + { 'event.action': 'ssh_login' }, + { 'event.category': 'array_append(authentication, session)' }, + { 'event.outcome': 'success' }, + { 'event.type': 'array_append(info)' }, + ], + }, + { + check: + '$_system.auth.ssh.event == DEAD_PROCESS OR $_system.auth.ssh.event == disconnect', + map: [ + { 'event.action': 'ssh_login' }, + { 'event.category': 'array_append(authentication, session)' }, + { 'event.outcome': 'success' }, + { 'event.type': 'array_append(end)' }, + ], + }, + { + check: + '$_system.auth.ssh.event == Invalid OR $_system.auth.ssh.event == Failed OR $_system.auth.ssh.event == failures OR $_system.auth.ssh.event == fatal', + map: [ + { 'event.action': 'ssh_login' }, + { 'event.category': 'array_append(authentication)' }, + { 'event.outcome': 'failure' }, + { 'event.type': 'array_append(info)' }, + ], + }, + { + check: [{ 'process.name': 'sudo' }], + 'parse|message': [ + String.raw` : <_system.auth.sudo.error> ; TTY=tty<~/literal/\/>?<~/literal/s><_system.process.tty.char_device.major> ; PWD=<_system.auth.sudo.pwd> ; USER= ; COMMAND=<_system.auth.sudo.command>`, + ' : <_system.auth.sudo.error> ; TTY=<_system.auth.sudo.tty> ; PWD=<_system.auth.sudo.pwd> ; USER= ; COMMAND=<_system.auth.sudo.command>', + ' : TTY=<_system.auth.sudo.tty> ; PWD=<_system.auth.sudo.pwd> ; USER= ; COMMAND=<_system.auth.sudo.command>', + String.raw` : TTY=tty<~/literal/\/>?<~/literal/s><_system.process.tty.char_device.major> ; PWD=<_system.auth.sudo.pwd> ; USER= ; COMMAND=<_system.auth.sudo.command>`, + ], + map: [{ 'event.category': 'array_append(process)' }], + }, + { + check: [{ message: 'contains("session opened")' }], + map: [{ 'event.action': 'logged-on' }], + }, + { + check: [{ message: 'contains("session closed")' }], + map: [{ 'event.action': 'logged-off' }], + }, + { + check: [{ message: 'contains(pam_)' }], + 'parse|message': [ + String.raw`pam_unix\(<~>:<~>\): authentication <_system.auth.pam.session.action>; logname= uid= euid= tty= ruser= rhost= user=`, + ], + map: [{ 'event.category': 'array_append(authentication)' }], + }, + { + check: [{ message: 'contains(pam_)' }], + 'parse|message': [ + String.raw`pam_unix\(<~>:<~>\): session <_system.auth.pam.session.action> for user <_system.auth.pam.foruser.name> by <_system.auth.pam.byuser.name>\(uid=\)`, + String.raw`pam_unix\(<~>:<~>\): session <_system.auth.pam.session.action> for user <_system.auth.pam.foruser.name> by \(uid=\)`, + String.raw`pam_unix\(<~>:<~>\): session <_system.auth.pam.session.action> for user <_system.auth.pam.foruser.name>`, + ], + map: [{ 'event.category': 'array_append(session)' }], + }, + { + check: [{ message: 'contains(pam_succeed_if)' }], + 'parse|message': [ + String.raw`pam_succeed_if\(<~>:<~>\): requirement <~> not met by user `, + ], + map: [{ 'event.outcome': 'failure' }], + }, + { + check: [{ message: 'contains(PAM)' }], + 'parse|message': [ + 'PAM <~> more authentication <_system.auth.pam.session.action>; logname= uid= euid= tty= ruser= rhost= user=', + ], + map: [{ 'event.category': 'array_append(authentication, session)' }], + }, + { + check: [{ message: 'contains(PAM)' }], + 'parse|message': [ + 'error: PAM: <~> authentication <~> user from ', ], }, + { + check: [{ '_system.auth.pam.byuser.name': "string_not_equal('')" }], + map: [ + { 'user.name': '$_system.auth.pam.byuser.name' }, + { 'user.effective.name': '$_system.auth.pam.foruser.name' }, + ], + }, + { + check: [{ '_system.auth.pam.byuser.name': 'not_exists()' }], + map: [{ 'user.name': '$_system.auth.pam.foruser.name' }], + }, + { + check: '$_system.auth.pam.session.action == closed', + }, ], }, { id: 3, - name: 'decoder/syslog/2', + name: 'decoder/apache-access/0', provider: 'native', status: 'disable', metadata: { - module: 'syslog3', - title: 'Syslog Decoder event', - description: 'Syslog header', - compatibility: 'This decoder has been tested on Wazuh version 4.3', + module: 'apache-http', + title: 'Apache HTTP Server access logs decoder', + description: 'Decoder for Apache HTTP Server access logs.', + versions: ['2.2.31', '2.4.16'], + compatibility: + 'The Apache datasets were tested with Apache 2.4.12 and 2.4.46 and are expected to work with all versions >= 2.2.31 and >= 2.4.16 (independent from operating system).', author: { - name: 'Wazuh, Inc.', - url: 'https://wazuh.com', - date: '2022/11/08', + name: 'Wazuh Inc.', + date: '2023/11/29', }, references: [ - 'https://www.ietf.org/rfc/rfc3164.txt', - 'https://www.ietf.org/rfc/rfc5424.txt', + 'https://httpd.apache.orgPlease generate document codes/2.4/logs.html', ], }, + check: '$event.module == apache-access', 'parse|event.original': [ - ' []:<~/ignore/ >', - - ' :<~/ignore/ >', - - ' []: ', - - ' : ', - ' ', - - ' []:<~/ignore/ >', + ' - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" ""', + ' - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" ""', + ' - [] "<_http_request>" <_ignore/literal/->?(? "" "")', + ' - [] "-" -', + ' - - [] "-" -', + ' - - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" ""', + ' - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" ""', + '[] "<_http_request>" ?<~/literal/->', + '[] "<_http_request>" ?<~/literal/->', + ' - - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" "" "-"', + ' - [] "<~/literal/->?<_http_request>" ?<~/literal/-> "" "" X-Forwarded-For="<_forwarded_for>"', ], normalize: [ { map: [ + { 'event.category': 'array_append(web)' }, + { 'event.dataset': 'apache-access' }, { 'event.kind': 'event' }, - { 'wazuh.decoders': 'array_append(syslog)' }, - { 'related.hosts': 'array_append($host.hostname)' }, - { 'process.name': 'rename($TAG)' }, - { 'host.ip': 'array_append($tmp.host_ip)' }, + { 'event.module': 'apache-http' }, + { 'service.type': 'apache' }, + { 'wazuh.decoders': 'array_append(apache-access)' }, + { 'source.ip': '$source.address' }, + { _tls: "split($network.protocol, 'v')" }, + { _tls_1: '$_tls.1' }, + { _client_ip: "split($_forwarded_for, ',')" }, + { 'client.ip': '$_client_ip.0' }, + { 'network.forwarded_ip': '$_client_ip.0' }, + { 'tls.version_protocol': '$_tls.0' }, + { 'tls.cipher': '$tls.cipher' }, + ], + 'parse|_http_request': [ + ' HTTP/', + ], + }, + { + check: [{ _tls_1: String.raw`regex_match(\d+\.\d+)` }], + map: [{ 'tls.version': '$_tls_1' }], + }, + { + check: [{ _tls_1: String.raw`regex_not_match(\d+\.d+)` }], + map: [{ 'tls.version': "concat_any($_tls_1, '.0')" }], + }, + { + check: 'int_less($http.response.status_code, 400)', + map: [{ 'event.outcome': 'success' }], + }, + { + check: 'int_greater_or_equal($http.response.status_code, 400)', + map: [{ 'event.outcome': 'failure' }], + }, + { + check: [{ 'source.ip': 'not_exists()' }], + map: [{ 'source.domain': 'parse_fqdn($source.address)' }], + }, + { + map: [ + { + 'url.extension': String.raw`regex_extract($url.original, '.*\.([a-zA-Z0-9]+)(?:\?|$)')`, + }, + { 'url.path': '$url.original' }, + { 'url.query': String.raw`regex_extract($url.original, '\?(.*)')` }, + { 'url.domain': '$destination.domain' }, ], }, ], @@ -136,10 +321,11 @@ export const decoder = [ provider: 'native', status: 'disable', metadata: { - module: 'syslog4', + module: 'syslog', title: 'Syslog Decoder event', description: 'Syslog header', compatibility: 'This decoder has been tested on Wazuh version 4.3', + versions: ['4.3'], author: { name: 'Wazuh, Inc.', url: 'https://wazuh.com', @@ -150,16 +336,19 @@ export const decoder = [ 'https://www.ietf.org/rfc/rfc5424.txt', ], }, + check: '$event.module == syslog', 'parse|event.original': [ + // BSD Syslog RFC 3164 standard ' []:<~/ignore/ >', - + // BSD Syslog RFC 3164 no pid ' :<~/ignore/ >', - + // BSD Syslog RFC 3164 standard ISO8601 ' []: ', - + // BSD Syslog RFC 3164 no pid ISO8601 ' : ', + // RFC3164 example 2 section 5.4 ' ', - + // RFC3164 example 4 section 5.4 ' []:<~/ignore/ >', ], normalize: [ From 61fba37382751096eaad8fbc85d977d0bb637c44 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 24 Jan 2025 21:03:23 +0100 Subject: [PATCH 22/30] Update inputs renders --- .../components/common/render-inputs.tsx | 97 +++++++++++-------- .../public/components/rules/rule-details.tsx | 13 +-- 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx index 464d246a1c..780544d6fb 100644 --- a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx @@ -4,6 +4,7 @@ import { EuiFormRow, EuiFieldText, EuiComboBox, + EuiToolTip, } from '@elastic/eui'; const possibleSteps = new Set([ @@ -14,41 +15,76 @@ const possibleSteps = new Set([ 'output', 'definitions', ]); +const renderArrayTable = new Set(['check', 'parse|', 'normalize']); +const renderWithoutLabel = new Set(['check']); + +const inputString = (input: { key: string; value: any }) => { + if (renderWithoutLabel.has(input.key)) { + return ( + + + + ); + } + + return ( + + + + + + ); +}; const inputArray = (input: any) => { - let onTable = false; const inputs = input.value.map((item: any) => { if (!Number.isNaN(Number.parseInt(input.key))) { - onTable = true; - return item; } if (typeof item === 'string') { - onTable = true; - return { label: item, value: item }; } return Object.entries(item).map(([key, value]) => ({ key, value })); }); - const comboBoxInput = ( - - - - ); + display='columnCompressed' + > + + + ); const tableInput = ( { /> ); - return onTable ? tableInput : comboBoxInput; + return renderArrayTable.has(input.key) ? tableInput : comboBoxInput; }; -const inputString = (input: { key: string; value: any }) => ( - - - -); - const inputObject = (input: { key: string; value: any }) => { const inputsList = Object.entries(input.value).map(([key, value]) => ({ key: `${input.key}.${key}`, @@ -91,7 +110,7 @@ const inputObject = (input: { key: string; value: any }) => { }; export const renderInputs = (input: { key: string; value: any }) => { - if (possibleSteps.has(input.key)) { + if (possibleSteps.has(input.key) && typeof input.value !== 'string') { const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ key, value, diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index 8b6ebb3ea6..9ae6ff5507 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -90,12 +90,13 @@ export const RuleDetails = () => { } case stepName === possibleSteps.normalize: { - return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map( - (obj: any) => - Object.values( - obj.map.map((subObj: any) => Object.keys(subObj)[0]), - ).join(', '), - )}`; + return `${capitalizeFirstLetter(stepName)} fields: `; + // ${item.value.map( + // (obj: any) => + // Object.values( + // obj.map.map((subObj: any) => Object.keys(subObj)[0]), + // ).join(', '), + // )}`; } case stepName === possibleSteps.allow: { From c9f7e559a8d1fcd7d10b39088563dee3210f9969 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 27 Jan 2025 20:24:29 +0100 Subject: [PATCH 23/30] Update inputs details rule view --- .../public/components/common/constants.ts | 9 ++ .../components/common/render-inputs.tsx | 127 ------------------ .../components/common/render-steps.scss | 3 + .../public/components/common/render-steps.tsx | 89 ++++++++++++ .../public/components/rules/rule-details.scss | 3 + .../public/components/rules/rule-details.tsx | 86 ++---------- .../components/utils/inputs/array-inputs.tsx | 87 ++++++++++++ .../components/utils/inputs/object-inputs.tsx | 10 ++ .../components/utils/inputs/render-inputs.tsx | 29 ++++ .../components/utils/inputs/string-inputs.tsx | 21 +++ 10 files changed, 259 insertions(+), 205 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/constants.ts delete mode 100644 plugins/wazuh-security-policies/public/components/common/render-inputs.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/render-steps.scss create mode 100644 plugins/wazuh-security-policies/public/components/common/render-steps.tsx create mode 100644 plugins/wazuh-security-policies/public/components/rules/rule-details.scss create mode 100644 plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx create mode 100644 plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx create mode 100644 plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx create mode 100644 plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/constants.ts b/plugins/wazuh-security-policies/public/components/common/constants.ts new file mode 100644 index 0000000000..5a6e288d50 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/constants.ts @@ -0,0 +1,9 @@ +export const STEPS = { + metadata: 'metadata', + check: 'check', + parse: 'parse|', + normalize: 'normalize', + allow: 'allow', + output: 'output', + definitions: 'definitions', +}; diff --git a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx deleted file mode 100644 index 780544d6fb..0000000000 --- a/plugins/wazuh-security-policies/public/components/common/render-inputs.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import React from 'react'; -import { - EuiBasicTable, - EuiFormRow, - EuiFieldText, - EuiComboBox, - EuiToolTip, -} from '@elastic/eui'; - -const possibleSteps = new Set([ - 'metadata', - 'check', - 'normalize', - 'allow', - 'output', - 'definitions', -]); -const renderArrayTable = new Set(['check', 'parse|', 'normalize']); -const renderWithoutLabel = new Set(['check']); - -const inputString = (input: { key: string; value: any }) => { - if (renderWithoutLabel.has(input.key)) { - return ( - - - - ); - } - - return ( - - - - - - ); -}; - -const inputArray = (input: any) => { - const inputs = input.value.map((item: any) => { - if (!Number.isNaN(Number.parseInt(input.key))) { - return item; - } - - if (typeof item === 'string') { - return { label: item, value: item }; - } - - return Object.entries(item).map(([key, value]) => ({ key, value })); - }); - const comboBoxInput = - inputs.length === 1 && inputs[0].value === '' ? ( - inputString({ key: input.key, value: inputs[0].value }) - ) : ( - - - - ); - const tableInput = ( - - ); - - return renderArrayTable.has(input.key) ? tableInput : comboBoxInput; -}; - -const inputObject = (input: { key: string; value: any }) => { - const inputsList = Object.entries(input.value).map(([key, value]) => ({ - key: `${input.key}.${key}`, - value, - })); - - return inputsList.map(item => inputString(item)); -}; - -export const renderInputs = (input: { key: string; value: any }) => { - if (possibleSteps.has(input.key) && typeof input.value !== 'string') { - const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ - key, - value, - })); - - return inputsSteps.map(step => renderInputs(step)); - } - - if (typeof input.value === 'string') { - return inputString(input); - } - - return Array.isArray(input.value) ? inputArray(input) : inputObject(input); -}; diff --git a/plugins/wazuh-security-policies/public/components/common/render-steps.scss b/plugins/wazuh-security-policies/public/components/common/render-steps.scss new file mode 100644 index 0000000000..2a091c664a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/render-steps.scss @@ -0,0 +1,3 @@ +.multiple-panels-step:not(:last-child) { + margin-bottom: 10px; +} diff --git a/plugins/wazuh-security-policies/public/components/common/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/render-steps.tsx new file mode 100644 index 0000000000..5847d1e520 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/render-steps.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { + EuiPanel, + EuiAccordion, + EuiHorizontalRule, + EuiForm, +} from '@elastic/eui'; +import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; +import { renderInputs } from '../utils/inputs/render-inputs'; +import { STEPS } from './constants'; +import './render-steps.scss'; + +export const renderStepPanel = step => { + const isArrayOfObjects = + Array.isArray(step.value) && typeof step.value[0] === 'object'; + + const renderCardTitle = (stepName: string, item: any) => { + switch (true) { + case stepName === STEPS.metadata: { + return `${capitalizeFirstLetter(stepName)} of ${item.value.title}, from ${item.value.module}`; + } + + case stepName === STEPS.check: { + if (typeof item.value === 'string') { + return `${capitalizeFirstLetter(stepName)}: ${item.value}`; + } + + return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map((obj: any) => Object.keys(obj)[0]).join(', ')}`; + } + + case stepName.startsWith(STEPS.parse): { + return stepName.split('|')[1]; + } + + case stepName === STEPS.normalize: { + // Item is the only step in this case as you can have several normalize steps. + return `${capitalizeFirstLetter(stepName)} fields: + ${Object.keys(item).join(', ')}`; + } + + case stepName === STEPS.allow: { + return capitalizeFirstLetter(stepName); + } + + case stepName === STEPS.output: { + return capitalizeFirstLetter(stepName); + } + + case stepName === STEPS.definitions: { + return capitalizeFirstLetter(stepName); + } + + default: { + return capitalizeFirstLetter(stepName); + } + } + }; + + if (isArrayOfObjects) { + return step.value.map((item, index) => ( + + + + + {renderInputs({ key: step.key, value: item })} + + + + )); + } + + return ( + + + + + {renderInputs(step)} + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.scss b/plugins/wazuh-security-policies/public/components/rules/rule-details.scss new file mode 100644 index 0000000000..5377dfda66 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.scss @@ -0,0 +1,3 @@ +.steps-details { + margin-top: 10px; +} diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index 9ae6ff5507..6b53b1e960 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -6,25 +6,13 @@ import { EuiText, EuiButtonIcon, EuiSteps, - EuiPanel, - EuiAccordion, - EuiHorizontalRule, - EuiForm, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { renderInputs } from '../common/render-inputs'; +import { renderStepPanel } from '../common/render-steps'; import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; +import { STEPS } from '../common/constants'; import { decoder } from './mock-data-rules'; - -const possibleSteps = { - metadata: 'metadata', - check: 'check', - parse: 'parse|', - normalize: 'normalize', - allow: 'allow', - output: 'output', - definitions: 'definitions', -}; +import './rule-details.scss'; export const RuleDetails = () => { const { id: name } = useParams(); @@ -46,7 +34,7 @@ export const RuleDetails = () => { , {}} > @@ -71,56 +59,10 @@ export const RuleDetails = () => { , ]; - const renderCardTitle = (stepName: string, item: any) => { - switch (true) { - case stepName === possibleSteps.metadata: { - return `${capitalizeFirstLetter(stepName)} of ${item.value.title}, from ${item.value.module}`; - } - - case stepName === possibleSteps.check: { - if (typeof item.value === 'string') { - return `${capitalizeFirstLetter(stepName)}: ${item.value}`; - } - - return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map((obj: any) => Object.keys(obj)[0]).join(', ')}`; - } - - case stepName.startsWith(possibleSteps.parse): { - return stepName.split('|')[1]; - } - - case stepName === possibleSteps.normalize: { - return `${capitalizeFirstLetter(stepName)} fields: `; - // ${item.value.map( - // (obj: any) => - // Object.values( - // obj.map.map((subObj: any) => Object.keys(subObj)[0]), - // ).join(', '), - // )}`; - } - - case stepName === possibleSteps.allow: { - return capitalizeFirstLetter(stepName); - } - - case stepName === possibleSteps.output: { - return capitalizeFirstLetter(stepName); - } - - case stepName === possibleSteps.definitions: { - return capitalizeFirstLetter(stepName); - } - - default: { - return capitalizeFirstLetter(stepName); - } - } - }; - const renderTitleStep = (stepName: string) => { let title = stepName; - if (stepName.startsWith(possibleSteps.parse)) { + if (stepName.startsWith(STEPS.parse)) { title = stepName.split('|')[0]; } @@ -137,19 +79,7 @@ export const RuleDetails = () => { })); const steps = arraySteps.map(step => ({ title: renderTitleStep(step.key), - children: ( - - - - - {renderInputs(step)} - - - ), + children: renderStepPanel(step), })); return steps; @@ -159,14 +89,14 @@ export const RuleDetails = () => { <> - + ); }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx new file mode 100644 index 0000000000..b8c6ce2930 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { EuiBasicTable, EuiFormRow, EuiComboBox } from '@elastic/eui'; +import { inputString } from './string-inputs'; + +export const inputArray = (input: { key: string; value: any }) => { + const renderArrayTable = ['check', 'parse|', 'normalize']; + const isArrayOfObjects = + Array.isArray(input.value) && + input.value.length > 0 && + !Array.isArray(input.value[0]) && + typeof input.value[0] === 'object'; + const inputs = input.value.map((item: any) => { + if (!Number.isNaN(Number.parseInt(input.key))) { + return item; + } + + if (typeof item === 'string') { + return { + label: Array.isArray(item) ? item.join(', ') : item, + value: Array.isArray(item) ? item.join(', ') : item, + }; + } + + return { + key: Object.keys(item)[0], + value: Array.isArray(Object.values(item)[0]) + ? Object.values(item)[0].join(', ') + : Object.values(item)[0], + }; + }); + + if (isArrayOfObjects) { + return ( + + ); + } + + const comboBoxInput = + inputs.length === 1 && inputs[0].value === '' ? ( + inputString({ key: input.key, value: inputs[0].value }) + ) : ( + + + + ); + const tableInput = ( + + ); + + return renderArrayTable.every(item => input.key.startsWith(item)) + ? tableInput + : comboBoxInput; +}; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx new file mode 100644 index 0000000000..4b888c6a01 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx @@ -0,0 +1,10 @@ +import { inputString } from './string-inputs'; + +export const inputObject = (input: { key: string; value: any }) => { + const inputsList = Object.entries(input.value).map(([key, value]) => ({ + key: `${input.key}.${key}`, + value, + })); + + return inputsList.map(item => inputString(item)); +}; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx new file mode 100644 index 0000000000..5bde1cb294 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx @@ -0,0 +1,29 @@ +import { inputString } from './string-inputs'; +import { inputArray } from './array-inputs'; +import { inputObject } from './object-inputs'; + +const possibleSteps = new Set([ + 'metadata', + 'check', + 'normalize', + 'allow', + 'output', + 'definitions', +]); + +export const renderInputs = (input: { key: string; value: any }) => { + if (possibleSteps.has(input.key) && typeof input.value !== 'string') { + const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ + key, + value, + })); + + return inputsSteps.map(step => renderInputs(step)); + } + + if (typeof input.value === 'string') { + return inputString(input); + } + + return Array.isArray(input.value) ? inputArray(input) : inputObject(input); +}; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx new file mode 100644 index 0000000000..6ec5552034 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { EuiFormRow, EuiFieldText, EuiToolTip } from '@elastic/eui'; + +export const inputString = (input: { key: string; value: any }) => ( + + + + + +); From 3926f30175ab8fc675f9368f9b80998cbf39ce74 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:50:10 +0100 Subject: [PATCH 24/30] Add details view to decoders and kvdb --- .../public/components/app.tsx | 1 + .../public/components/common/common.scss | 12 ++ .../details-template.scss} | 0 .../components/common/details-template.tsx | 104 ++++++++++++++++++ .../components/common/overview-template.tsx | 36 +++--- .../components/decoders/decoder-details.tsx | 7 +- .../public/components/decoders/overview.tsx | 6 +- .../public/components/kvdb/kvdb-details.tsx | 7 +- .../public/components/kvdb/overview.tsx | 6 +- .../public/components/rules/overview.tsx | 6 +- .../public/components/rules/rule-details.tsx | 102 +---------------- .../components/utils/inputs/array-inputs.tsx | 2 + 12 files changed, 146 insertions(+), 143 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/common.scss rename plugins/wazuh-security-policies/public/components/{rules/rule-details.scss => common/details-template.scss} (100%) create mode 100644 plugins/wazuh-security-policies/public/components/common/details-template.tsx diff --git a/plugins/wazuh-security-policies/public/components/app.tsx b/plugins/wazuh-security-policies/public/components/app.tsx index 7ef83437e4..a0f409f9cc 100644 --- a/plugins/wazuh-security-policies/public/components/app.tsx +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -10,6 +10,7 @@ import { import { Router, Route, Switch, Redirect, useParams } from 'react-router-dom'; import { getCore, getHistory } from '../plugin-services'; import { views } from './common/views'; +import '../components/common/common.scss'; export const WazuhSecurityPoliciesApp = () => { const history = getHistory(); diff --git a/plugins/wazuh-security-policies/public/components/common/common.scss b/plugins/wazuh-security-policies/public/components/common/common.scss new file mode 100644 index 0000000000..0b4f8c37fe --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/common.scss @@ -0,0 +1,12 @@ +.wz-mtb-10 { + margin-top: 10px; + margin-bottom: 10px; +} + +.wz-mr-10 { + margin-right: '10px'; +} + +.wz-capitalize { + text-transform: capitalize; +} diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.scss b/plugins/wazuh-security-policies/public/components/common/details-template.scss similarity index 100% rename from plugins/wazuh-security-policies/public/components/rules/rule-details.scss rename to plugins/wazuh-security-policies/public/components/common/details-template.scss diff --git a/plugins/wazuh-security-policies/public/components/common/details-template.tsx b/plugins/wazuh-security-policies/public/components/common/details-template.tsx new file mode 100644 index 0000000000..3b54c7eb6a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/details-template.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { + EuiPageHeader, + EuiButton, + EuiButtonEmpty, + EuiText, + EuiButtonIcon, + EuiSteps, +} from '@elastic/eui'; +import { useParams } from 'react-router-dom'; +import { decoder } from '../rules/mock-data-rules'; +import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; +import { renderStepPanel } from './render-steps'; +import { STEPS } from './constants'; +import './details-template.scss'; + +export const DetailsTemplate = () => { + // const { pathname } = useLocation(); + // const view = pathname.split('/')[1]; + const { id: name } = useParams(); + const item = decoder.find(item => item.name === decodeURIComponent(name)); + const title = ( +
+ {item?.name} + {}} + iconType='pencil' + aria-label='Edit' + /> +
+ ); + const buttons = [ + + Edit + , + {}} + > + Test decoder + , + {}} + className='wz-capitalize' + > + {item?.status} + , + {}} + > + View in XML + , + ]; + + const renderTitleStep = (stepName: string) => { + let title = stepName; + + if (stepName.startsWith(STEPS.parse)) { + title = stepName.split('|')[0]; + } + + return capitalizeFirstLetter(title); + }; + + const step = (item: any) => { + const removeEntries = new Set(['id', 'name', 'provider', 'status']); + const arraySteps = Object.entries(item) + .filter(([key]) => !removeEntries.has(key)) + .map(([key, value]) => ({ + key, + value, + })); + const steps = arraySteps.map(step => ({ + title: renderTitleStep(step.key), + children: renderStepPanel(step), + })); + + return steps; + }; + + return ( + <> + + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx index 4c9e06a725..71e71dbe32 100644 --- a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/overview-template.tsx @@ -6,26 +6,25 @@ import { EuiBasicTable, EuiHealth, EuiPageHeader, + EuiLink, } from '@elastic/eui'; +import { useLocation } from 'react-router-dom'; import { decoder } from '../rules/mock-data-rules'; import { getHistory } from '../../plugin-services'; import { LastUpdateContentManagerText } from './last-update-content-manager-text.tsx'; import { SearchBar } from './searchbar'; import { NoResultsData } from './no-results'; -interface OverviewTemplateProps { - view: 'rules' | 'decoders' | 'kvdb'; -} - -export const OverviewTemplate = (props: OverviewTemplateProps) => { - const { view } = props; +export const OverviewTemplate = () => { + const { pathname } = useLocation(); + const view = pathname.split('/')[1]; const history = getHistory(); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(5); const [decoderList, setDecoderList] = useState(decoder); const [query, setQuery] = useState({ text: '' }); // Header start - const titleHeader =

{view}

; + const titleHeader = {view}; const descriptionHeader = LastUpdateContentManagerText({ status: 'Updated', lastUpdateDate: '31/01/2025', @@ -144,12 +143,23 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { return {label}; }; + const handleNavigation = (path: string) => { + history.push(path); + }; + + const getRowProps = (item: string) => + handleNavigation(`/${view}/${encodeURIComponent(item)}`); const columns = [ { field: 'name', name: 'Name', sortable: true, truncateText: true, + render: (item: string) => ( + getRowProps(item)} className='wz-capitalize'> + {item} + + ), }, { field: 'metadata.module', @@ -168,7 +178,7 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { sortable: true, truncateText: true, render: (item: string) => ( - {item} + {item} ), }, { @@ -238,15 +248,6 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { pageSizeOptions: [3, 5, 8], }; - const handleNavigation = (path: string) => { - history.push(path); - }; - - const getRowProps = item => ({ - onClick: () => - handleNavigation(`/${view}/${encodeURIComponent(item.name)}`), - }); - // Table end return ( @@ -263,7 +264,6 @@ export const OverviewTemplate = (props: OverviewTemplateProps) => { pagination={pagination} isSelectable={true} onChange={onTableChange} - rowProps={getRowProps} /> {decoder.length <= 0 && }
diff --git a/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx b/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx index a93a11c531..8770bcfc99 100644 --- a/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx +++ b/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx @@ -1,7 +1,4 @@ import React from 'react'; +import { DetailsTemplate } from '../common/details-template'; -export const DecoderDetails = () => ( - <> -

DecoderDetails

- -); +export const DecoderDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/decoders/overview.tsx b/plugins/wazuh-security-policies/public/components/decoders/overview.tsx index d4b1e8bf97..21ce8d2e86 100644 --- a/plugins/wazuh-security-policies/public/components/decoders/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/decoders/overview.tsx @@ -1,8 +1,4 @@ import React from 'react'; import { OverviewTemplate } from '../common/overview-template'; -export const DecodersOverview = () => { - const view = 'decoders'; - - return ; -}; +export const DecodersOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx b/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx index a0660a9172..9949909c76 100644 --- a/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx +++ b/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx @@ -1,7 +1,4 @@ import React from 'react'; +import { DetailsTemplate } from '../common/details-template'; -export const KVDBDetails = () => ( - <> -

KVDBDetails

- -); +export const KVDBDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx b/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx index 16dec6c83e..e8a30f7785 100644 --- a/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx @@ -1,8 +1,4 @@ import React from 'react'; import { OverviewTemplate } from '../common/overview-template'; -export const KVDBOverview = () => { - const view = 'kvdb'; - - return ; -}; +export const KVDBOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/rules/overview.tsx b/plugins/wazuh-security-policies/public/components/rules/overview.tsx index d8e82b2732..2df514d2dc 100644 --- a/plugins/wazuh-security-policies/public/components/rules/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/overview.tsx @@ -1,8 +1,4 @@ import React from 'react'; import { OverviewTemplate } from '../common/overview-template'; -export const RulesOverview = () => { - const view = 'rules'; - - return ; -}; +export const RulesOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx index 6b53b1e960..adf5039b0b 100644 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx @@ -1,102 +1,4 @@ import React from 'react'; -import { - EuiPageHeader, - EuiButton, - EuiButtonEmpty, - EuiText, - EuiButtonIcon, - EuiSteps, -} from '@elastic/eui'; -import { useParams } from 'react-router-dom'; -import { renderStepPanel } from '../common/render-steps'; -import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; -import { STEPS } from '../common/constants'; -import { decoder } from './mock-data-rules'; -import './rule-details.scss'; +import { DetailsTemplate } from '../common/details-template'; -export const RuleDetails = () => { - const { id: name } = useParams(); - const item = decoder.find(item => item.name === decodeURIComponent(name)); - const title = ( -
- {item?.name} - {}} - iconType='pencil' - aria-label='Edit' - /> -
- ); - const buttons = [ - - Edit - , - {}} - > - Test decoder - , - {}} - style={{ textTransform: 'capitalize' }} - > - {item?.status} - , - {}} - > - View in XML - , - ]; - - const renderTitleStep = (stepName: string) => { - let title = stepName; - - if (stepName.startsWith(STEPS.parse)) { - title = stepName.split('|')[0]; - } - - return capitalizeFirstLetter(title); - }; - - const step = (item: any) => { - const removeEntries = new Set(['id', 'name', 'provider', 'status']); - const arraySteps = Object.entries(item) - .filter(([key]) => !removeEntries.has(key)) - .map(([key, value]) => ({ - key, - value, - })); - const steps = arraySteps.map(step => ({ - title: renderTitleStep(step.key), - children: renderStepPanel(step), - })); - - return steps; - }; - - return ( - <> - - - - ); -}; +export const RuleDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx index b8c6ce2930..e26f7efc29 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -33,6 +33,7 @@ export const inputArray = (input: { key: string; value: any }) => { return ( { ); const tableInput = ( Date: Tue, 28 Jan 2025 22:24:03 +0100 Subject: [PATCH 25/30] Changes and add create template --- .../last-update-content-manager-text.tsx.tsx | 0 .../common/{ => components}/popover.tsx | 0 .../common/{ => components}/render-steps.scss | 0 .../common/{ => components}/render-steps.tsx | 6 +- .../common/{ => components}/searchbar.tsx | 0 .../common/templates/create-template.tsx | 182 ++++++++++++++++++ .../{ => templates}/details-template.scss | 0 .../{ => templates}/details-template.tsx | 8 +- .../common/{ => templates}/no-results.tsx | 0 .../{ => templates}/overview-template.tsx | 20 +- .../public/components/common/views.tsx | 53 +++-- .../components/decoders/decoder-details.tsx | 4 - .../public/components/decoders/overview.tsx | 4 - .../components/card-integration.tsx | 2 +- .../integretions/integration-details.tsx | 2 +- .../components/integretions/overview.tsx | 6 +- .../public/components/kvdb/kvdb-details.tsx | 4 - .../public/components/kvdb/overview.tsx | 4 - .../public/components/rules/overview.tsx | 4 - .../public/components/rules/rule-details.tsx | 4 - .../rules/schemas/metadata.schema.ts | 50 +++++ .../public/components/utils/get-app-url.ts | 3 + .../components/utils/inputs/array-inputs.tsx | 19 +- .../components/utils/inputs/object-inputs.tsx | 7 +- .../components/utils/inputs/render-inputs.tsx | 11 +- .../components/utils/inputs/string-inputs.tsx | 13 +- 26 files changed, 336 insertions(+), 70 deletions(-) rename plugins/wazuh-security-policies/public/components/common/{ => components}/last-update-content-manager-text.tsx.tsx (100%) rename plugins/wazuh-security-policies/public/components/common/{ => components}/popover.tsx (100%) rename plugins/wazuh-security-policies/public/components/common/{ => components}/render-steps.scss (100%) rename plugins/wazuh-security-policies/public/components/common/{ => components}/render-steps.tsx (92%) rename plugins/wazuh-security-policies/public/components/common/{ => components}/searchbar.tsx (100%) create mode 100644 plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx rename plugins/wazuh-security-policies/public/components/common/{ => templates}/details-template.scss (100%) rename plugins/wazuh-security-policies/public/components/common/{ => templates}/details-template.tsx (90%) rename plugins/wazuh-security-policies/public/components/common/{ => templates}/no-results.tsx (100%) rename plugins/wazuh-security-policies/public/components/common/{ => templates}/overview-template.tsx (92%) delete mode 100644 plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx delete mode 100644 plugins/wazuh-security-policies/public/components/decoders/overview.tsx delete mode 100644 plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx delete mode 100644 plugins/wazuh-security-policies/public/components/kvdb/overview.tsx delete mode 100644 plugins/wazuh-security-policies/public/components/rules/overview.tsx delete mode 100644 plugins/wazuh-security-policies/public/components/rules/rule-details.tsx create mode 100644 plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts create mode 100644 plugins/wazuh-security-policies/public/components/utils/get-app-url.ts diff --git a/plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx b/plugins/wazuh-security-policies/public/components/common/components/last-update-content-manager-text.tsx.tsx similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/last-update-content-manager-text.tsx.tsx rename to plugins/wazuh-security-policies/public/components/common/components/last-update-content-manager-text.tsx.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/popover.tsx b/plugins/wazuh-security-policies/public/components/common/components/popover.tsx similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/popover.tsx rename to plugins/wazuh-security-policies/public/components/common/components/popover.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/render-steps.scss b/plugins/wazuh-security-policies/public/components/common/components/render-steps.scss similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/render-steps.scss rename to plugins/wazuh-security-policies/public/components/common/components/render-steps.scss diff --git a/plugins/wazuh-security-policies/public/components/common/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx similarity index 92% rename from plugins/wazuh-security-policies/public/components/common/render-steps.tsx rename to plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx index 5847d1e520..57a3a87312 100644 --- a/plugins/wazuh-security-policies/public/components/common/render-steps.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -5,9 +5,9 @@ import { EuiHorizontalRule, EuiForm, } from '@elastic/eui'; -import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; -import { renderInputs } from '../utils/inputs/render-inputs'; -import { STEPS } from './constants'; +import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; +import { renderInputs } from '../../utils/inputs/render-inputs'; +import { STEPS } from '../constants'; import './render-steps.scss'; export const renderStepPanel = step => { diff --git a/plugins/wazuh-security-policies/public/components/common/searchbar.tsx b/plugins/wazuh-security-policies/public/components/common/components/searchbar.tsx similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/searchbar.tsx rename to plugins/wazuh-security-policies/public/components/common/components/searchbar.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx new file mode 100644 index 0000000000..d4bb6c9eb2 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -0,0 +1,182 @@ +import React, { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { + EuiPageHeader, + EuiFieldText, + EuiButtonEmpty, + EuiButton, + EuiHorizontalRule, + EuiSteps, + EuiSelect, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import { PopoverIconButton } from '../components/popover'; +import { getAppUrl } from '../../utils/get-app-url'; +import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; +import { metadataInitialValues } from '../../rules/schemas/metadata.schema'; +import { renderStepPanel } from '../components/render-steps'; + +const getAvailableOptions = selectedSteps => { + const baseOptions = [ + { value: 'Select step', text: 'Select step' }, + { value: 'check', text: 'Check' }, + { value: 'parse', text: 'Parse' }, + { value: 'normalize', text: 'Normalize' }, + { value: 'allow', text: 'Allow' }, + { value: 'outputs', text: 'Outputs' }, + { value: 'definitions', text: 'Definitions' }, + ]; + + if ( + selectedSteps.includes('check') || + selectedSteps.includes('parse') || + selectedSteps.includes('normalize') + ) { + return baseOptions.filter( + option => !['allow', 'outputs', ...selectedSteps].includes(option.value), + ); + } + + if (selectedSteps.includes('outputs')) { + return baseOptions.filter( + option => + !['normalize', 'parse', ...selectedSteps].includes(option.value), + ); + } + + if (selectedSteps.includes('allow')) { + return baseOptions.filter( + option => + !['check', 'normalize', ...selectedSteps].includes(option.value), + ); + } + + return baseOptions; +}; + +export const CreateTemplate = () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [onXml, setOnXml] = useState(false); + const [stepsToRender, setstepsToRender] = useState(['metadata']); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [item, setItem] = useState(metadataInitialValues); + const [addStep, setAddStep] = useState( + getAvailableOptions(stepsToRender)?.[0]?.value, + ); + const view = getAppUrl(); + const buttonsPopover = [ + { + id: 'editOnFormOrXML', + label: onXml ? 'Edit on form' : 'Edit on XML', + color: 'text', + }, + { + id: 'enable/disable', + label: item?.enable ? 'Disable' : 'Enable', + color: item?.enable ? 'danger' : 'primary', + }, + { + id: 'testItem', + label: `Test ${view}`, + color: 'text', + }, + { + id: 'setSaveAsDraft', + label: useParams()?.id ? 'Save as Draft' : 'Set as draft', + color: 'text', + }, + ]; + const buttons = [ + +
+ {buttonsPopover.map((button, index) => ( + + + {button.label} + + {index < buttonsPopover.length - 1 && ( + + )} + + ))} +
+
, + {}}> + Save + , + + Cancel + , + ]; + + const addStepRender = () => { + setstepsToRender([...stepsToRender, addStep]); + }; + + const steps = stepsItems => { + const stepsArray = Object.entries(stepsItems) + .filter(([key]) => stepsToRender.includes(key)) + .map(([stepName, value]) => ({ + title: capitalizeFirstLetter(stepName), + children: renderStepPanel({ key: stepName, value }), + })); + const optionsToSelect = getAvailableOptions(stepsToRender); + + if (optionsToSelect.length > 1) { + stepsArray.push({ + title: 'Add step', + children: ( + + + setAddStep(event_.target.value)} + placeholder='Select next step' + /> + + + + Add step + + + + ), + }); + } + + return stepsArray; + }; + + return ( + <> + {}} + /> + } + bottomBorder={true} + alignItems='center' + rightSideGroupProps={{ + alignItems: 'center', + }} + rightSideItems={buttons} + /> + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/details-template.scss b/plugins/wazuh-security-policies/public/components/common/templates/details-template.scss similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/details-template.scss rename to plugins/wazuh-security-policies/public/components/common/templates/details-template.scss diff --git a/plugins/wazuh-security-policies/public/components/common/details-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx similarity index 90% rename from plugins/wazuh-security-policies/public/components/common/details-template.tsx rename to plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx index 3b54c7eb6a..3cb8c1df70 100644 --- a/plugins/wazuh-security-policies/public/components/common/details-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx @@ -8,10 +8,10 @@ import { EuiSteps, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { decoder } from '../rules/mock-data-rules'; -import { capitalizeFirstLetter } from '../utils/capitalize-first-letter'; -import { renderStepPanel } from './render-steps'; -import { STEPS } from './constants'; +import { decoder } from '../../rules/mock-data-rules'; +import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; +import { renderStepPanel } from '../components/render-steps'; +import { STEPS } from '../constants'; import './details-template.scss'; export const DetailsTemplate = () => { diff --git a/plugins/wazuh-security-policies/public/components/common/no-results.tsx b/plugins/wazuh-security-policies/public/components/common/templates/no-results.tsx similarity index 100% rename from plugins/wazuh-security-policies/public/components/common/no-results.tsx rename to plugins/wazuh-security-policies/public/components/common/templates/no-results.tsx diff --git a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx similarity index 92% rename from plugins/wazuh-security-policies/public/components/common/overview-template.tsx rename to plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx index 71e71dbe32..1c501ad082 100644 --- a/plugins/wazuh-security-policies/public/components/common/overview-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx @@ -8,16 +8,15 @@ import { EuiPageHeader, EuiLink, } from '@elastic/eui'; -import { useLocation } from 'react-router-dom'; -import { decoder } from '../rules/mock-data-rules'; -import { getHistory } from '../../plugin-services'; -import { LastUpdateContentManagerText } from './last-update-content-manager-text.tsx'; -import { SearchBar } from './searchbar'; +import { decoder } from '../../rules/mock-data-rules'; +import { getHistory } from '../../../plugin-services'; +import { getAppUrl } from '../../utils/get-app-url'; +import { LastUpdateContentManagerText } from '../components/last-update-content-manager-text.tsx'; +import { SearchBar } from '../components/searchbar'; import { NoResultsData } from './no-results'; export const OverviewTemplate = () => { - const { pathname } = useLocation(); - const view = pathname.split('/')[1]; + const view = getAppUrl(); const history = getHistory(); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(5); @@ -29,8 +28,13 @@ export const OverviewTemplate = () => { status: 'Updated', lastUpdateDate: '31/01/2025', }); + + const goCreateView = () => { + history.push(`/${view}/create`); + }; + const rightSideItems = [ - console.log('clicked')} fill> + Create , ]; diff --git a/plugins/wazuh-security-policies/public/components/common/views.tsx b/plugins/wazuh-security-policies/public/components/common/views.tsx index daa333ea6b..f7016d9ecd 100644 --- a/plugins/wazuh-security-policies/public/components/common/views.tsx +++ b/plugins/wazuh-security-policies/public/components/common/views.tsx @@ -3,13 +3,10 @@ import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; import { IntegrationOverview } from '../integretions/overview'; import { IntegrationView } from '../integretions/integration-details'; -import { RulesOverview } from '../rules/overview'; -import { RuleDetails } from '../rules/rule-details'; -import { DecodersOverview } from '../decoders/overview'; -import { DecoderDetails } from '../decoders/decoder-details'; -import { KVDBOverview } from '../kvdb/overview'; -import { KVDBDetails } from '../kvdb/kvdb-details'; import { getCore } from '../../plugin-services'; +import { OverviewTemplate } from './templates/overview-template'; +import { DetailsTemplate } from './templates/details-template'; +import { CreateTemplate } from './templates/create-template'; interface ViewInterface { name: string; @@ -82,7 +79,7 @@ export const views: ViewInterface[] = [ path: '/rules', renderOnMenu: true, renderMenu: true, - render: () => , + render: () => , breadcrumb: () => [ { className: 'osdBreadcrumbs', @@ -95,6 +92,38 @@ export const views: ViewInterface[] = [ }, ], }, + { + name: i18n.translate('wz-security-policies-rules-create', { + defaultMessage: 'Rules create', + }), + id: 'rulesCreate', + path: '/rules/create', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: () => [ + { + className: 'osdBreadcrumbs', + text: ( + + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'rules')?.path}`, + }), + }, + { + text: ( + + ), + }, + ], + }, { name: i18n.translate('wz-security-policies-rule-details', { defaultMessage: 'Rule details', @@ -103,7 +132,7 @@ export const views: ViewInterface[] = [ path: '/rules/:id', renderOnMenu: false, renderMenu: false, - render: () => , + render: () => , breadcrumb: (name?: string) => [ { text: ( @@ -130,7 +159,7 @@ export const views: ViewInterface[] = [ path: '/decoders', renderOnMenu: true, renderMenu: true, - render: () => , + render: () => , breadcrumb: () => [ { className: 'osdBreadcrumbs', @@ -151,7 +180,7 @@ export const views: ViewInterface[] = [ path: '/decoders/:id', renderOnMenu: false, renderMenu: false, - render: () => , + render: () => , breadcrumb: (name?: string) => [ { text: ( @@ -178,7 +207,7 @@ export const views: ViewInterface[] = [ path: '/kvdb', renderOnMenu: true, renderMenu: true, - render: () => , + render: () => , breadcrumb: () => [ { className: 'osdBreadcrumbs', @@ -199,7 +228,7 @@ export const views: ViewInterface[] = [ path: '/kvdb/:id', renderOnMenu: false, renderMenu: false, - render: () => , + render: () => , breadcrumb: (name?: string) => [ { text: ( diff --git a/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx b/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx deleted file mode 100644 index 8770bcfc99..0000000000 --- a/plugins/wazuh-security-policies/public/components/decoders/decoder-details.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { DetailsTemplate } from '../common/details-template'; - -export const DecoderDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/decoders/overview.tsx b/plugins/wazuh-security-policies/public/components/decoders/overview.tsx deleted file mode 100644 index 21ce8d2e86..0000000000 --- a/plugins/wazuh-security-policies/public/components/decoders/overview.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { OverviewTemplate } from '../common/overview-template'; - -export const DecodersOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx index 43bd2a043a..7935f91448 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx @@ -5,7 +5,7 @@ import { EuiButtonEmpty, EuiHorizontalRule, } from '@elastic/eui'; -import { PopoverIconButton } from '../../common/popover'; +import { PopoverIconButton } from '../../common/components/popover'; import { getHistory } from '../../../plugin-services'; interface CardIntegrationProps { diff --git a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx index 52c139ce1e..9cc1cb5ba2 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx @@ -8,7 +8,7 @@ import { EuiPageHeader, } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; +import { LastUpdateContentManagerText } from '../common/components/last-update-content-manager-text.tsx'; import { integrations } from './mock-data-integrations'; import './integrations.scss'; import { IntegrationDescription } from './components/integration-description'; diff --git a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx index 07b909524e..fb70c81bb3 100644 --- a/plugins/wazuh-security-policies/public/components/integretions/overview.tsx +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -8,9 +8,9 @@ import { EuiPageHeader, } from '@elastic/eui'; import './integrations.scss'; -import { SearchBar } from '../common/searchbar'; -import { LastUpdateContentManagerText } from '../common/last-update-content-manager-text.tsx'; -import { NoResultsData } from '../common/no-results'; +import { SearchBar } from '../common/components/searchbar'; +import { LastUpdateContentManagerText } from '../common/components/last-update-content-manager-text.tsx'; +import { NoResultsData } from '../common/templates/no-results'; import { CardIntegration } from './components/card-integration'; import { integrations } from './mock-data-integrations'; diff --git a/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx b/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx deleted file mode 100644 index 9949909c76..0000000000 --- a/plugins/wazuh-security-policies/public/components/kvdb/kvdb-details.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { DetailsTemplate } from '../common/details-template'; - -export const KVDBDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx b/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx deleted file mode 100644 index e8a30f7785..0000000000 --- a/plugins/wazuh-security-policies/public/components/kvdb/overview.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { OverviewTemplate } from '../common/overview-template'; - -export const KVDBOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/rules/overview.tsx b/plugins/wazuh-security-policies/public/components/rules/overview.tsx deleted file mode 100644 index 2df514d2dc..0000000000 --- a/plugins/wazuh-security-policies/public/components/rules/overview.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { OverviewTemplate } from '../common/overview-template'; - -export const RulesOverview = () => ; diff --git a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx b/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx deleted file mode 100644 index adf5039b0b..0000000000 --- a/plugins/wazuh-security-policies/public/components/rules/rule-details.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import { DetailsTemplate } from '../common/details-template'; - -export const RuleDetails = () => ; diff --git a/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts new file mode 100644 index 0000000000..aedd59917b --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts @@ -0,0 +1,50 @@ +export interface MetadataSchema { + enable: boolean; + name: string; + metadata: { + module: string; + title: string; + description: string; + compatibility: string; + versions: string[]; + author: { + name: string; + date: string; + email?: string; + }; + references: string[]; + }; + check?: string | Record[]; + normalize?: { + map: Record[]; + check?: string | Record[]; + parse?: string | string[]; + }[]; + parse?: string | string[]; + definitions?: string | string[]; + allow?: string; + outputs?: string; +} + +export const metadataInitialValues: MetadataSchema = { + enable: false, + name: '', + metadata: { + module: '', + title: '', + description: '', + compatibility: '', + versions: [''], + author: { + name: '', + date: new Date().toISOString().split('T')[0], + email: '', + }, + references: [''], + }, + check: '', + normalize: [], + definitions: '', + allow: '', + outputs: '', +}; diff --git a/plugins/wazuh-security-policies/public/components/utils/get-app-url.ts b/plugins/wazuh-security-policies/public/components/utils/get-app-url.ts new file mode 100644 index 0000000000..0414561f24 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/get-app-url.ts @@ -0,0 +1,3 @@ +import { useLocation } from 'react-router-dom'; + +export const getAppUrl = () => useLocation().pathname.split('/')[1]; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx index e26f7efc29..451c335b51 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -1,8 +1,12 @@ import React from 'react'; import { EuiBasicTable, EuiFormRow, EuiComboBox } from '@elastic/eui'; +import { capitalizeFirstLetter } from '../capitalize-first-letter'; import { inputString } from './string-inputs'; -export const inputArray = (input: { key: string; value: any }) => { +export const inputArray = ( + input: { key: string; value: any }, + isEditable: boolean, +) => { const renderArrayTable = ['check', 'parse|', 'normalize']; const isArrayOfObjects = Array.isArray(input.value) && @@ -50,12 +54,12 @@ export const inputArray = (input: { key: string; value: any }) => { } const comboBoxInput = - inputs.length === 1 && inputs[0].value === '' ? ( - inputString({ key: input.key, value: inputs[0].value }) + !isEditable && inputs.length === 1 && inputs[0].value === '' ? ( + inputString({ key: input.key, value: inputs[0].value }, isEditable) ) : ( @@ -63,9 +67,12 @@ export const inputArray = (input: { key: string; value: any }) => { label={input.key} fullWidth noSuggestions - selectedOptions={inputs} + placeholder={`${capitalizeFirstLetter(input.key)} value`} + selectedOptions={ + isEditable && inputs?.[0]?.value === '' ? [] : inputs + } compressed - isDisabled + isDisabled={!isEditable} /> ); diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx index 4b888c6a01..2e691a2bb0 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx @@ -1,10 +1,13 @@ import { inputString } from './string-inputs'; -export const inputObject = (input: { key: string; value: any }) => { +export const inputObject = ( + input: { key: string; value: any }, + isEditable: boolean, +) => { const inputsList = Object.entries(input.value).map(([key, value]) => ({ key: `${input.key}.${key}`, value, })); - return inputsList.map(item => inputString(item)); + return inputsList.map(item => inputString(item, isEditable)); }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx index 5bde1cb294..999ede12a3 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx @@ -1,3 +1,4 @@ +import { useLocation } from 'react-router-dom'; import { inputString } from './string-inputs'; import { inputArray } from './array-inputs'; import { inputObject } from './object-inputs'; @@ -12,6 +13,10 @@ const possibleSteps = new Set([ ]); export const renderInputs = (input: { key: string; value: any }) => { + const isEditable = + useLocation().pathname.includes('edit') || + useLocation().pathname.includes('create'); + if (possibleSteps.has(input.key) && typeof input.value !== 'string') { const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ key, @@ -22,8 +27,10 @@ export const renderInputs = (input: { key: string; value: any }) => { } if (typeof input.value === 'string') { - return inputString(input); + return inputString(input, isEditable); } - return Array.isArray(input.value) ? inputArray(input) : inputObject(input); + return Array.isArray(input.value) + ? inputArray(input, isEditable) + : inputObject(input, isEditable); }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx index 6ec5552034..5a05322636 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx @@ -1,20 +1,25 @@ import React from 'react'; import { EuiFormRow, EuiFieldText, EuiToolTip } from '@elastic/eui'; +import { capitalizeFirstLetter } from '../capitalize-first-letter'; -export const inputString = (input: { key: string; value: any }) => ( +export const inputString = ( + input: { key: string; value: any }, + isEditable: boolean, +) => ( From d7dcdd2c6132e386c7b8828864f0523289d62207 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 28 Jan 2025 22:28:46 +0100 Subject: [PATCH 26/30] Fix lint --- .../public/components/common/templates/create-template.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx index d4bb6c9eb2..7fad498947 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -136,7 +136,7 @@ export const CreateTemplate = () => { value={addStep} compressed fullWidth - onChange={event_ => setAddStep(event_.target.value)} + onChange={event => setAddStep(event.target.value)} placeholder='Select next step' /> From a034679e74fcfebfc44e12cdb39da654ebbd68fb Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:29:10 +0100 Subject: [PATCH 27/30] Update inputs form --- .../common/components/render-steps.tsx | 7 +- .../common/templates/create-template.tsx | 66 +++++++++++++++++-- .../public/components/common/views.tsx | 62 +++++++++++++++++ .../rules/schemas/metadata.schema.ts | 9 ++- .../components/utils/inputs/array-inputs.tsx | 38 ++++++++++- .../components/utils/inputs/object-inputs.tsx | 13 +++- .../components/utils/inputs/render-inputs.tsx | 16 ++++- .../components/utils/inputs/string-inputs.tsx | 56 ++++++++++------ .../utils/transform-input-key-name.ts | 4 ++ 9 files changed, 235 insertions(+), 36 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/utils/transform-input-key-name.ts diff --git a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx index 57a3a87312..cc1d2cf429 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -66,7 +66,12 @@ export const renderStepPanel = step => { > - {renderInputs({ key: step.key, value: item })} + {renderInputs({ + key: step.key, + value: item, + handleSetItem: step.handleSetItem, + keyValue: step.keyValue ?? '', + })}
diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx index 7fad498947..1a5714d1db 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; +import { set } from 'lodash'; import { PopoverIconButton } from '../components/popover'; import { getAppUrl } from '../../utils/get-app-url'; import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; @@ -59,32 +60,61 @@ export const CreateTemplate = () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [onXml, setOnXml] = useState(false); const [stepsToRender, setstepsToRender] = useState(['metadata']); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [item, setItem] = useState(metadataInitialValues); const [addStep, setAddStep] = useState( getAvailableOptions(stepsToRender)?.[0]?.value, ); const view = getAppUrl(); + + const handleSetItem = ({ + newValue, + key, + }: { + newValue: string | boolean; + key: string; + }) => { + setItem(prevItem => { + const newItem = { ...prevItem }; + + set(newItem, key, newValue); + + return newItem; + }); + }; + const buttonsPopover = [ { id: 'editOnFormOrXML', label: onXml ? 'Edit on form' : 'Edit on XML', color: 'text', + onClick: () => setOnXml(!onXml), }, { id: 'enable/disable', label: item?.enable ? 'Disable' : 'Enable', color: item?.enable ? 'danger' : 'primary', + onClick: () => { + handleSetItem({ + newValue: !item?.enable, + key: 'enable', + }); + }, }, { id: 'testItem', label: `Test ${view}`, color: 'text', + onClick: () => {}, }, { id: 'setSaveAsDraft', label: useParams()?.id ? 'Save as Draft' : 'Set as draft', color: 'text', + onClick: () => + handleSetItem({ + newValue: 'draft', + key: 'status', + }), }, ]; const buttons = [ @@ -102,10 +132,29 @@ export const CreateTemplate = () => { ))} , - {}}> + + console.log( + Object.fromEntries( + Object.entries(item).filter(([key]) => + [...stepsToRender, 'name', 'status', 'enable'].includes(key), + ), + ), + ) + } + > Save , - + history.back()} + > Cancel , ]; @@ -119,7 +168,7 @@ export const CreateTemplate = () => { .filter(([key]) => stepsToRender.includes(key)) .map(([stepName, value]) => ({ title: capitalizeFirstLetter(stepName), - children: renderStepPanel({ key: stepName, value }), + children: renderStepPanel({ key: stepName, value, handleSetItem }), })); const optionsToSelect = getAvailableOptions(stepsToRender); @@ -163,10 +212,15 @@ export const CreateTemplate = () => { pageTitle={ {}} + onChange={event => + handleSetItem({ + newValue: event.target.value, + key: 'name', + }) + } /> } bottomBorder={true} diff --git a/plugins/wazuh-security-policies/public/components/common/views.tsx b/plugins/wazuh-security-policies/public/components/common/views.tsx index f7016d9ecd..7e4eafb38d 100644 --- a/plugins/wazuh-security-policies/public/components/common/views.tsx +++ b/plugins/wazuh-security-policies/public/components/common/views.tsx @@ -172,6 +172,37 @@ export const views: ViewInterface[] = [ }, ], }, + { + name: i18n.translate('wz-security-policies-decoders-details', { + defaultMessage: 'Create decoder', + }), + id: 'decodersCreate', + path: '/decoders/create', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: () => [ + { + text: ( + view.id === 'decoders')?.name} + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'decoders')?.path}`, + }), + }, + { + text: ( + + ), + }, + ], + }, { name: i18n.translate('wz-security-policies-decoders-details', { defaultMessage: 'Decoders details', @@ -220,6 +251,37 @@ export const views: ViewInterface[] = [ }, ], }, + { + name: i18n.translate('wz-security-policies-decoders-details', { + defaultMessage: 'Create KVDB', + }), + id: 'kvdbCreate', + path: '/kvdb/create', + renderOnMenu: false, + renderMenu: false, + render: () => , + breadcrumb: () => [ + { + text: ( + view.id === 'kvdb')?.name} + /> + ), + href: getCore().application.getUrlForApp('wazuhSecurityPolicies', { + path: `#/${views.find(view => view.id === 'decoders')?.path}`, + }), + }, + { + text: ( + + ), + }, + ], + }, { name: i18n.translate('wz-security-policies-kvdb-details', { defaultMessage: 'KVDB details', diff --git a/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts index aedd59917b..71097db038 100644 --- a/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts +++ b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts @@ -1,6 +1,7 @@ export interface MetadataSchema { enable: boolean; name: string; + status: string; metadata: { module: string; title: string; @@ -29,20 +30,22 @@ export interface MetadataSchema { export const metadataInitialValues: MetadataSchema = { enable: false, name: '', + status: '', metadata: { module: '', title: '', description: '', compatibility: '', - versions: [''], + versions: [], author: { name: '', - date: new Date().toISOString().split('T')[0], + date: '', email: '', }, - references: [''], + references: [], }, check: '', + parse: '', normalize: [], definitions: '', allow: '', diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx index 451c335b51..64615ad001 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { EuiBasicTable, EuiFormRow, EuiComboBox } from '@elastic/eui'; import { capitalizeFirstLetter } from '../capitalize-first-letter'; +import { transformInputKeyName } from '../transform-input-key-name'; import { inputString } from './string-inputs'; export const inputArray = ( - input: { key: string; value: any }, + input: { key: string; value: any; handleSetItem: any; keyValue?: string }, isEditable: boolean, ) => { const renderArrayTable = ['check', 'parse|', 'normalize']; @@ -53,9 +54,39 @@ export const inputArray = ( ); } + const onChange = event => { + const newValue = event.map(({ value }) => value); + + input.handleSetItem({ + key: transformInputKeyName(input.keyValue, input.key), + newValue: newValue, + }); + }; + + const onCreateOption = (searchValue: string) => { + const normalizedSearchValue = searchValue.trim().toLowerCase(); + + if (!normalizedSearchValue) { + return; + } + + input.handleSetItem({ + key: transformInputKeyName(input.keyValue, input.key), + newValue: [...input.value, searchValue], + }); + }; + const comboBoxInput = !isEditable && inputs.length === 1 && inputs[0].value === '' ? ( - inputString({ key: input.key, value: inputs[0].value }, isEditable) + inputString( + { + key: input.key, + value: inputs[0].value, + handleSetItem: input.handleSetItem, + keyValue: input.keyValue, + }, + isEditable, + ) ) : ( ); diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx index 2e691a2bb0..a56372074f 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx @@ -1,7 +1,7 @@ import { inputString } from './string-inputs'; export const inputObject = ( - input: { key: string; value: any }, + input: { key: string; value: any; handleSetItem: any; keyValue?: string }, isEditable: boolean, ) => { const inputsList = Object.entries(input.value).map(([key, value]) => ({ @@ -9,5 +9,14 @@ export const inputObject = ( value, })); - return inputsList.map(item => inputString(item, isEditable)); + return inputsList.map(item => + inputString( + { + ...item, + handleSetItem: input.handleSetItem, + keyValue: input.keyValue ?? '', + }, + isEditable, + ), + ); }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx index 999ede12a3..f2a09ede35 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx @@ -1,4 +1,5 @@ import { useLocation } from 'react-router-dom'; +import { transformInputKeyName } from '../transform-input-key-name'; import { inputString } from './string-inputs'; import { inputArray } from './array-inputs'; import { inputObject } from './object-inputs'; @@ -12,7 +13,12 @@ const possibleSteps = new Set([ 'definitions', ]); -export const renderInputs = (input: { key: string; value: any }) => { +export const renderInputs = (input: { + key: string; + value: any; + handleSetItem: any; + keyValue?: string; +}) => { const isEditable = useLocation().pathname.includes('edit') || useLocation().pathname.includes('create'); @@ -23,7 +29,13 @@ export const renderInputs = (input: { key: string; value: any }) => { value, })); - return inputsSteps.map(step => renderInputs(step)); + return inputsSteps.map(step => + renderInputs({ + ...step, + handleSetItem: input.handleSetItem, + keyValue: transformInputKeyName(input.keyValue, input.key), + }), + ); } if (typeof input.value === 'string') { diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx index 5a05322636..4bbaf5bdb1 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx @@ -1,26 +1,42 @@ import React from 'react'; import { EuiFormRow, EuiFieldText, EuiToolTip } from '@elastic/eui'; import { capitalizeFirstLetter } from '../capitalize-first-letter'; +import { transformInputKeyName } from '../transform-input-key-name'; export const inputString = ( - input: { key: string; value: any }, + input: { + key: string; + value: any; + handleSetItem: any; + keyValue: string | undefined; + }, isEditable: boolean, -) => ( - - - - - -); +) => { + const keyFinal = transformInputKeyName(input.keyValue, input.key); + + return ( + + + + input.handleSetItem({ + newValue: event.target.value, + key: keyFinal, + }) + } + /> + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/utils/transform-input-key-name.ts b/plugins/wazuh-security-policies/public/components/utils/transform-input-key-name.ts new file mode 100644 index 0000000000..0645cbe89e --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/transform-input-key-name.ts @@ -0,0 +1,4 @@ +export const transformInputKeyName = ( + parentKey: string | undefined, + currentKey: string, +) => (parentKey ? `${parentKey}.${currentKey}` : currentKey); From 19bfc0009a67ba08067178bc766439c37edaa597 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:10:22 +0100 Subject: [PATCH 28/30] Update form --- .../common/components/render-steps.tsx | 103 ++++++++++----- .../components/steps/parse-name-input.tsx | 38 ++++++ .../components/steps/render-check-step.tsx | 81 ++++++++++++ .../steps/render-normalize-step.tsx | 120 ++++++++++++++++++ .../components/steps/render-parse-step.tsx | 54 ++++++++ .../common/components/table-form.tsx | 86 +++++++++++++ .../public/components/common/constants.ts | 2 +- .../common/templates/create-template.tsx | 18 ++- .../common/templates/details-template.tsx | 6 +- .../rules/schemas/metadata.schema.ts | 4 +- .../components/utils/inputs/array-inputs.tsx | 9 +- .../components/utils/inputs/render-inputs.tsx | 12 +- .../components/utils/inputs/string-inputs.tsx | 2 +- .../public/components/utils/is-editable.ts | 5 + 14 files changed, 491 insertions(+), 49 deletions(-) create mode 100644 plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/steps/render-check-step.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/table-form.tsx create mode 100644 plugins/wazuh-security-policies/public/components/utils/is-editable.ts diff --git a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx index cc1d2cf429..bcbb97079e 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -4,15 +4,21 @@ import { EuiAccordion, EuiHorizontalRule, EuiForm, + EuiButton, } from '@elastic/eui'; import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; import { renderInputs } from '../../utils/inputs/render-inputs'; import { STEPS } from '../constants'; import './render-steps.scss'; +import { isEditable } from '../../utils/is-editable'; +import { RenderCheckStep } from './steps/render-check-step'; +import { RenderNormalizeStep } from './steps/render-normalize-step'; +import { RenderParseStep } from './steps/render-parse-step'; +import { ParseNameInput } from './steps/parse-name-input'; export const renderStepPanel = step => { - const isArrayOfObjects = - Array.isArray(step.value) && typeof step.value[0] === 'object'; + const editable = isEditable(); + let panelToRender: React.ReactNode; const renderCardTitle = (stepName: string, item: any) => { switch (true) { @@ -21,19 +27,26 @@ export const renderStepPanel = step => { } case stepName === STEPS.check: { + panelToRender = ; + if (typeof item.value === 'string') { return `${capitalizeFirstLetter(stepName)}: ${item.value}`; } - return `${capitalizeFirstLetter(stepName)} fields: ${item.value.map((obj: any) => Object.keys(obj)[0]).join(', ')}`; + return `${capitalizeFirstLetter(stepName)} fields: `; + // ${item.value.map((obj: any) => Object.keys(obj)[0]).join(', ')}`; } case stepName.startsWith(STEPS.parse): { + panelToRender = ; + return stepName.split('|')[1]; } case stepName === STEPS.normalize: { // Item is the only step in this case as you can have several normalize steps. + panelToRender = ; + return `${capitalizeFirstLetter(stepName)} fields: ${Object.keys(item).join(', ')}`; } @@ -56,39 +69,71 @@ export const renderStepPanel = step => { } }; - if (isArrayOfObjects) { - return step.value.map((item, index) => ( - + const stepsDiferentRender = [ + STEPS.check, + STEPS.parse, + STEPS.normalize, + STEPS.definitions, + ]; + + if (step.key === STEPS.normalize) { + return ( + <> + {step.value.map((item, index) => ( + + + + + {editable + ? panelToRender + : renderInputs({ + key: step.key, + value: item, + handleSetItem: step.handleSetItem, + keyValue: step.keyValue ?? '', + })} + + + + ))} + + step.handleSetItem({ key: step.key, newValue: [...step.value, {}] }) + } + > + Add new + + + ); + } + + return ( + + {step.key === STEPS.parse ? ( + + ) : ( + - {renderInputs({ - key: step.key, - value: item, - handleSetItem: step.handleSetItem, - keyValue: step.keyValue ?? '', - })} + {editable && stepsDiferentRender.includes(step.key) + ? panelToRender + : renderInputs(step)} - - )); - } - - return ( - - - - - {renderInputs(step)} - + )} ); }; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx new file mode 100644 index 0000000000..f8c094d91a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react'; +import { EuiFieldText, EuiButton } from '@elastic/eui'; +import { STEPS } from '../../constants'; + +interface ParseNameInputProps { + step: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + }; +} + +export const ParseNameInput = ({ step }: ParseNameInputProps) => { + const [inputValue, setInputValue] = useState(''); + + return ( +
+ { + setInputValue(event.target.value); + }} + compressed + /> + { + step.handleSetItem({ + key: `${STEPS.parse}|${inputValue}`, + newValue: step.value, + }); + }} + /> +
+ ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/render-check-step.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/render-check-step.tsx new file mode 100644 index 0000000000..32f3fd6857 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-check-step.tsx @@ -0,0 +1,81 @@ +import React, { useState, useRef } from 'react'; +import { EuiRadioGroup, EuiButton } from '@elastic/eui'; +import { inputString } from '../../../utils/inputs/string-inputs'; +import { TableForm } from '../table-form'; + +interface RenderCheckStepProps { + step: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + }; +} + +export const RenderCheckStep = (props: RenderCheckStepProps) => { + const { step } = props; + const instanceId = useRef( + `${step.key}-${Math.random().toString(36).slice(2, 11)}`, + ); + const [isString, setIsString] = useState( + typeof step.value === 'string' ? 'string' : 'array', + ); + const [valueString, setValueString] = useState( + typeof step.value === 'string' ? step.value : '', + ); + + const handleStringItem = ({ newValue }: { newValue: string }) => { + setValueString(newValue); + }; + + const handleSaveButton = () => { + step.handleSetItem({ + key: step.key, + newValue: valueString, + }); + }; + + return ( + <> + { + setIsString(id.split('-')[0]); + }} + name={`radio-group-${instanceId.current}`} + legend={{ + children: One or more, + }} + /> + {isString === 'string' && ( + <> + {inputString( + { ...step, value: valueString, handleSetItem: handleStringItem }, + true, + )} + { + handleSaveButton(); + }} + disabled={valueString === ''} + > + Save + + + )} + {isString === 'array' && ( + + )} + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx new file mode 100644 index 0000000000..435dfe94d5 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx @@ -0,0 +1,120 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiSelect, + EuiTitle, +} from '@elastic/eui'; +import { TableForm } from '../table-form'; +import { STEPS } from '../../constants'; +import { capitalizeFirstLetter } from '../../../utils/capitalize-first-letter'; +import { RenderCheckStep } from './render-check-step'; + +export const RenderNormalizeStep = (props: { + step: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + }; +}) => { + const [itemSelected, setItemSelected] = useState(''); + const [items, setItems] = useState([]); + const optionsToSelect = [ + { + text: 'Select item', + value: '', + }, + { + text: 'Map', + value: 'map', + }, + { + text: 'Check', + value: 'check', + }, + { + text: 'Parse', + value: 'parse', + }, + ]; + + const addItem = () => { + setItems([...items, itemSelected]); + }; + + let options = [ + { + text: 'Select item', + value: 'Select item', + }, + { + text: 'Map', + value: 'map', + }, + { + text: 'Check', + value: 'check', + }, + { + text: 'Parse', + value: 'parse', + }, + ]; + + useEffect(() => { + setItemSelected(options?.[0].value); + options = optionsToSelect.filter(option => !items.includes(option.value)); + }, [items]); + + return ( + <> + + + setItemSelected(event.target.value)} + /> + + + addItem()}> + Add step + + + + {items?.map((item, index) => { + switch (item) { + case STEPS.check: { + return ( +
+ +

{capitalizeFirstLetter(STEPS.check)}

+
+ +
+ ); + } + + default: { + return ( +
+ +

Map

+
+ +
+ ); + } + } + })} + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx new file mode 100644 index 0000000000..b86a72ac7e --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react'; +import { EuiButton } from '@elastic/eui'; +import { isEqual } from 'lodash'; +import { inputString } from '../../../utils/inputs/string-inputs'; +import { inputArray } from '../../../utils/inputs/array-inputs'; + +export const RenderParseStep = (props: any) => { + const { step } = props; + const [value, setValue] = useState(''); + const [valueArray, setValueArray] = useState(step?.value || []); + + const handleSetValue = ({ newValue }: { newValue: string }) => { + setValue(newValue); + }; + + const restartValue = () => setValue(''); + + const handleAddButton = () => { + setValueArray([...valueArray, value]); + restartValue(); + }; + + const handleSaveButton = () => { + step.handleSetItem({ + key: step.key, + newValue: valueArray, + }); + }; + + return ( + <> + {inputString({ ...step, value, handleSetItem: handleSetValue }, true)} + { + handleAddButton(); + }} + disabled={value === ''} + > + Add item + + {inputArray({ ...step, value: valueArray }, true)} + { + handleSaveButton(); + }} + disabled={valueArray.length === 0 || isEqual(valueArray, step.value)} + > + Save + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/table-form.tsx b/plugins/wazuh-security-policies/public/components/common/components/table-form.tsx new file mode 100644 index 0000000000..581284aa73 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/table-form.tsx @@ -0,0 +1,86 @@ +import React, { useState } from 'react'; +import { EuiButton } from '@elastic/eui'; +import { inputArray } from '../../utils/inputs/array-inputs'; +import { inputString } from '../../utils/inputs/string-inputs'; + +interface TableFormProps { + parentKey: string; + handleSetItem: (props: { key: string; newValue: any }) => void; +} + +export const TableForm = (props: TableFormProps) => { + const { parentKey, handleSetItem } = props; + const [valueObject, setValueObject] = useState({ field: '', value: '' }); + const [valueArray, setValueArray] = useState[]>([]); + + const handleObjectItem = ({ + key, + newValue, + }: { + key: string; + newValue: any; + }) => { + setValueObject({ ...valueObject, [key]: newValue }); + }; + + const restartValue = () => setValueObject({ field: '', value: '' }); + + const handleAddButton = () => { + setValueArray([...valueArray, { [valueObject.field]: valueObject.value }]); + restartValue(); + }; + + const handleSaveButton = () => { + handleSetItem({ + key: parentKey, + newValue: valueArray, + }); + }; + + return ( + <> + {inputString( + { + key: 'field', + value: valueObject.field, + handleSetItem: handleObjectItem, + }, + true, + )} + {inputString( + { + key: 'value', + value: valueObject.value, + handleSetItem: handleObjectItem, + }, + true, + )} + { + handleAddButton(); + }} + disabled={valueObject.field === '' || valueObject.value === ''} + > + Add item + + {inputArray( + { + key: parentKey, + value: valueArray, + handleSetItem: handleSetItem, + }, + true, + )} + { + handleSaveButton(); + }} + disabled={valueArray.length === 0} + > + Save + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/constants.ts b/plugins/wazuh-security-policies/public/components/common/constants.ts index 5a6e288d50..19cd1b08ec 100644 --- a/plugins/wazuh-security-policies/public/components/common/constants.ts +++ b/plugins/wazuh-security-policies/public/components/common/constants.ts @@ -1,7 +1,7 @@ export const STEPS = { metadata: 'metadata', check: 'check', - parse: 'parse|', + parse: 'parse', normalize: 'normalize', allow: 'allow', output: 'output', diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx index 1a5714d1db..a4fbef0411 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -70,7 +70,7 @@ export const CreateTemplate = () => { newValue, key, }: { - newValue: string | boolean; + newValue: string | boolean | object; key: string; }) => { setItem(prevItem => { @@ -140,8 +140,14 @@ export const CreateTemplate = () => { onClick={() => console.log( Object.fromEntries( - Object.entries(item).filter(([key]) => - [...stepsToRender, 'name', 'status', 'enable'].includes(key), + Object.entries(item).filter( + ([key]) => + [ + ...stepsToRender.filter(step => step !== 'parse'), + 'name', + 'status', + 'enable', + ].includes(key) || key.startsWith('parse|'), ), ), ) @@ -230,7 +236,11 @@ export const CreateTemplate = () => { }} rightSideItems={buttons} /> - + ); }; diff --git a/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx index 3cb8c1df70..d3484e2a96 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx @@ -98,7 +98,11 @@ export const DetailsTemplate = () => { }} rightSideItems={buttons} /> - + ); }; diff --git a/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts index 71097db038..0c3bf13e7a 100644 --- a/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts +++ b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts @@ -45,8 +45,8 @@ export const metadataInitialValues: MetadataSchema = { references: [], }, check: '', - parse: '', - normalize: [], + parse: [], + normalize: [{}], definitions: '', allow: '', outputs: '', diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx index 64615ad001..f93b9867e7 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -8,7 +8,7 @@ export const inputArray = ( input: { key: string; value: any; handleSetItem: any; keyValue?: string }, isEditable: boolean, ) => { - const renderArrayTable = ['check', 'parse|', 'normalize']; + const renderArrayTable = ['check', 'parse', 'normalize']; const isArrayOfObjects = Array.isArray(input.value) && input.value.length > 0 && @@ -123,8 +123,9 @@ export const inputArray = ( ]} /> ); + const tableOrComboBox = renderArrayTable.filter(item => + input.key.startsWith(item), + ); - return renderArrayTable.every(item => input.key.startsWith(item)) - ? tableInput - : comboBoxInput; + return tableOrComboBox.length === 0 ? comboBoxInput : tableInput; }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx index f2a09ede35..ce8ae599a4 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx @@ -1,5 +1,5 @@ -import { useLocation } from 'react-router-dom'; import { transformInputKeyName } from '../transform-input-key-name'; +import { isEditable } from '../is-editable'; import { inputString } from './string-inputs'; import { inputArray } from './array-inputs'; import { inputObject } from './object-inputs'; @@ -19,9 +19,7 @@ export const renderInputs = (input: { handleSetItem: any; keyValue?: string; }) => { - const isEditable = - useLocation().pathname.includes('edit') || - useLocation().pathname.includes('create'); + const editable = isEditable(); if (possibleSteps.has(input.key) && typeof input.value !== 'string') { const inputsSteps = Object.entries(input.value).map(([key, value]) => ({ @@ -39,10 +37,10 @@ export const renderInputs = (input: { } if (typeof input.value === 'string') { - return inputString(input, isEditable); + return inputString(input, editable); } return Array.isArray(input.value) - ? inputArray(input, isEditable) - : inputObject(input, isEditable); + ? inputArray(input, editable) + : inputObject(input, editable); }; diff --git a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx index 4bbaf5bdb1..0bde12685a 100644 --- a/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx @@ -8,7 +8,7 @@ export const inputString = ( key: string; value: any; handleSetItem: any; - keyValue: string | undefined; + keyValue?: string | undefined; }, isEditable: boolean, ) => { diff --git a/plugins/wazuh-security-policies/public/components/utils/is-editable.ts b/plugins/wazuh-security-policies/public/components/utils/is-editable.ts new file mode 100644 index 0000000000..11caf898a6 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/is-editable.ts @@ -0,0 +1,5 @@ +import { useLocation } from 'react-router-dom'; + +export const isEditable = () => + useLocation().pathname.includes('edit') || + useLocation().pathname.includes('create'); From c30e7117794383ab6e7be887fd926ee294a2bfab Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:06:18 +0100 Subject: [PATCH 29/30] Update forms and start codeEditor --- plugins/wazuh-security-policies/package.json | 3 + .../components/common/components/popover.tsx | 5 +- .../common/components/render-steps.tsx | 100 +++++++++--- .../components/steps/parse-name-input.tsx | 38 ----- .../steps/render-normalize-step.tsx | 148 +++++++++--------- .../components/steps/render-parse-step.tsx | 16 +- .../common/components/xml-editor.tsx | 62 ++++++++ .../common/templates/create-template.tsx | 51 ++++-- plugins/wazuh-security-policies/yarn.lock | 11 ++ 9 files changed, 286 insertions(+), 148 deletions(-) delete mode 100644 plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/xml-editor.tsx diff --git a/plugins/wazuh-security-policies/package.json b/plugins/wazuh-security-policies/package.json index e0dd122d44..0beee3edd7 100644 --- a/plugins/wazuh-security-policies/package.json +++ b/plugins/wazuh-security-policies/package.json @@ -6,5 +6,8 @@ "build": "yarn plugin-helpers build", "plugin-helpers": "../../scripts/use_node ../../scripts/plugin_helpers", "osd": "../../scripts/use_node ../../scripts/osd" + }, + "dependencies": { + "xml-js": "^1.6.11" } } diff --git a/plugins/wazuh-security-policies/public/components/common/components/popover.tsx b/plugins/wazuh-security-policies/public/components/common/components/popover.tsx index 62c1459252..d9148c44c4 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/popover.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/popover.tsx @@ -5,10 +5,11 @@ interface PopoverIconButtonProps { children?: React.ReactNode; styles?: React.CSSProperties; color?: string; + icon?: string; } export const PopoverIconButton = (props: PopoverIconButtonProps) => { - const { children, styles, color = 'text' } = props; + const { children, styles, color = 'text', icon = 'boxesVertical' } = props; const [isPopoverOpen, setIsPopoverOpen] = useState(false); const onButtonClick = () => setIsPopoverOpen(isPopoverOpen => !isPopoverOpen); const closePopover = () => setIsPopoverOpen(false); @@ -21,7 +22,7 @@ export const PopoverIconButton = (props: PopoverIconButtonProps) => { } diff --git a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx index bcbb97079e..0774086c09 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -1,23 +1,36 @@ -import React from 'react'; +import React, { useState } from 'react'; import { EuiPanel, EuiAccordion, EuiHorizontalRule, EuiForm, EuiButton, + EuiButtonEmpty, } from '@elastic/eui'; +import { cloneDeep } from 'lodash'; import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; import { renderInputs } from '../../utils/inputs/render-inputs'; import { STEPS } from '../constants'; import './render-steps.scss'; import { isEditable } from '../../utils/is-editable'; +import { metadataInitialValues } from '../../rules/schemas/metadata.schema'; import { RenderCheckStep } from './steps/render-check-step'; import { RenderNormalizeStep } from './steps/render-normalize-step'; import { RenderParseStep } from './steps/render-parse-step'; -import { ParseNameInput } from './steps/parse-name-input'; +import { PopoverIconButton } from './popover'; + +interface StepsProps { + step: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + keyValue?: string; + }; +} -export const renderStepPanel = step => { +export const RenderStepPanel = ({ step }: StepsProps) => { const editable = isEditable(); + const [onXml, setOnXml] = useState(false); let panelToRender: React.ReactNode; const renderCardTitle = (stepName: string, item: any) => { @@ -40,6 +53,10 @@ export const renderStepPanel = step => { case stepName.startsWith(STEPS.parse): { panelToRender = ; + if (!stepName.includes('|')) { + return 'Parse'; + } + return stepName.split('|')[1]; } @@ -115,25 +132,68 @@ export const renderStepPanel = step => { ); } + const buttonsPopover = [ + { + id: `editOnFormOrXMLStep-${step.key}`, + label: onXml ? 'Edit on form' : 'Edit on XML', + color: 'text', + onClick: () => setOnXml(!onXml), + }, + { + id: `duplicateItem-${step.key}`, + label: `Duplicate ${step.key}`, + color: 'text', + onClick: () => {}, + }, + { + id: `clear-${step.key}`, + label: 'Clear', + color: 'text', + onClick: () => { + step.handleSetItem({ + newValue: cloneDeep(metadataInitialValues[step.key]), + key: step.key, + }); + }, + }, + ]; + const popover = ( + +
+ {buttonsPopover.map((button, index) => ( + + + {button.label} + + {index < buttonsPopover.length - 1 && ( + + )} + + ))} +
+
+ ); + return ( - {step.key === STEPS.parse ? ( - - ) : ( - - - - - {editable && stepsDiferentRender.includes(step.key) - ? panelToRender - : renderInputs(step)} - - - )} + + + + + {editable && stepsDiferentRender.includes(step.key) + ? panelToRender + : renderInputs(step)} + + ); }; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx deleted file mode 100644 index f8c094d91a..0000000000 --- a/plugins/wazuh-security-policies/public/components/common/components/steps/parse-name-input.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from 'react'; -import { EuiFieldText, EuiButton } from '@elastic/eui'; -import { STEPS } from '../../constants'; - -interface ParseNameInputProps { - step: { - key: string; - value: any; - handleSetItem: (props: { key: string; newValue: any }) => void; - }; -} - -export const ParseNameInput = ({ step }: ParseNameInputProps) => { - const [inputValue, setInputValue] = useState(''); - - return ( -
- { - setInputValue(event.target.value); - }} - compressed - /> - { - step.handleSetItem({ - key: `${STEPS.parse}|${inputValue}`, - newValue: step.value, - }); - }} - /> -
- ); -}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx index 435dfe94d5..aa1e8b901f 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { EuiButton, EuiFlexGroup, @@ -10,6 +10,26 @@ import { TableForm } from '../table-form'; import { STEPS } from '../../constants'; import { capitalizeFirstLetter } from '../../../utils/capitalize-first-letter'; import { RenderCheckStep } from './render-check-step'; +import { RenderParseStep } from './render-parse-step'; + +const optionsToSelect = [ + { + text: 'Select item', + value: '', + }, + { + text: 'Map', + value: 'map', + }, + { + text: 'Check', + value: 'check', + }, + { + text: 'Parse', + value: 'parse', + }, +]; export const RenderNormalizeStep = (props: { step: { @@ -18,55 +38,18 @@ export const RenderNormalizeStep = (props: { handleSetItem: (props: { key: string; newValue: any }) => void; }; }) => { - const [itemSelected, setItemSelected] = useState(''); + const [itemSelected, setItemSelected] = useState(optionsToSelect[0].text); const [items, setItems] = useState([]); - const optionsToSelect = [ - { - text: 'Select item', - value: '', - }, - { - text: 'Map', - value: 'map', - }, - { - text: 'Check', - value: 'check', - }, - { - text: 'Parse', - value: 'parse', - }, - ]; + const [optionSelect, setOptionSelect] = + useState<{ text: string; value: string }[]>(optionsToSelect); const addItem = () => { setItems([...items, itemSelected]); + setOptionSelect( + optionSelect.filter(option => option.value !== itemSelected), + ); }; - let options = [ - { - text: 'Select item', - value: 'Select item', - }, - { - text: 'Map', - value: 'map', - }, - { - text: 'Check', - value: 'check', - }, - { - text: 'Parse', - value: 'parse', - }, - ]; - - useEffect(() => { - setItemSelected(options?.[0].value); - options = optionsToSelect.filter(option => !items.includes(option.value)); - }, [items]); - return ( <> @@ -74,7 +57,7 @@ export const RenderNormalizeStep = (props: { - {items?.map((item, index) => { - switch (item) { - case STEPS.check: { - return ( -
- -

{capitalizeFirstLetter(STEPS.check)}

-
- -
- ); - } + + {items?.map((item, index) => { + switch (item) { + case STEPS.check: { + return ( + + +

{capitalizeFirstLetter(STEPS.check)}

+
+ +
+ ); + } + + case STEPS.parse: { + return ( + + +

{capitalizeFirstLetter(STEPS.parse)}

+
+ +
+ ); + } - default: { - return ( -
- -

Map

-
- -
- ); + default: { + return ( + + +

Map

+
+ +
+ ); + } } - } - })} + })} +
); }; diff --git a/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx b/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx index b86a72ac7e..1e80933f7b 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx @@ -7,6 +7,7 @@ import { inputArray } from '../../../utils/inputs/array-inputs'; export const RenderParseStep = (props: any) => { const { step } = props; const [value, setValue] = useState(''); + const [eventField, setEventField] = useState(''); const [valueArray, setValueArray] = useState(step?.value || []); const handleSetValue = ({ newValue }: { newValue: string }) => { @@ -22,13 +23,26 @@ export const RenderParseStep = (props: any) => { const handleSaveButton = () => { step.handleSetItem({ - key: step.key, + key: `${step.key}|${eventField}`, newValue: valueArray, }); }; + const handleParseTitle = ({ newValue }: { newValue: string }) => { + setEventField(newValue); + }; + return ( <> + {inputString( + { + ...step, + key: 'Field to parse', + value: eventField, + handleSetItem: handleParseTitle, + }, + true, + )} {inputString({ ...step, value, handleSetItem: handleSetValue }, true)} void; +} + +export const XMLEditor: React.FC = ({ data, onChange }) => { + const [xmlContent, setXmlContent] = useState(''); + + const jsonToXml = (obj: any, indent = 0) => { + const spaces = ' '.repeat(indent); + let xml = ''; + + for (const prop in obj) { + if (Array.isArray(obj[prop])) { + for (const item of obj[prop]) { + xml += `${spaces}<${prop}>${ + typeof item === 'object' + ? '\n' + jsonToXml(item, indent + 2) + spaces + : item + }\n`; + } + } else if (typeof obj[prop] === 'object' && obj[prop] !== null) { + xml += `${spaces}<${prop}>\n${jsonToXml( + obj[prop], + indent + 2, + )}${spaces}\n`; + } else { + xml += `${spaces}<${prop}>${obj[prop]}\n`; + } + } + + return xml; + }; + + useEffect(() => { + const xmlData = `${jsonToXml(data)}`; + + setXmlContent(xmlData); + }, [data]); + + const handleEditorChange = (value: string) => { + setXmlContent(value); + onChange?.(value); + }; + + return ( + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx index a4fbef0411..7a09855b57 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -11,12 +11,14 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { set } from 'lodash'; +import { set, cloneDeep } from 'lodash'; +import convert from 'xml-js'; import { PopoverIconButton } from '../components/popover'; import { getAppUrl } from '../../utils/get-app-url'; import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; import { metadataInitialValues } from '../../rules/schemas/metadata.schema'; -import { renderStepPanel } from '../components/render-steps'; +import { RenderStepPanel } from '../components/render-steps'; +import { XMLEditor } from '../components/xml-editor'; const getAvailableOptions = selectedSteps => { const baseOptions = [ @@ -57,10 +59,10 @@ const getAvailableOptions = selectedSteps => { }; export const CreateTemplate = () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars + const cloneInitialValues = cloneDeep(metadataInitialValues); const [onXml, setOnXml] = useState(false); const [stepsToRender, setstepsToRender] = useState(['metadata']); - const [item, setItem] = useState(metadataInitialValues); + const [item, setItem] = useState(cloneInitialValues); const [addStep, setAddStep] = useState( getAvailableOptions(stepsToRender)?.[0]?.value, ); @@ -122,7 +124,11 @@ export const CreateTemplate = () => {
{buttonsPopover.map((button, index) => ( - + {button.label} {index < buttonsPopover.length - 1 && ( @@ -170,12 +176,14 @@ export const CreateTemplate = () => { }; const steps = stepsItems => { - const stepsArray = Object.entries(stepsItems) - .filter(([key]) => stepsToRender.includes(key)) - .map(([stepName, value]) => ({ - title: capitalizeFirstLetter(stepName), - children: renderStepPanel({ key: stepName, value, handleSetItem }), - })); + const stepsArray = stepsToRender.map(stepName => ({ + title: capitalizeFirstLetter(stepName), + children: ( + + ), + })); const optionsToSelect = getAvailableOptions(stepsToRender); if (optionsToSelect.length > 1) { @@ -236,11 +244,22 @@ export const CreateTemplate = () => { }} rightSideItems={buttons} /> - + {onXml ? ( + { + console.log('Check', value); + }} + /> + ) : ( + + )} ); }; diff --git a/plugins/wazuh-security-policies/yarn.lock b/plugins/wazuh-security-policies/yarn.lock index fb57ccd13a..2b90b3f456 100644 --- a/plugins/wazuh-security-policies/yarn.lock +++ b/plugins/wazuh-security-policies/yarn.lock @@ -2,3 +2,14 @@ # yarn lockfile v1 +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" From d8435828f1e1f7e7ba93744b24708df8aa895328 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:59:56 +0100 Subject: [PATCH 30/30] Change xml to yaml --- plugins/wazuh-security-policies/package.json | 6 +- .../common/components/render-steps.tsx | 8 +- .../common/components/xml-editor.tsx | 62 -------------- .../common/components/yaml-editor.tsx | 45 ++++++++++ .../common/templates/create-template.tsx | 84 +++++++++++-------- .../common/templates/details-template.tsx | 4 +- plugins/wazuh-security-policies/yarn.lock | 37 +++++--- 7 files changed, 132 insertions(+), 114 deletions(-) delete mode 100644 plugins/wazuh-security-policies/public/components/common/components/xml-editor.tsx create mode 100644 plugins/wazuh-security-policies/public/components/common/components/yaml-editor.tsx diff --git a/plugins/wazuh-security-policies/package.json b/plugins/wazuh-security-policies/package.json index 0beee3edd7..7832c342e2 100644 --- a/plugins/wazuh-security-policies/package.json +++ b/plugins/wazuh-security-policies/package.json @@ -8,6 +8,10 @@ "osd": "../../scripts/use_node ../../scripts/osd" }, "dependencies": { - "xml-js": "^1.6.11" + "@types/js-yaml": "^4.0.9", + "js-yaml": "^4.1.0" + }, + "devDependencies": { + "@types/react": "^19.0.8" } } diff --git a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx index 0774086c09..1cb6382980 100644 --- a/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -30,7 +30,7 @@ interface StepsProps { export const RenderStepPanel = ({ step }: StepsProps) => { const editable = isEditable(); - const [onXml, setOnXml] = useState(false); + const [onYaml, setOnYaml] = useState(false); let panelToRender: React.ReactNode; const renderCardTitle = (stepName: string, item: any) => { @@ -134,10 +134,10 @@ export const RenderStepPanel = ({ step }: StepsProps) => { const buttonsPopover = [ { - id: `editOnFormOrXMLStep-${step.key}`, - label: onXml ? 'Edit on form' : 'Edit on XML', + id: `editOnFormOrYAMLStep-${step.key}`, + label: onYaml ? 'Edit on form' : 'Edit on YAML', color: 'text', - onClick: () => setOnXml(!onXml), + onClick: () => setOnYaml(!onYaml), }, { id: `duplicateItem-${step.key}`, diff --git a/plugins/wazuh-security-policies/public/components/common/components/xml-editor.tsx b/plugins/wazuh-security-policies/public/components/common/components/xml-editor.tsx deleted file mode 100644 index e9f78e1099..0000000000 --- a/plugins/wazuh-security-policies/public/components/common/components/xml-editor.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import MonacoEditor from 'react-monaco-editor'; - -interface XMLEditorProps { - data: any; - onChange?: (value: string) => void; -} - -export const XMLEditor: React.FC = ({ data, onChange }) => { - const [xmlContent, setXmlContent] = useState(''); - - const jsonToXml = (obj: any, indent = 0) => { - const spaces = ' '.repeat(indent); - let xml = ''; - - for (const prop in obj) { - if (Array.isArray(obj[prop])) { - for (const item of obj[prop]) { - xml += `${spaces}<${prop}>${ - typeof item === 'object' - ? '\n' + jsonToXml(item, indent + 2) + spaces - : item - }\n`; - } - } else if (typeof obj[prop] === 'object' && obj[prop] !== null) { - xml += `${spaces}<${prop}>\n${jsonToXml( - obj[prop], - indent + 2, - )}${spaces}\n`; - } else { - xml += `${spaces}<${prop}>${obj[prop]}\n`; - } - } - - return xml; - }; - - useEffect(() => { - const xmlData = `${jsonToXml(data)}`; - - setXmlContent(xmlData); - }, [data]); - - const handleEditorChange = (value: string) => { - setXmlContent(value); - onChange?.(value); - }; - - return ( - - ); -}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/yaml-editor.tsx b/plugins/wazuh-security-policies/public/components/common/components/yaml-editor.tsx new file mode 100644 index 0000000000..c04d58d93b --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/yaml-editor.tsx @@ -0,0 +1,45 @@ +import React, { useEffect, useState } from 'react'; +import MonacoEditor from 'react-monaco-editor'; +import yaml from 'js-yaml'; + +interface YAMLEditorProps { + data: any; + onChange?: (value: string) => void; +} + +export const YAMLEditor: React.FC = ({ data, onChange }) => { + const [yamlContent, setYamlContent] = useState(''); + + useEffect(() => { + if (typeof data === 'string') { + return setYamlContent(data); + } + + const yamlData = yaml.dump(data, { + indent: 2, + lineWidth: -1, + noRefs: true, + }); + + setYamlContent(yamlData); + }, [data]); + + const handleEditorChange = (value: string) => { + setYamlContent(value); + onChange?.(value); + }; + + return ( + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx index 7a09855b57..65276764ab 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -10,15 +10,16 @@ import { EuiSelect, EuiFlexGroup, EuiFlexItem, + EuiTitle, } from '@elastic/eui'; import { set, cloneDeep } from 'lodash'; -import convert from 'xml-js'; +import yaml from 'js-yaml'; import { PopoverIconButton } from '../components/popover'; import { getAppUrl } from '../../utils/get-app-url'; import { capitalizeFirstLetter } from '../../utils/capitalize-first-letter'; import { metadataInitialValues } from '../../rules/schemas/metadata.schema'; import { RenderStepPanel } from '../components/render-steps'; -import { XMLEditor } from '../components/xml-editor'; +import { YAMLEditor } from '../components/yaml-editor'; const getAvailableOptions = selectedSteps => { const baseOptions = [ @@ -60,7 +61,7 @@ const getAvailableOptions = selectedSteps => { export const CreateTemplate = () => { const cloneInitialValues = cloneDeep(metadataInitialValues); - const [onXml, setOnXml] = useState(false); + const [onYaml, setOnYaml] = useState(false); const [stepsToRender, setstepsToRender] = useState(['metadata']); const [item, setItem] = useState(cloneInitialValues); const [addStep, setAddStep] = useState( @@ -86,10 +87,16 @@ export const CreateTemplate = () => { const buttonsPopover = [ { - id: 'editOnFormOrXML', - label: onXml ? 'Edit on form' : 'Edit on XML', + id: 'editOnFormOrYAML', + label: onYaml ? 'Edit on form' : 'Edit on YAML', color: 'text', - onClick: () => setOnXml(!onXml), + onClick: () => { + if (onYaml) { + setItem(yaml.load(item)); + } + + setOnYaml(!onYaml); + }, }, { id: 'enable/disable', @@ -145,17 +152,19 @@ export const CreateTemplate = () => { fill onClick={() => console.log( - Object.fromEntries( - Object.entries(item).filter( - ([key]) => - [ - ...stepsToRender.filter(step => step !== 'parse'), - 'name', - 'status', - 'enable', - ].includes(key) || key.startsWith('parse|'), - ), - ), + onYaml + ? item + : Object.fromEntries( + Object.entries(item).filter( + ([key]) => + [ + ...stepsToRender.filter(step => step !== 'parse'), + 'name', + 'status', + 'enable', + ].includes(key) || key.startsWith('parse|'), + ), + ), ) } > @@ -224,18 +233,24 @@ export const CreateTemplate = () => { <> - handleSetItem({ - newValue: event.target.value, - key: 'name', - }) - } - /> + onYaml ? ( + +

test

+
+ ) : ( + + handleSetItem({ + newValue: event.target.value, + key: 'name', + }) + } + /> + ) } bottomBorder={true} alignItems='center' @@ -244,20 +259,19 @@ export const CreateTemplate = () => { }} rightSideItems={buttons} /> - {onXml ? ( - { - console.log('Check', value); + setItem(value); + console.log(yaml.load(value)); }} /> ) : ( )} diff --git a/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx index d3484e2a96..3e2871bd84 100644 --- a/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx +++ b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx @@ -52,12 +52,12 @@ export const DetailsTemplate = () => { {item?.status}
, {}} > - View in XML + View in YAML , ]; diff --git a/plugins/wazuh-security-policies/yarn.lock b/plugins/wazuh-security-policies/yarn.lock index 2b90b3f456..63928dd81f 100644 --- a/plugins/wazuh-security-policies/yarn.lock +++ b/plugins/wazuh-security-policies/yarn.lock @@ -2,14 +2,31 @@ # yarn lockfile v1 -sax@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" - integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== - -xml-js@^1.6.11: - version "1.6.11" - resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" - integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== +"@types/js-yaml@^4.0.9": + "integrity" "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" + "resolved" "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz" + "version" "4.0.9" + +"@types/react@^19.0.8": + "integrity" "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==" + "resolved" "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz" + "version" "19.0.8" + dependencies: + "csstype" "^3.0.2" + +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" + +"csstype@^3.0.2": + "integrity" "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + "version" "3.1.3" + +"js-yaml@^4.1.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" dependencies: - sax "^1.2.4" + "argparse" "^2.0.1"