diff --git a/docker/osd-dev/dev.yml b/docker/osd-dev/dev.yml index c07d2b6c6d..2dcc8a56d5 100755 --- a/docker/osd-dev/dev.yml +++ b/docker/osd-dev/dev.yml @@ -239,11 +239,13 @@ 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' - '${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..77ac702ffd --- /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", "opensearchDashboardsUtils"], + "optionalPlugins": [] +} diff --git a/plugins/wazuh-security-policies/package.json b/plugins/wazuh-security-policies/package.json new file mode 100644 index 0000000000..7832c342e2 --- /dev/null +++ b/plugins/wazuh-security-policies/package.json @@ -0,0 +1,17 @@ +{ + "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" + }, + "dependencies": { + "@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/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..a0f409f9cc --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/app.tsx @@ -0,0 +1,111 @@ +import React, { useState, useEffect } from 'react'; +import { I18nProvider } from '@osd/i18n/react'; +import { + EuiPage, + EuiPageBody, + EuiSideNav, + EuiPageSideBar, + EuiPanel, +} from '@elastic/eui'; +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(); + const [currentTab, setCurrentTab] = useState(''); + const [renderMenu, setRenderMenu] = useState(true); + const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false); + + const toggleOpenOnMobile = () => { + setIsSideNavOpenOnMobile(!isSideNavOpenOnMobile); + }; + + useEffect(() => { + setCurrentTab(history.location.pathname); + }, [history.location.pathname]); + + const sideNav = [ + { + name: 'Ruleset', + id: 'wazuhRuleset', + 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}`, + })), + }, + ]; + + // Render the application DOM. + // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. + return ( + + + <> + + {renderMenu && ( + + toggleOpenOnMobile()} + isOpenOnMobile={isSideNavOpenOnMobile} + aria-label='Ruleset' + items={sideNav} + /> + + )} + + + + + {views.map(view => [ + { + const { id } = useParams() || null; + + if (id) { + getCore().chrome.setBreadcrumbs( + view.breadcrumb(decodeURIComponent(id)), + ); + } else { + 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/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/common/components/last-update-content-manager-text.tsx.tsx b/plugins/wazuh-security-policies/public/components/common/components/last-update-content-manager-text.tsx.tsx new file mode 100644 index 0000000000..44733ff087 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/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/components/popover.tsx b/plugins/wazuh-security-policies/public/components/common/components/popover.tsx new file mode 100644 index 0000000000..d9148c44c4 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/popover.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { EuiButtonIcon, EuiPopover } from '@elastic/eui'; + +interface PopoverIconButtonProps { + children?: React.ReactNode; + styles?: React.CSSProperties; + color?: string; + icon?: string; +} + +export const PopoverIconButton = (props: PopoverIconButtonProps) => { + const { children, styles, color = 'text', icon = 'boxesVertical' } = 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/common/components/render-steps.scss b/plugins/wazuh-security-policies/public/components/common/components/render-steps.scss new file mode 100644 index 0000000000..2a091c664a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/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/components/render-steps.tsx b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx new file mode 100644 index 0000000000..1cb6382980 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/render-steps.tsx @@ -0,0 +1,199 @@ +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 { PopoverIconButton } from './popover'; + +interface StepsProps { + step: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + keyValue?: string; + }; +} + +export const RenderStepPanel = ({ step }: StepsProps) => { + const editable = isEditable(); + const [onYaml, setOnYaml] = useState(false); + let panelToRender: React.ReactNode; + + 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: { + 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(', ')}`; + } + + case stepName.startsWith(STEPS.parse): { + panelToRender = ; + + if (!stepName.includes('|')) { + return 'Parse'; + } + + 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(', ')}`; + } + + 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); + } + } + }; + + 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 + + + ); + } + + const buttonsPopover = [ + { + id: `editOnFormOrYAMLStep-${step.key}`, + label: onYaml ? 'Edit on form' : 'Edit on YAML', + color: 'text', + onClick: () => setOnYaml(!onYaml), + }, + { + 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 ( + + + + + + {editable && stepsDiferentRender.includes(step.key) + ? panelToRender + : renderInputs(step)} + + + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/components/searchbar.tsx b/plugins/wazuh-security-policies/public/components/common/components/searchbar.tsx new file mode 100644 index 0000000000..7b5f858290 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/searchbar.tsx @@ -0,0 +1,58 @@ +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, 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) { + return; + } + + return ( + <> + + + ); + }; + + return ( + <> +
+ +
+ {renderError()} + + ); +}; 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..aa1e8b901f --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-normalize-step.tsx @@ -0,0 +1,126 @@ +import React, { 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'; +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: { + key: string; + value: any; + handleSetItem: (props: { key: string; newValue: any }) => void; + }; +}) => { + const [itemSelected, setItemSelected] = useState(optionsToSelect[0].text); + const [items, setItems] = useState([]); + const [optionSelect, setOptionSelect] = + useState<{ text: string; value: string }[]>(optionsToSelect); + + const addItem = () => { + setItems([...items, itemSelected]); + setOptionSelect( + optionSelect.filter(option => option.value !== itemSelected), + ); + }; + + return ( + <> + + + setItemSelected(event.target.value)} + /> + + + addItem()}> + Add step + + + + + {items?.map((item, index) => { + switch (item) { + case STEPS.check: { + return ( + + +

{capitalizeFirstLetter(STEPS.check)}

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

{capitalizeFirstLetter(STEPS.parse)}

+
+ +
+ ); + } + + 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..1e80933f7b --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/components/steps/render-parse-step.tsx @@ -0,0 +1,68 @@ +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 [eventField, setEventField] = 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}|${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)} + { + 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/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/constants.ts b/plugins/wazuh-security-policies/public/components/common/constants.ts new file mode 100644 index 0000000000..19cd1b08ec --- /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/templates/create-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx new file mode 100644 index 0000000000..65276764ab --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/create-template.tsx @@ -0,0 +1,279 @@ +import React, { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { + EuiPageHeader, + EuiFieldText, + EuiButtonEmpty, + EuiButton, + EuiHorizontalRule, + EuiSteps, + EuiSelect, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, +} from '@elastic/eui'; +import { set, cloneDeep } from 'lodash'; +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 { YAMLEditor } from '../components/yaml-editor'; + +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 = () => { + const cloneInitialValues = cloneDeep(metadataInitialValues); + const [onYaml, setOnYaml] = useState(false); + const [stepsToRender, setstepsToRender] = useState(['metadata']); + const [item, setItem] = useState(cloneInitialValues); + const [addStep, setAddStep] = useState( + getAvailableOptions(stepsToRender)?.[0]?.value, + ); + const view = getAppUrl(); + + const handleSetItem = ({ + newValue, + key, + }: { + newValue: string | boolean | object; + key: string; + }) => { + setItem(prevItem => { + const newItem = { ...prevItem }; + + set(newItem, key, newValue); + + return newItem; + }); + }; + + const buttonsPopover = [ + { + id: 'editOnFormOrYAML', + label: onYaml ? 'Edit on form' : 'Edit on YAML', + color: 'text', + onClick: () => { + if (onYaml) { + setItem(yaml.load(item)); + } + + setOnYaml(!onYaml); + }, + }, + { + 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 = [ + +
+ {buttonsPopover.map((button, index) => ( + + + {button.label} + + {index < buttonsPopover.length - 1 && ( + + )} + + ))} +
+
, + + console.log( + onYaml + ? item + : Object.fromEntries( + Object.entries(item).filter( + ([key]) => + [ + ...stepsToRender.filter(step => step !== 'parse'), + 'name', + 'status', + 'enable', + ].includes(key) || key.startsWith('parse|'), + ), + ), + ) + } + > + Save + , + history.back()} + > + Cancel + , + ]; + + const addStepRender = () => { + setstepsToRender([...stepsToRender, addStep]); + }; + + const steps = stepsItems => { + const stepsArray = stepsToRender.map(stepName => ({ + title: capitalizeFirstLetter(stepName), + children: ( + + ), + })); + 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 ( + <> + +

test

+ + ) : ( + + handleSetItem({ + newValue: event.target.value, + key: 'name', + }) + } + /> + ) + } + bottomBorder={true} + alignItems='center' + rightSideGroupProps={{ + alignItems: 'center', + }} + rightSideItems={buttons} + /> + {onYaml ? ( + { + setItem(value); + console.log(yaml.load(value)); + }} + /> + ) : ( + + )} + + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/templates/details-template.scss b/plugins/wazuh-security-policies/public/components/common/templates/details-template.scss new file mode 100644 index 0000000000..5377dfda66 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/details-template.scss @@ -0,0 +1,3 @@ +.steps-details { + margin-top: 10px; +} 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 new file mode 100644 index 0000000000..3e2871bd84 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/details-template.tsx @@ -0,0 +1,108 @@ +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 '../components/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 YAML + , + ]; + + 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/templates/no-results.tsx b/plugins/wazuh-security-policies/public/components/common/templates/no-results.tsx new file mode 100644 index 0000000000..8e8f5dbce7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/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} + '. +

+ + } + /> + ); +}; diff --git a/plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx b/plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx new file mode 100644 index 0000000000..1c501ad082 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/templates/overview-template.tsx @@ -0,0 +1,275 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiPanel, + EuiButton, + EuiText, + EuiBasicTable, + EuiHealth, + EuiPageHeader, + EuiLink, +} from '@elastic/eui'; +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 view = getAppUrl(); + 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 goCreateView = () => { + history.push(`/${view}/create`); + }; + + const rightSideItems = [ + + Create + , + ]; + // Header end + // Searchbar start + const isActiveOption = [ + { value: 'enable', name: 'Enable' }, + { 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 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', + 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], + }; + + // Table end + + return ( + + + + + {decoder.length <= 0 && } + + ); +}; 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..7e4eafb38d --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/common/views.tsx @@ -0,0 +1,312 @@ +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 { 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; + 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: true, + 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-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', + }), + 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: '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', + }), + 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-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', + }), + 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 || ''), + }, + ], + }, +]; 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..7935f91448 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/components/card-integration.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { + EuiCard, + EuiIcon, + EuiButtonEmpty, + EuiHorizontalRule, +} from '@elastic/eui'; +import { PopoverIconButton } from '../../common/components/popover'; +import { getHistory } from '../../../plugin-services'; + +interface CardIntegrationProps { + image: string; + title: string; + description: string; + isEnable: boolean; +} + +export const CardIntegration = (props: CardIntegrationProps) => { + const history = getHistory(); + 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', + }, + ]; + + const handleNavigation = (path: string) => { + history.push(path); + }; + + return ( + <> + } + paddingSize='m' + onClick={() => handleNavigation(`/integrations/${title}`)} + /> + +
+ {buttonIntegrations.map((button, index) => ( + + + {button.label} + + {index < buttonIntegrations.length - 1 && ( + + )} + + ))} +
+
+ + ); +}; 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-details.tsx b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx new file mode 100644 index 0000000000..9cc1cb5ba2 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/integration-details.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiHealth, + EuiIcon, + EuiButton, + EuiDescriptionList, + EuiPanel, + EuiPageHeader, +} from '@elastic/eui'; +import { useParams } from 'react-router-dom'; +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'; + +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[]; + module: string; + }>({ + image: '', + title: '', + module: '', + 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 = LastUpdateContentManagerText( + integrationData.lastUpdate, + ); + + 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 new file mode 100644 index 0000000000..585394ae7c --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/integrations.scss @@ -0,0 +1,14 @@ +.integration-title-header { + 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 new file mode 100644 index 0000000000..fb70c81bb3 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/integretions/overview.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +import { + EuiButton, + EuiHealth, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPageHeader, +} from '@elastic/eui'; +import './integrations.scss'; +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'; + +export const IntegrationOverview = () => { + const [query, setQuery] = useState({ text: '' }); + const [lastUpdate, setLastUpdate] = useState({ + lastUpdateDate: '12/18/2024', + status: 'Success', + }); + // Header page start + const titleHeader = ( + +

Integrations

+ + Updated + +
+ ); + + const updateContentManager = () => { + const currentDate = new Date().toLocaleString(); + + setLastUpdate({ + lastUpdateDate: currentDate, + status: 'Success', + }); + }; + + const descriptionHeader = LastUpdateContentManagerText( + integrations[0].lastUpdate, + ); + const rightSideItems = [ + + Update + , + ]; + // Header page end + // Search bar start + 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', + }, + }, + }; + // 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 ( + <> + + + + {!query.text && listAllIntegrationsComponent} + {query.text && integrationFilter.length === 0 ? ( + + ) : ( + integrationFilter + )} + + + ); +}; 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 new file mode 100644 index 0000000000..ca7f222101 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/mock-data-rules.tsx @@ -0,0 +1,704 @@ +export const decoder = [ + { + id: 1, + name: 'rule/discovery_kernel_module_enumeration/0', + provider: 'native', + status: 'enable', + metadata: { + module: 'Linux', + title: 'System Information Discovery', + description: 'Detects System Information Discovery commands.', + compatibility: '', + versions: [''], + author: { + name: 'Wazuh, Inc.', + date: '2024/09/09', + }, + references: [ + '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: + "$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.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/system-auth/0', + provider: 'native', + status: 'disable', + metadata: { + 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.', + email: 'info@wazuh.com', + date: '2023/05/15', + }, + references: [ + 'https://www.loggly.com/ultimate-guide/linux-logging-basics/', + ], + }, + 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' }, + { '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/apache-access/0', + provider: 'native', + status: 'disable', + metadata: { + 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.', + date: '2023/11/29', + }, + references: [ + 'https://httpd.apache.orgPlease generate document codes/2.4/logs.html', + ], + }, + check: '$event.module == apache-access', + 'parse|event.original': [ + ' - [] "<~/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' }, + { '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' }, + ], + }, + ], + }, + { + id: 4, + name: 'decoder/syslog/0', + provider: 'native', + status: 'disable', + metadata: { + 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', + date: '2022/11/08', + }, + references: [ + 'https://www.ietf.org/rfc/rfc3164.txt', + '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: [ + { + 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)' }, + ], + }, + ], + }, + { + id: 5, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 6, + name: 'check/string', + 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', + ], + }, + check: + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 7, + name: 'check/array', + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/ignore/ >', + ], + 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)' }, + ], + }, + { + id: 8, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 9, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 10, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 11, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, + { + id: 12, + 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': [ + ' []:<~/ignore/ >', + + ' :<~/ignore/ >', + + ' []: ', + + ' : ', + ' ', + + ' []:<~/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)' }, + ], + }, + ], + }, +]; 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..0c3bf13e7a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/rules/schemas/metadata.schema.ts @@ -0,0 +1,53 @@ +export interface MetadataSchema { + enable: boolean; + name: string; + status: 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: '', + status: '', + metadata: { + module: '', + title: '', + description: '', + compatibility: '', + versions: [], + author: { + name: '', + date: '', + email: '', + }, + references: [], + }, + check: '', + parse: [], + normalize: [{}], + definitions: '', + allow: '', + outputs: '', +}; 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); 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 new file mode 100644 index 0000000000..f93b9867e7 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/array-inputs.tsx @@ -0,0 +1,131 @@ +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; handleSetItem: any; keyValue?: string }, + isEditable: boolean, +) => { + 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 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, + handleSetItem: input.handleSetItem, + keyValue: input.keyValue, + }, + isEditable, + ) + ) : ( + + + + ); + const tableInput = ( + + ); + const tableOrComboBox = renderArrayTable.filter(item => + input.key.startsWith(item), + ); + + return tableOrComboBox.length === 0 ? comboBoxInput : tableInput; +}; 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..a56372074f --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/object-inputs.tsx @@ -0,0 +1,22 @@ +import { inputString } from './string-inputs'; + +export const inputObject = ( + input: { key: string; value: any; handleSetItem: any; keyValue?: string }, + isEditable: boolean, +) => { + const inputsList = Object.entries(input.value).map(([key, value]) => ({ + key: `${input.key}.${key}`, + value, + })); + + 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 new file mode 100644 index 0000000000..ce8ae599a4 --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/render-inputs.tsx @@ -0,0 +1,46 @@ +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'; + +const possibleSteps = new Set([ + 'metadata', + 'check', + 'normalize', + 'allow', + 'output', + 'definitions', +]); + +export const renderInputs = (input: { + key: string; + value: any; + handleSetItem: any; + keyValue?: string; +}) => { + const editable = isEditable(); + + 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, + handleSetItem: input.handleSetItem, + keyValue: transformInputKeyName(input.keyValue, input.key), + }), + ); + } + + if (typeof input.value === 'string') { + return inputString(input, editable); + } + + return Array.isArray(input.value) + ? 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 new file mode 100644 index 0000000000..0bde12685a --- /dev/null +++ b/plugins/wazuh-security-policies/public/components/utils/inputs/string-inputs.tsx @@ -0,0 +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; + 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/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'); 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); 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-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 new file mode 100644 index 0000000000..98039ebe47 --- /dev/null +++ b/plugins/wazuh-security-policies/public/plugin.ts @@ -0,0 +1,79 @@ +import { i18n } from '@osd/i18n'; +import { createHashHistory } from 'history'; +import { + AppMountParameters, + CoreSetup, + CoreStart, + DEFAULT_NAV_GROUPS, + Plugin, +} from '../../../src/core/public'; +import { PLUGIN_NAME } from '../common'; +import { + WazuhSecurityPoliciesPluginSetup, + WazuhSecurityPoliciesPluginStart, + AppPluginStartDependencies, +} from './types'; +import { setCore, setHistory } from './plugin-services'; + +export class WazuhSecurityPoliciesPlugin + implements + Plugin +{ + public setup(core: CoreSetup): WazuhSecurityPoliciesPluginSetup { + const pluginId = 'wazuhSecurityPolicies'; + + // Register an application into the side navigation menu + core.application.register({ + id: pluginId, + title: PLUGIN_NAME, + category: { + id: 'wz-category-security-policies', + label: i18n.translate('wz-app-category-security-policies', { + defaultMessage: 'Security Policies', + }), + order: 1000, + 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(); + + setHistory(createHashHistory()); + + // Render the application + return renderApp( + coreStart, + depsStart as AppPluginStartDependencies, + params, + ); + }, + }); + + 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 { + setCore(core); + + 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..1ea2b0bf0f --- /dev/null +++ b/plugins/wazuh-security-policies/public/types.ts @@ -0,0 +1,10 @@ +import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; + +// 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 {} + +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..63928dd81f --- /dev/null +++ b/plugins/wazuh-security-policies/yarn.lock @@ -0,0 +1,32 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@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: + "argparse" "^2.0.1"