From 6eafba24b7ac090d800fac6c8c29db16726edab6 Mon Sep 17 00:00:00 2001 From: JuanGarriuz Date: Mon, 18 Sep 2023 15:01:35 +0200 Subject: [PATCH 1/2] Adapt the new global menu redirections (#5827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix endpoint summary and server managment redirections * fix: adapt the redirections to applications - Endpoints summary - IT Hygiene - Groups * fix(health-check): fix redirection to health check the removed the tab query parameter from URL and then the previous URL could not be rebuilt * fix(redirections): redirection from Overview app to Endpoints summary * fix(redirections): removed wz- prefix from the redirections to applications * fix: renamed apps redirection URLs * fix: app redirections URLs * fix: test * fix: tests --------- Co-authored-by: Antonio David GutiĆ©rrez --- plugins/main/common/wazuh-modules.ts | 106 +++-- .../agents/syscollector/inventory.tsx | 7 +- .../modules/events-enhance-discover-fields.ts | 241 +++++++--- .../agent-group-truncate/group-truncate.tsx | 104 ++-- .../common/welcome/agents-welcome.js | 36 +- .../fim_events_table/fim_events_table.tsx | 50 +- .../common/welcome/components/menu-agent.js | 73 ++- .../requirement_vis/requirement_vis.tsx | 109 +++-- .../welcome/components/sca_scan/sca_scan.tsx | 443 +++++++++--------- .../common/welcome/overview-welcome.js | 14 +- .../components/sample-data-warning.js | 19 +- .../components/sample-data-warning.test.js | 21 +- .../agent/components/agents-table.js | 13 +- .../configuration/configuration-no-agent.js | 25 +- .../configuration/configuration-switch.js | 132 ++++-- .../management/groups/group-agents-table.js | 3 +- .../management/ruleset/views/rule-info.tsx | 337 ++++++++----- .../statistics/statistics-overview.js | 240 +++++----- .../public/controllers/management/groups.js | 23 +- .../controllers/management/monitoring.js | 95 ++-- .../controllers/overview/components/stats.js | 115 +++-- .../main/public/services/resolves/get-ip.js | 38 +- .../server/routes/wazuh-reporting.test.ts | 10 +- 23 files changed, 1337 insertions(+), 917 deletions(-) diff --git a/plugins/main/common/wazuh-modules.ts b/plugins/main/common/wazuh-modules.ts index 99b32f1fb8..35b0221bfe 100644 --- a/plugins/main/common/wazuh-modules.ts +++ b/plugins/main/common/wazuh-modules.ts @@ -11,133 +11,165 @@ */ export const WAZUH_MODULES = { general: { - title: 'Security events', + title: 'Threat hunting', + appId: 'threat-hunting', description: - 'Browse through your security alerts, identifying issues and threats in your environment.' + 'Browse through your security alerts, identifying issues and threats in your environment.', }, fim: { - title: 'Integrity monitoring', + title: 'File integrity monitoring', + appId: 'file-integrity-monitoring', description: - 'Alerts related to file changes, including permissions, content, ownership and attributes.' + 'Alerts related to file changes, including permissions, content, ownership and attributes.', }, pm: { - title: 'Policy monitoring', + title: 'Malware detection', + appId: 'malware-detection', description: - 'Verify that your systems are configured according to your security policies baseline.' + 'Verify that your systems are configured according to your security policies baseline.', }, vuls: { - title: 'Vulnerabilities', + title: 'Vulnerability detection', + appId: 'vulnerability-detection', description: - 'Discover what applications in your environment are affected by well-known vulnerabilities.' + 'Discover what applications in your environment are affected by well-known vulnerabilities.', }, oscap: { title: 'OpenSCAP', + appId: 'openscap', description: - 'Configuration assessment and automation of compliance monitoring using SCAP checks.' + 'Configuration assessment and automation of compliance monitoring using SCAP checks.', }, audit: { title: 'System auditing', + appId: 'system-auditing', description: - 'Audit users behavior, monitoring command execution and alerting on access to critical files.' + 'Audit users behavior, monitoring command execution and alerting on access to critical files.', }, pci: { title: 'PCI DSS', + appId: 'pci-dss', description: - 'Global security standard for entities that process, store or transmit payment cardholder data.' + 'Global security standard for entities that process, store or transmit payment cardholder data.', }, gdpr: { title: 'GDPR', + appId: 'gdpr', description: - 'General Data Protection Regulation (GDPR) sets guidelines for processing of personal data.' + 'General Data Protection Regulation (GDPR) sets guidelines for processing of personal data.', }, hipaa: { title: 'HIPAA', + appId: 'hipaa', description: - 'Health Insurance Portability and Accountability Act of 1996 (HIPAA) provides data privacy and security provisions for safeguarding medical information.' + 'Health Insurance Portability and Accountability Act of 1996 (HIPAA) provides data privacy and security provisions for safeguarding medical information.', }, nist: { title: 'NIST 800-53', + appId: 'nist-800-53', description: - 'National Institute of Standards and Technology Special Publication 800-53 (NIST 800-53) sets guidelines for federal information systems.' + 'National Institute of Standards and Technology Special Publication 800-53 (NIST 800-53) sets guidelines for federal information systems.', }, tsc: { title: 'TSC', + appId: 'tsc', description: - 'Trust Services Criteria for Security, Availability, Processing Integrity, Confidentiality, and Privacy' + 'Trust Services Criteria for Security, Availability, Processing Integrity, Confidentiality, and Privacy', }, ciscat: { title: 'CIS-CAT', + appId: 'ciscat', description: - 'Configuration assessment using Center of Internet Security scanner and SCAP checks.' + 'Configuration assessment using Center of Internet Security scanner and SCAP checks.', }, aws: { - title: 'Amazon AWS', + title: 'AWS', + appId: 'amazon-web-services', description: - 'Security events related to your Amazon AWS services, collected directly via AWS API.' + 'Security events related to your Amazon AWS services, collected directly via AWS API.', }, office: { title: 'Office 365', - description: - 'Security events related to your Office 365 services.' + appId: 'office365', + description: 'Security events related to your Office 365 services.', }, gcp: { - title: 'Google Cloud Platform', + title: 'Google Cloud', + appId: 'google-cloud', description: - 'Security events related to your Google Cloud Platform services, collected directly via GCP API.' // TODO GCP + 'Security events related to your Google Cloud Platform services, collected directly via GCP API.', // TODO GCP }, virustotal: { title: 'VirusTotal', + appId: 'virustotal', description: - 'Alerts resulting from VirusTotal analysis of suspicious files via an integration with their API.' + 'Alerts resulting from VirusTotal analysis of suspicious files via an integration with their API.', }, mitre: { title: 'MITRE ATT&CK', + appId: 'mitre-attack', description: - 'Security events from the knowledge base of adversary tactics and techniques based on real-world observations' + 'Security events from the knowledge base of adversary tactics and techniques based on real-world observations', }, syscollector: { title: 'Inventory data', + // This appId is not used, for consistency was added. + appId: 'it-hygiene', description: - 'Applications, network configuration, open ports and processes running on your monitored systems.' + 'Applications, network configuration, open ports and processes running on your monitored systems.', }, stats: { title: 'Stats', - description: 'Stats for agent and logcollector' + // This appId is not used, for consistency was added. + appId: 'it-hygiene', + description: 'Stats for agent and logcollector', }, configuration: { title: 'Configuration', + // This appId is not used, for consistency was added. + appId: 'it-hygiene', description: - 'Check the current agent configuration remotely applied by its group.' + 'Check the current agent configuration remotely applied by its group.', }, osquery: { title: 'Osquery', + appId: 'osquery', description: - 'Osquery can be used to expose an operating system as a high-performance relational database.' + 'Osquery can be used to expose an operating system as a high-performance relational database.', }, sca: { - title: 'Security configuration assessment', - description: 'Scan your assets as part of a configuration assessment audit.' + title: 'Configuration assessment', + appId: 'configuration-assessment', + description: + 'Scan your assets as part of a configuration assessment audit.', }, docker: { - title: 'Docker listener', + title: 'Docker', + appId: 'docker', description: - 'Monitor and collect the activity from Docker containers such as creation, running, starting, stopping or pausing events.' + 'Monitor and collect the activity from Docker containers such as creation, running, starting, stopping or pausing events.', }, github: { title: 'GitHub', + appId: 'github', description: - 'Monitoring events from audit logs of your GitHub organizations.' + 'Monitoring events from audit logs of your GitHub organizations.', }, devTools: { title: 'API console', - description: 'Test the Wazuh API endpoints.' + appId: 'api-console', + description: 'Test the Wazuh API endpoints.', }, logtest: { title: 'Test your logs', - description: 'Check your ruleset testing logs.' + appId: 'ruleset-test', + description: 'Check your ruleset testing logs.', }, + + // TODO - Research the uses of this code. testConfiguration: { title: 'Test your configurations', - description: 'Check configurations before applying them' - } + appId: '', + description: 'Check configurations before applying them', + }, }; diff --git a/plugins/main/public/components/agents/syscollector/inventory.tsx b/plugins/main/public/components/agents/syscollector/inventory.tsx index f341dc32e3..84cb02e0ba 100644 --- a/plugins/main/public/components/agents/syscollector/inventory.tsx +++ b/plugins/main/public/components/agents/syscollector/inventory.tsx @@ -24,6 +24,7 @@ import { SyscollectorTable } from './components/syscollector-table'; import { processColumns, portsColumns, packagesColumns } from './columns'; import { API_NAME_AGENT_STATUS } from '../../../../common/constants'; import { webDocumentationLink } from '../../../../common/services/web_documentation'; +import { getNavigationAppURL } from '../../../react-services/navigate-app'; export function SyscollectorInventory({ agent }) { if (agent && agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED) { @@ -51,7 +52,11 @@ export function SyscollectorInventory({ agent }) { } actions={ - + Back } diff --git a/plugins/main/public/components/common/modules/events-enhance-discover-fields.ts b/plugins/main/public/components/common/modules/events-enhance-discover-fields.ts index 798a6af3a6..70bcfd99c8 100644 --- a/plugins/main/public/components/common/modules/events-enhance-discover-fields.ts +++ b/plugins/main/public/components/common/modules/events-enhance-discover-fields.ts @@ -13,115 +13,208 @@ import { FlyoutDetail } from '../../../components/agents/fim/inventory/flyout'; import { FlyoutTechnique } from '../../overview/mitre/components/techniques/components/flyout-technique'; import { AppNavigate } from '../../../react-services/app-navigate'; +import { getNavigationAppURL } from '../../../react-services/navigate-app'; // Field to add to elements enchanced -const CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD = 'data-wazuh-discover-field-enhanced'; +const CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD = + 'data-wazuh-discover-field-enhanced'; type TGetFlyoutProps = (content: string, rowData, options) => any; // Set attributes (as object) in a HTML element const addAttributesToElement = (element, attributes: any, ...options) => { Object.keys(attributes).forEach(attribute => { - const attributeValue: ((...options) => (string | undefined)) | string | undefined = attributes[attribute]; - const attributeValueResult = typeof attributeValue === 'function' ? attributeValue(...options) : attributeValue; - if(attributeValueResult){ + const attributeValue: + | ((...options) => string | undefined) + | string + | undefined = attributes[attribute]; + const attributeValueResult = + typeof attributeValue === 'function' + ? attributeValue(...options) + : attributeValue; + if (attributeValueResult) { element.setAttribute(attribute, attributeValueResult); - }; + } }); -} +}; // Enhance field: create an anchor HTML element that redirect to some URL -const createElementFieldURLRedirection = (attributes?: any) => (content: string, rowData, element, options) => { - const container = document.createElement('a'); - addAttributesToElement(container, attributes, content, rowData, options); - container.textContent = content; - return container.getAttribute('href') ? container : undefined; -}; +const createElementFieldURLRedirection = + (attributes?: any) => (content: string, rowData, element, options) => { + const container = document.createElement('a'); + addAttributesToElement(container, attributes, content, rowData, options); + container.textContent = content; + return container.getAttribute('href') ? container : undefined; + }; // Enhance field: create an anchor HTML element that open a flyout React component -const createElementFieldOpenFlyout = (FlyoutComponent, getFlyoutProps: TGetFlyoutProps) => (content: string, rowData, element, options) => { - const container = document.createElement('a'); - container.onclick = () => { - options.setFlyout({ component: FlyoutComponent, props: getFlyoutProps(content, rowData, options)}); +const createElementFieldOpenFlyout = + (FlyoutComponent, getFlyoutProps: TGetFlyoutProps) => + (content: string, rowData, element, options) => { + const container = document.createElement('a'); + container.onclick = () => { + options.setFlyout({ + component: FlyoutComponent, + props: getFlyoutProps(content, rowData, options), + }); + }; + container.textContent = content; + return container; }; - container.textContent = content; - return container; -} // Enhance field: create a div HTML element with anchor HTML elements in the same cell to open a flyout React component with different data -const createElementFieldOpenFlyoutMultiple = (FlyoutComponent, getFlyoutProps: TGetFlyoutProps, containerOptions) => (content: string, rowData, element, options) => { - const container = document.createElement(containerOptions.element || 'div'); - const formattedContent = content.match(containerOptions.contentRegex); - if(!formattedContent){ return }; - const createElementFieldOpenFlyoutCreator = createElementFieldOpenFlyout(FlyoutComponent, getFlyoutProps); - formattedContent.forEach((item, itemIndex) => { - const itemElement = createElementFieldOpenFlyoutCreator(item, rowData, element, options); - container.appendChild(itemElement); - if(itemIndex < formattedContent.length - 1 ){ - const separatorElement = document.createElement(containerOptions.separatorElement || 'span'); - separatorElement.textContent = containerOptions.separator || ', '; - container.appendChild(separatorElement); +const createElementFieldOpenFlyoutMultiple = + (FlyoutComponent, getFlyoutProps: TGetFlyoutProps, containerOptions) => + (content: string, rowData, element, options) => { + const container = document.createElement(containerOptions.element || 'div'); + const formattedContent = content.match(containerOptions.contentRegex); + if (!formattedContent) { + return; } - }); - return container; -} + const createElementFieldOpenFlyoutCreator = createElementFieldOpenFlyout( + FlyoutComponent, + getFlyoutProps, + ); + formattedContent.forEach((item, itemIndex) => { + const itemElement = createElementFieldOpenFlyoutCreator( + item, + rowData, + element, + options, + ); + container.appendChild(itemElement); + if (itemIndex < formattedContent.length - 1) { + const separatorElement = document.createElement( + containerOptions.separatorElement || 'span', + ); + separatorElement.textContent = containerOptions.separator || ', '; + container.appendChild(separatorElement); + } + }); + return container; + }; // Returns the style attribute value as string -const styleObjectToString = (obj: any) => Object.keys(obj).map(key => `${key}: ${obj[key]}`).join('; '); +const styleObjectToString = (obj: any) => + Object.keys(obj) + .map(key => `${key}: ${obj[key]}`) + .join('; '); // Styles for external links (as object) const attributesExternalLink = { target: '__blank', - rel: 'noreferrer' + rel: 'noreferrer', }; // Define button styles const buttonStyles = attributesExternalLink; export const EventsEnhanceDiscoverCell = { - 'rule.id': createElementFieldURLRedirection({...buttonStyles, href: (content) => `#/manager/rules?tab=rules&redirectRule=${content}`}), - 'agent.id': createElementFieldURLRedirection({...buttonStyles, href: (content) => content !== '000' ? `#/agents?tab=welcome&agent=${content}` : undefined}), - 'agent.name': createElementFieldURLRedirection({...buttonStyles, href: (content, rowData) => { - const agentId = (((rowData || {})._source || {}).agent || {}).id; - return agentId !== '000' ? `#/agents?tab=welcome&agent=${agentId}` : undefined; - }}), - 'syscheck.path': createElementFieldOpenFlyout(FlyoutDetail, (content, rowData, options) => ({ - fileName: content, - agentId: (((rowData || {})._source || {}).agent || {}).id, - type:'file', - view:'events', - closeFlyout: options.closeFlyout - })), - 'rule.mitre.id': createElementFieldOpenFlyoutMultiple(FlyoutTechnique, (content, rowData, options) => ({ - openDiscover(e, techniqueID) { - AppNavigate.navigateToModule(e, 'overview', { "tab": 'mitre', "tabView": "discover", filters: { 'rule.mitre.id': techniqueID } }) - }, - openDashboard(e, techniqueID) { - AppNavigate.navigateToModule(e, 'overview', { "tab": 'mitre', "tabView": "dashboard", filters: { 'rule.mitre.id': techniqueID } }) - }, - onChangeFlyout(isFlyoutVisible){ - isFlyoutVisible ? null : options.closeFlyout() + 'rule.id': createElementFieldURLRedirection({ + ...buttonStyles, + href: content => + getNavigationAppURL( + `/app/rules#/manager/rules?tab=rules&redirectRule=${content}`, + ), + }), + 'agent.id': createElementFieldURLRedirection({ + ...buttonStyles, + href: content => + content !== '000' + ? getNavigationAppURL( + `/app/it-hygiene#/agents?tab=welcome&agent=${content}`, + ) + : undefined, + }), + 'agent.name': createElementFieldURLRedirection({ + ...buttonStyles, + href: (content, rowData) => { + const agentId = (((rowData || {})._source || {}).agent || {}).id; + return agentId !== '000' + ? getNavigationAppURL( + `/app/it-hygiene#/agents?tab=welcome&agent=${agentId}`, + ) + : undefined; }, - currentTechnique: content - }), { - contentRegex: /(T\d+\.?(\d+)?)/g, - element: 'span' }), + 'syscheck.path': createElementFieldOpenFlyout( + FlyoutDetail, + (content, rowData, options) => ({ + fileName: content, + agentId: (((rowData || {})._source || {}).agent || {}).id, + type: 'file', + view: 'events', + closeFlyout: options.closeFlyout, + }), + ), + 'rule.mitre.id': createElementFieldOpenFlyoutMultiple( + FlyoutTechnique, + (content, rowData, options) => ({ + openDiscover(e, techniqueID) { + AppNavigate.navigateToModule(e, 'overview', { + tab: 'mitre', + tabView: 'discover', + filters: { 'rule.mitre.id': techniqueID }, + }); + }, + openDashboard(e, techniqueID) { + AppNavigate.navigateToModule(e, 'overview', { + tab: 'mitre', + tabView: 'dashboard', + filters: { 'rule.mitre.id': techniqueID }, + }); + }, + onChangeFlyout(isFlyoutVisible) { + isFlyoutVisible ? null : options.closeFlyout(); + }, + currentTechnique: content, + }), + { + contentRegex: /(T\d+\.?(\d+)?)/g, + element: 'span', + }, + ), 'syscheck.value_name': (content, rowData, element, options) => { if (content) return; - const container = document.createElement("span"); - container.insertAdjacentHTML('beforeend', ''); - container.insertAdjacentHTML('beforeend','Empty field'); + const container = document.createElement('span'); + container.insertAdjacentHTML( + 'beforeend', + '', + ); + container.insertAdjacentHTML( + 'beforeend', + 'Empty field', + ); return container; - } -} + }, +}; // Method to enhance a cell of discover table -export const enhanceDiscoverEventsCell = (field, content, rowData, element, options) => { - if(!EventsEnhanceDiscoverCell[field] || !element || element.attributes[CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD]){ return }; - const elementCellEnhanced = EventsEnhanceDiscoverCell[field](content, rowData, element, options); - if(elementCellEnhanced){ - elementCellEnhanced.setAttribute(CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD, ''); // Set a custom attribute to indentify that element was enhanced +export const enhanceDiscoverEventsCell = ( + field, + content, + rowData, + element, + options, +) => { + if ( + !EventsEnhanceDiscoverCell[field] || + !element || + element.attributes[CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD] + ) { + return; + } + const elementCellEnhanced = EventsEnhanceDiscoverCell[field]( + content, + rowData, + element, + options, + ); + if (elementCellEnhanced) { + elementCellEnhanced.setAttribute( + CUSTOM_ATTRIBUTE_ENHANCED_DISCOVER_FIELD, + '', + ); // Set a custom attribute to indentify that element was enhanced element.replaceWith(elementCellEnhanced); - }; -} + } +}; diff --git a/plugins/main/public/components/common/util/agent-group-truncate/group-truncate.tsx b/plugins/main/public/components/common/util/agent-group-truncate/group-truncate.tsx index d423aa2208..251bf902a0 100644 --- a/plugins/main/public/components/common/util/agent-group-truncate/group-truncate.tsx +++ b/plugins/main/public/components/common/util/agent-group-truncate/group-truncate.tsx @@ -18,29 +18,30 @@ import { EuiFlexGrid, EuiLink, EuiBadge, - EuiPopover + EuiPopover, } from '@elastic/eui'; +import { navigateAppURL } from '../../../../react-services/navigate-app'; export class GroupTruncate extends React.Component { _isMount = false; state: { - groups: any, - popoverOpen: boolean - } + groups: any; + popoverOpen: boolean; + }; props!: { - label: String, - length: number, - agent: Object, - action: String, - filterAction: any - } - + label: String; + length: number; + agent: Object; + action: String; + filterAction: any; + }; + constructor(props) { super(props); this.state = { groups: '', popoverOpen: false, - } + }; } filterAction(group) { @@ -50,7 +51,9 @@ export class GroupTruncate extends React.Component { action(index, group) { switch (this.props.action) { case 'redirect': - return this.props.goGroups(this.props.agent, index); + return navigateAppURL( + `/app/endpoint-groups#/manager/?tab=groups&group=${group}`, + ); case 'filter': return this.filterAction(group); default: @@ -62,16 +65,19 @@ export class GroupTruncate extends React.Component { renderButton(auxIndex) { return ( { ev.stopPropagation() }} - onClick={ (ev) => { + onMouseDown={ev => { ev.stopPropagation(); - this.setState({popoverOpen: !this.state.popoverOpen}) - }}> + }} + onClick={ev => { + ev.stopPropagation(); + this.setState({ popoverOpen: !this.state.popoverOpen }); + }} + >  {`+${auxIndex} ${this.props.label}`} - ) + ); } renderBadge(group, index) { @@ -80,59 +86,55 @@ export class GroupTruncate extends React.Component { color={'hollow'} key={`agent-group-${index}`} onClickAriaLabel={`agent-group-${index}`} - onMouseDown={ (ev) => { ev.stopPropagation() }} - onClick={ - (ev) => { - ev.stopPropagation(); - this.action(index, group); - } - }> + onMouseDown={ev => { + ev.stopPropagation(); + }} + onClick={ev => { + ev.stopPropagation(); + this.action(index, group); + }} + > {group} - ) + ); } renderGroups(groups) { const { length } = this.props; let auxGroups: Array = []; - let tooltipGroups: Array = [] + let tooltipGroups: Array = []; let auxLength = 0; let auxIndex = 0; if (groups.length >= 2 && groups.toString().length >= length) { - groups.map( (group, index) => { + groups.map((group, index) => { auxLength = auxLength + group.length; - if (auxLength >= length ) { + if (auxLength >= length) { tooltipGroups.push( {this.renderBadge(group, index)} - + , ); ++auxIndex; } else { - auxGroups.push( - this.renderBadge(group, index) - ); + auxGroups.push(this.renderBadge(group, index)); } }); } else { - groups.map( (group, index) => { - auxGroups.push( - this.renderBadge(group, index) - ) - }) + groups.map((group, index) => { + auxGroups.push(this.renderBadge(group, index)); + }); } const button = this.renderButton(auxIndex); return ( - - - {auxGroups} - - {auxIndex > 0 && + + {auxGroups} + {auxIndex > 0 && ( this.setState({popoverOpen: false})}> - + closePopover={() => this.setState({ popoverOpen: false })} + > + {tooltipGroups} @@ -140,15 +142,13 @@ export class GroupTruncate extends React.Component { - } + )} ); } - + render() { - const groups = this.renderGroups(this.props.groups) - return ( - groups - ) + const groups = this.renderGroups(this.props.groups); + return groups; } } diff --git a/plugins/main/public/components/common/welcome/agents-welcome.js b/plugins/main/public/components/common/welcome/agents-welcome.js index 8e3b8ebd73..d0ea39f7d7 100644 --- a/plugins/main/public/components/common/welcome/agents-welcome.js +++ b/plugins/main/public/components/common/welcome/agents-welcome.js @@ -52,8 +52,16 @@ import { getAngularModule } from '../../../kibana-services'; import { hasAgentSupportModule } from '../../../react-services/wz-agents'; import { withErrorBoundary, withGlobalBreadcrumb, withReduxProvider } from '../hocs'; import { compose } from 'redux'; -import { API_NAME_AGENT_STATUS } from '../../../../common/constants'; +import { + API_NAME_AGENT_STATUS, + WAZUH_AGENTS_OS_TYPE, +} from '../../../../common/constants'; import { webDocumentationLink } from '../../../../common/services/web_documentation'; +import { WAZUH_MODULES } from '../../../../common/wazuh-modules'; +import { + getNavigationAppURL, + navigateAppURL, +} from '../../../react-services/navigate-app'; export const AgentsWelcome = compose( withReduxProvider, @@ -224,13 +232,15 @@ export const AgentsWelcome = compose( > { - window.location.href = `#/overview/?tab=${ - menuAgent.id - }&tabView=${ - menuAgent.text === 'Security configuration assessment' - ? 'inventory' - : 'panels' - }`; + navigateAppURL( + `/app/${ + WAZUH_MODULES[menuAgent.id].appId + }#/overview/?tab=${menuAgent.id}&tabView=${ + menuAgent.text === 'Security configuration assessment' + ? 'inventory' + : 'panels' + }`, + ); this.router.reload(); }} style={{ cursor: 'pointer' }} @@ -387,7 +397,7 @@ export const AgentsWelcome = compose( iconType='popout' color='primary' onClick={() => { - window.location.href = `#/overview?tab=mitre`; + navigateAppURL('/app/mitre-attack'); this.router.reload(); }} aria-label='Open MITRE' @@ -496,7 +506,13 @@ export const AgentsWelcome = compose( } actions={ - + Back } diff --git a/plugins/main/public/components/common/welcome/components/fim_events_table/fim_events_table.tsx b/plugins/main/public/components/common/welcome/components/fim_events_table/fim_events_table.tsx index 6d086a6bae..5b5482bb8b 100644 --- a/plugins/main/public/components/common/welcome/components/fim_events_table/fim_events_table.tsx +++ b/plugins/main/public/components/common/welcome/components/fim_events_table/fim_events_table.tsx @@ -33,30 +33,31 @@ import { formatUIDate } from '../../../../../react-services/time-service'; import { FlyoutDetail } from '../../../../agents/fim/inventory/flyout'; import { EuiLink } from '@elastic/eui'; import { getDataPlugin } from '../../../../../kibana-services'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; export function FimEventsTable({ agent, router }) { return ( - + - +

FIM: Recent events

- + navigateToFim(agent, router)} - aria-label="Open FIM" + aria-label='Open FIM' />
- +
@@ -82,7 +83,10 @@ function FimTable({ agent }) { const [fimAlerts, setFimAlerts] = useState([]); const [isOpen, setIsOpen] = useState(false); const [file, setFile] = useState(''); - const [sort, setSort] = useState({ field: '_source.timestamp', direction: 'desc' }); + const [sort, setSort] = useState({ + field: '_source.timestamp', + direction: 'desc', + }); const timeFilter = useTimeFilter(); useEffect(() => { getFimAlerts(agent.id, timeFilter, sort).then(setFimAlerts); @@ -94,16 +98,16 @@ function FimTable({ agent }) { columns={columns(setFile, setIsOpen)} loading={false} sorting={{ sort }} - onChange={(e) => setSort(e.sort)} - itemId="fim-alerts" - noItemsMessage="No recent events" + onChange={e => setSort(e.sort)} + itemId='fim-alerts' + noItemsMessage='No recent events' /> {isOpen && ( setIsOpen(false)} fileName={file} - view="extern" + view='extern' {...{ agent }} /> )} @@ -112,8 +116,8 @@ function FimTable({ agent }) { } function navigateToFim(agent, router) { - window.location.href = `#/overview/?tab=fim`; store.dispatch(updateCurrentAgentData(agent)); + navigateAppURL('/app/file-integrity-monitoring'); router.reload(); } @@ -122,7 +126,7 @@ const columns = (setFile, setIsOpen) => [ field: '_source.timestamp', name: 'Time', sortable: true, - render: (field) => formatUIDate(field), + render: field => formatUIDate(field), width: '150px', }, { @@ -130,22 +134,32 @@ const columns = (setFile, setIsOpen) => [ name: 'Path', sortable: true, truncateText: true, - render: (path) => renderPath(path, setFile, setIsOpen), + render: path => renderPath(path, setFile, setIsOpen), + }, + { + field: '_source.syscheck.event', + name: 'Action', + sortable: true, + width: '100px', }, - { field: '_source.syscheck.event', name: 'Action', sortable: true, width: '100px' }, { field: '_source.rule.description', name: 'Rule description', sortable: true, truncateText: true, }, - { field: '_source.rule.level', name: 'Rule Level', sortable: true, width: '75px' }, + { + field: '_source.rule.level', + name: 'Rule Level', + sortable: true, + width: '75px', + }, { field: '_source.rule.id', name: 'Rule Id', sortable: true, width: '75px' }, ]; const renderPath = (path, setFile, setIsOpen) => ( { setFile(path), setIsOpen(true); }} diff --git a/plugins/main/public/components/common/welcome/components/menu-agent.js b/plugins/main/public/components/common/welcome/components/menu-agent.js index 9fc5fb6159..6a87e9dd3f 100644 --- a/plugins/main/public/components/common/welcome/components/menu-agent.js +++ b/plugins/main/public/components/common/welcome/components/menu-agent.js @@ -10,13 +10,21 @@ * Find more information about this on the LICENSE file. */ import React, { Component } from 'react'; -import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSideNav } from '@elastic/eui'; +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSideNav, +} from '@elastic/eui'; import { connect } from 'react-redux'; import { AppState } from '../../../../react-services/app-state'; import { hasAgentSupportModule } from '../../../../react-services/wz-agents'; import { getAngularModule, getToasts } from '../../../../kibana-services'; import { updateCurrentAgentData } from '../../../../redux/actions/appStateActions'; import { getAgentSections } from './agent-sections'; +import { WAZUH_MODULES } from '../../../../../common/wazuh-modules'; +import { navigateAppURL } from '../../../../react-services/navigate-app'; class WzMenuAgent extends Component { constructor(props) { @@ -31,14 +39,14 @@ class WzMenuAgent extends Component { ? JSON.parse(window.localStorage.getItem('menuAgent')) : {}; - this.agentSections = getAgentSections(this.menuAgent) + this.agentSections = getAgentSections(this.menuAgent); this.securityInformationItems = [ this.agentSections.general, this.agentSections.fim, this.agentSections.aws, this.agentSections.gcp, - this.agentSections.github + this.agentSections.github, ]; this.auditingItems = [ this.agentSections.pm, @@ -70,11 +78,13 @@ class WzMenuAgent extends Component { this.router = $injector.get('$route'); } - clickMenuItem = (section) => { + clickMenuItem = section => { this.props.closePopover(); if (this.props.currentTab !== section) { // do not redirect if we already are in that tab - window.location.href = `#/overview/?tab=${section}`; + navigateAppURL( + `/app/${WAZUH_MODULES[section].appId}#/overview/?tab=${section}`, + ); this.props.updateCurrentAgentData(this.props.isAgent); this.router.reload(); } @@ -84,17 +94,17 @@ class WzMenuAgent extends Component { getToasts().add({ title, text, toastLifeTimeMs: time, color }); } - createItems = (items) => { - const keyExists = (key) => Object.keys(this.state.extensions).includes(key); - const keyIsTrue = (key) => (this.state.extensions || [])[key]; + createItems = items => { + const keyExists = key => Object.keys(this.state.extensions).includes(key); + const keyIsTrue = key => (this.state.extensions || [])[key]; return items .filter( - (item) => + item => hasAgentSupportModule(this.props.currentAgentData, item.id) && Object.keys(this.state.extensions).length && - (!keyExists(item.id) || keyIsTrue(item.id)) + (!keyExists(item.id) || keyIsTrue(item.id)), ) - .map((item) => this.createItem(item)); + .map(item => this.createItem(item)); }; createItem = (item, data = {}) => { @@ -139,12 +149,15 @@ class WzMenuAgent extends Component { color: 'danger', }); } - window.localStorage.setItem('menuAgent', JSON.stringify(this.menuAgent)); + window.localStorage.setItem( + 'menuAgent', + JSON.stringify(this.menuAgent), + ); this.props.updateMenuAgents(); }} - color="primary" + color='primary' type={this.menuAgent[item.id] ? 'pinFilled' : 'pin'} - aria-label="Next" + aria-label='Next' style={{ cursor: 'pointer' }} />
@@ -158,48 +171,57 @@ class WzMenuAgent extends Component { render() { const securityInformation = [ this.createItem(this.agentSections.securityInformation, { - icon: , + icon: , items: this.createItems(this.securityInformationItems), }), ]; const auditing = [ this.createItem(this.agentSections.auditing, { - icon: , + icon: , items: this.createItems(this.auditingItems), }), ]; const threatDetection = [ this.createItem(this.agentSections.threatDetection, { - icon: , + icon: , items: this.createItems(this.threatDetectionItems), }), ]; const regulatoryCompliance = [ this.createItem(this.agentSections.regulatoryCompliance, { - icon: , + icon: , items: this.createItems(this.regulatoryComplianceItems), }), ]; return ( -
+
{(Object.keys(this.state.extensions).length && (
- + - + - +
@@ -209,15 +231,16 @@ class WzMenuAgent extends Component { } } -const mapStateToProps = (state) => { +const mapStateToProps = state => { return { currentAgentData: state.appStateReducers.currentAgentData, currentTab: state.appStateReducers.currentTab, }; }; -const mapDispatchToProps = (dispatch) => ({ - updateCurrentAgentData: (agentData) => dispatch(updateCurrentAgentData(agentData)), +const mapDispatchToProps = dispatch => ({ + updateCurrentAgentData: agentData => + dispatch(updateCurrentAgentData(agentData)), }); export default connect(mapStateToProps, mapDispatchToProps)(WzMenuAgent); diff --git a/plugins/main/public/components/common/welcome/components/requirement_vis/requirement_vis.tsx b/plugins/main/public/components/common/welcome/components/requirement_vis/requirement_vis.tsx index 0af9aeb0c6..02b2bfa4c7 100644 --- a/plugins/main/public/components/common/welcome/components/requirement_vis/requirement_vis.tsx +++ b/plugins/main/public/components/common/welcome/components/requirement_vis/requirement_vis.tsx @@ -12,12 +12,8 @@ * Find more information about this on the LICENSE file. */ -import React, { useCallback, useState } from 'react' -import { - EuiFlexItem, - EuiPanel, - euiPaletteColorBlind -} from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { EuiFlexItem, EuiPanel, euiPaletteColorBlind } from '@elastic/eui'; import { VisualizationBasicWidgetSelector } from '../../../charts/visualizations/basic'; import { getRequirementAlerts } from './lib'; import { useTimeFilter } from '../../../hooks'; @@ -27,6 +23,8 @@ import { getAngularModule } from '../../../../../kibana-services'; import { getIndexPattern } from '../../../../overview/mitre/lib'; import { buildPhraseFilter } from '../../../../../../../../src/plugins/data/common'; import rison from 'rison-node'; +import { WAZUH_MODULES } from '../../../../../../common/wazuh-modules'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; const selectionOptionsCompliance = [ { value: 'pci_dss', text: 'PCI DSS' }, @@ -34,72 +32,95 @@ const selectionOptionsCompliance = [ { value: 'nist_800_53', text: 'NIST 800-53' }, { value: 'hipaa', text: 'HIPAA' }, { value: 'gpg13', text: 'GPG13' }, - { value: 'tsc', text: 'TSC' } + { value: 'tsc', text: 'TSC' }, ]; const requirementNameModuleID = { - 'pci_dss': 'pci', - 'gdpr': 'gdpr', - 'nist_800_53': 'nist', - 'hipaa': 'hipaa', - 'gpg13': '', - 'tsc': 'tsc', -} + pci_dss: 'pci', + gdpr: 'gdpr', + nist_800_53: 'nist', + hipaa: 'hipaa', + gpg13: '', + tsc: 'tsc', +}; export function RequirementVis(props) { const colors = euiPaletteColorBlind(); - const {timeFilter} = useTimeFilter(); + const { timeFilter } = useTimeFilter(); const dispatch = useDispatch(); const goToDashboardWithFilter = async (requirement, key, agent) => { - try{ + try { dispatch(updateCurrentAgentData(agent)); const $injector = getAngularModule().$injector; const route = $injector.get('$route'); - const indexPattern = getIndexPattern() - const filters = [{ - ...buildPhraseFilter({ name: `rule.${requirement}`, type: 'text' }, key, indexPattern), - "$state": { "isImplicit": false, "store": "appState" }, - }]; + const indexPattern = getIndexPattern(); + const filters = [ + { + ...buildPhraseFilter( + { name: `rule.${requirement}`, type: 'text' }, + key, + indexPattern, + ), + $state: { isImplicit: false, store: 'appState' }, + }, + ]; const _w = { filters }; const params = { tab: requirementNameModuleID[requirement], - _w: rison.encode(_w) + _w: rison.encode(_w), }; - const url = Object.entries(params).map(e => e.join('=')).join('&'); - window.location.href = `#/overview?${url}`; - route.reload(); - }catch(error){ - - } - } + const url = Object.entries(params) + .map(e => e.join('=')) + .join('&'); + // TODO: redirection to gdpr will fail + navigateAppURL( + `/app/${WAZUH_MODULES[params.tab].appId}#/overview?${url}`, + ); + route.reload(); + } catch (error) {} + }; - const fetchData = useCallback(async (selectedOptionValue, timeFilter, agent) => { - const buckets = await getRequirementAlerts(agent.id, timeFilter, selectedOptionValue); - return buckets?.length ? buckets.map(({key, doc_count}, index) => ({ - label: key, - value: doc_count, - color: colors[index], - onClick: selectedOptionValue === 'gpg13' ? undefined : (() => goToDashboardWithFilter(selectedOptionValue, key, agent)) - })) : null; - }, []); + const fetchData = useCallback( + async (selectedOptionValue, timeFilter, agent) => { + const buckets = await getRequirementAlerts( + agent.id, + timeFilter, + selectedOptionValue, + ); + return buckets?.length + ? buckets.map(({ key, doc_count }, index) => ({ + label: key, + value: doc_count, + color: colors[index], + onClick: + selectedOptionValue === 'gpg13' + ? undefined + : () => + goToDashboardWithFilter(selectedOptionValue, key, agent), + })) + : null; + }, + [], + ); return ( - + `No ${optionRequirement.text} results were found in the selected time range.`} + noDataMessage={(_, optionRequirement) => + `No ${optionRequirement.text} results were found in the selected time range.` + } /> - ) + ); } - diff --git a/plugins/main/public/components/common/welcome/components/sca_scan/sca_scan.tsx b/plugins/main/public/components/common/welcome/components/sca_scan/sca_scan.tsx index 470db2f74f..e788ab8d7e 100644 --- a/plugins/main/public/components/common/welcome/components/sca_scan/sca_scan.tsx +++ b/plugins/main/public/components/common/welcome/components/sca_scan/sca_scan.tsx @@ -32,244 +32,263 @@ import { updateCurrentAgentData } from '../../../../../redux/actions/appStateAct import { WzRequest } from '../../../../../react-services'; import { formatUIDate } from '../../../../../react-services/time-service'; import { getAngularModule } from '../../../../../kibana-services'; -import { withReduxProvider, withUserAuthorizationPrompt } from "../../../hocs"; +import { withReduxProvider, withUserAuthorizationPrompt } from '../../../hocs'; import { compose } from 'redux'; import SCAPoliciesTable from '../../../../agents/sca/inventory/agent-policies-table'; import { MODULE_SCA_CHECK_RESULT_LABEL } from '../../../../../../common/constants'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; type Props = { agent: { [key in string]: any }; router: any; // its angular router -} +}; export const ScaScan = compose( withReduxProvider, withUserAuthorizationPrompt([ [ - {action: 'agent:read', resource: 'agent:id:*'}, - {action: 'agent:read', resource: 'agent:group:*'} + { action: 'agent:read', resource: 'agent:id:*' }, + { action: 'agent:read', resource: 'agent:group:*' }, ], [ - {action: 'sca:read', resource: 'agent:id:*'}, - {action: 'sca:read', resource: 'agent:group:*'} - ] - ]) -)(class ScaScan extends Component { - _isMount = false; - router; - state: { - lastScan: { - [key: string]: any - }, - isLoading: Boolean, - firstTable: Boolean, - policies: any[], - } - - constructor(props) { - super(props); - this.state = { - lastScan: {}, - isLoading: true, - firstTable: true, - policies: [], + { action: 'sca:read', resource: 'agent:id:*' }, + { action: 'sca:read', resource: 'agent:group:*' }, + ], + ]), +)( + class ScaScan extends Component { + _isMount = false; + router; + state: { + lastScan: { + [key: string]: any; + }; + isLoading: Boolean; + firstTable: Boolean; + policies: any[]; }; - } + constructor(props) { + super(props); + this.state = { + lastScan: {}, + isLoading: true, + firstTable: true, + policies: [], + }; + } - async componentDidMount() { - this._isMount = true; - const $injector = getAngularModule().$injector; - this.router = $injector.get('$route'); - this.getLastScan(this.props.agent.id); - } + async componentDidMount() { + this._isMount = true; + const $injector = getAngularModule().$injector; + this.router = $injector.get('$route'); + this.getLastScan(this.props.agent.id); + } - async getLastScan(agentId: Number) { - const scans = await WzRequest.apiReq('GET', `/sca/${agentId}?sort=-end_scan`, {params:{ limit: 1} }); - this._isMount && - this.setState({ - lastScan: (((scans.data || {}).data || {}).affected_items || {})[0], - isLoading: false, - }); - } + async getLastScan(agentId: Number) { + const scans = await WzRequest.apiReq( + 'GET', + `/sca/${agentId}?sort=-end_scan`, + { params: { limit: 1 } }, + ); + this._isMount && + this.setState({ + lastScan: (((scans.data || {}).data || {}).affected_items || {})[0], + isLoading: false, + }); + } - durationScan() { - const { lastScan } = this.state; - const start_scan = moment(lastScan.start_scan); - const end_scan = moment(lastScan.end_scan); - let diff = start_scan.diff(end_scan); - let duration = moment.duration(diff); - let auxDuration = Math.floor(duration.asHours()) + moment.utc(diff).format(":mm:ss"); - return auxDuration === '0:00:00' ? '< 1s' : auxDuration; - } + durationScan() { + const { lastScan } = this.state; + const start_scan = moment(lastScan.start_scan); + const end_scan = moment(lastScan.end_scan); + let diff = start_scan.diff(end_scan); + let duration = moment.duration(diff); + let auxDuration = + Math.floor(duration.asHours()) + moment.utc(diff).format(':mm:ss'); + return auxDuration === '0:00:00' ? '< 1s' : auxDuration; + } - renderLoadingStatus() { - const { isLoading } = this.state; - if (!isLoading) { - return; - } else { - return ( - - -
- -
-
-
- ) + renderLoadingStatus() { + const { isLoading } = this.state; + if (!isLoading) { + return; + } else { + return ( + + +
+ +
+
+
+ ); + } } - } - onClickRow = (policy) => { - window.location.href = `#/overview?tab=sca&redirectPolicyTable=${policy.policy_id}`; - store.dispatch(updateCurrentAgentData(this.props.agent)); - this.router.reload(); - } + onClickRow = policy => { + window.location.href = `#/overview?tab=sca&redirectPolicyTable=${policy.policy_id}`; + store.dispatch(updateCurrentAgentData(this.props.agent)); + this.router.reload(); + }; - renderScanDetails() { - const { isLoading, lastScan } = this.state; - if (isLoading || lastScan === undefined) return; + renderScanDetails() { + const { isLoading, lastScan } = this.state; + if (isLoading || lastScan === undefined) return; - const columnsPolicies = [ - { - field: 'name', - name: 'Policy', - width: '40%', - }, - { - field: 'end_scan', - name: 'End scan', - dataType: 'date', - render: formatUIDate, - width: '20%', - }, - { - field: 'pass', - name: MODULE_SCA_CHECK_RESULT_LABEL.passed, - width: '10%', - }, - { - field: 'fail', - name: MODULE_SCA_CHECK_RESULT_LABEL.failed, - width: '10%', - }, - { - field: 'invalid', - name: MODULE_SCA_CHECK_RESULT_LABEL['not applicable'], - width: '10%', - }, - { - field: 'score', - name: 'Score', - width: '10%', - render: (score) => { - return `${score}%`; - } - }, - ]; + const columnsPolicies = [ + { + field: 'name', + name: 'Policy', + width: '40%', + }, + { + field: 'end_scan', + name: 'End scan', + dataType: 'date', + render: formatUIDate, + width: '20%', + }, + { + field: 'pass', + name: MODULE_SCA_CHECK_RESULT_LABEL.passed, + width: '10%', + }, + { + field: 'fail', + name: MODULE_SCA_CHECK_RESULT_LABEL.failed, + width: '10%', + }, + { + field: 'invalid', + name: MODULE_SCA_CHECK_RESULT_LABEL['not applicable'], + width: '10%', + }, + { + field: 'score', + name: 'Score', + width: '10%', + render: score => { + return `${score}%`; + }, + }, + ]; - const tableProps = { - tablePageSizeOptions: [4], - hidePerPageOptions: true - } - - return ( - - - - - { - window.location.href = `#/overview?tab=sca&redirectPolicyTable=${lastScan.policy_id}`; - store.dispatch(updateCurrentAgentData(this.props.agent)); - this.router.reload(); - } - }> -

{lastScan.name}

- -
-
-
- - {lastScan.policy_id} - -
- - - -
- ) - } + const tableProps = { + tablePageSizeOptions: [4], + hidePerPageOptions: true, + }; + return ( + + + + + { + window.location.href = `#/overview?tab=sca&redirectPolicyTable=${lastScan.policy_id}`; + store.dispatch(updateCurrentAgentData(this.props.agent)); + this.router.reload(); + }} + > +

{lastScan.name}

+ +
+
+
+ + {lastScan.policy_id} + +
+ + + +
+ ); + } - renderEmptyPrompt() { - const { isLoading } = this.state; - if (isLoading) return; - return ( - - You don't have SCA scans in this agent.} - body={ - -

- Check your agent settings to generate scans. -

-
- } - /> -
- ) - } + renderEmptyPrompt() { + const { isLoading } = this.state; + if (isLoading) return; + return ( + + You don't have SCA scans in this agent.} + body={ + +

Check your agent settings to generate scans.

+
+ } + /> +
+ ); + } - render() { - const { lastScan } = this.state; - const loading = this.renderLoadingStatus(); - const scaScan = this.renderScanDetails(); - const emptyPrompt = this.renderEmptyPrompt(); - return ( - - - - - - - { - window.location.href = `#/overview?tab=sca&redirectPolicy=${lastScan.policy_id}`; - store.dispatch(updateCurrentAgentData(this.props.agent)); - this.router.reload(); - } - }> -

SCA: Lastest scans

-
-
-
- - - { - window.location.href = `#/overview?tab=sca`; - store.dispatch(updateCurrentAgentData(this.props.agent)); + render() { + const { lastScan } = this.state; + const loading = this.renderLoadingStatus(); + const scaScan = this.renderScanDetails(); + const emptyPrompt = this.renderEmptyPrompt(); + return ( + + + + + + + { + store.dispatch( + updateCurrentAgentData(this.props.agent), + ); + navigateAppURL( + `/app/configuration-assessment#/overview?tab=sca&redirectPolicy=${lastScan.policy_id}`, + ); this.router.reload(); - } - } - aria-label="Open SCA Scans" /> - - -
-
- {lastScan === undefined && emptyPrompt} - {loading} - {scaScan} -
-
- ) - } -}); + }} + > +

SCA: Lastest scans

+ + + + + + { + store.dispatch( + updateCurrentAgentData(this.props.agent), + ); + navigateAppURL('/app/configuration-assessment'); + this.router.reload(); + }} + aria-label='Open SCA Scans' + /> + + + + + {lastScan === undefined && emptyPrompt} + {loading} + {scaScan} + + + ); + } + }, +); diff --git a/plugins/main/public/components/common/welcome/overview-welcome.js b/plugins/main/public/components/common/welcome/overview-welcome.js index 578ec047e9..f09615632b 100644 --- a/plugins/main/public/components/common/welcome/overview-welcome.js +++ b/plugins/main/public/components/common/welcome/overview-welcome.js @@ -40,16 +40,14 @@ import { LogoOffice365, } from '../logos'; import { compose } from 'redux'; +import { getNavigationAppURL } from '../../../react-services/navigate-app'; export const OverviewWelcome = compose( withReduxProvider, withErrorBoundary, withGlobalBreadcrumb(props => { - return [ - { text: '' }, - { text: 'Overview' } - ]; - }) + return [{ text: '' }, { text: 'Overview' }]; + }), )( class OverviewWelcome extends Component { constructor(props) { @@ -87,7 +85,11 @@ export const OverviewWelcome = compose( title={ <> No agents were added to this manager.{' '} - + Add agent diff --git a/plugins/main/public/components/visualize/components/sample-data-warning.js b/plugins/main/public/components/visualize/components/sample-data-warning.js index c125a2d229..8bcb7fc191 100644 --- a/plugins/main/public/components/visualize/components/sample-data-warning.js +++ b/plugins/main/public/components/visualize/components/sample-data-warning.js @@ -15,6 +15,7 @@ import { UI_LOGGER_LEVELS } from '../../../../common/constants'; import React, { useState, useEffect } from 'react'; import { WzRequest } from '../../../react-services'; import { getErrorOrchestrator } from '../../../react-services/common-services'; +import { getNavigationAppURL } from '../../../react-services/navigate-app'; export const SampleDataWarning = ({ ...props }) => { const [isSampleData, setIsSampleData] = useState(false); @@ -22,8 +23,9 @@ export const SampleDataWarning = ({ ...props }) => { useEffect(() => { (async () => { try { - const result = (await WzRequest.genericReq('GET', '/elastic/samplealerts')).data - .sampleAlertsInstalled; + const result = ( + await WzRequest.genericReq('GET', '/elastic/samplealerts') + ).data.sampleAlertsInstalled; setIsSampleData(result); } catch (error) { const options = { @@ -50,16 +52,19 @@ export const SampleDataWarning = ({ ...props }) => { if (isSampleData) { return (

{'The data displayed may contain sample alerts. Go '} - + {'here '} {'to configure the sample data.'} diff --git a/plugins/main/public/components/visualize/components/sample-data-warning.test.js b/plugins/main/public/components/visualize/components/sample-data-warning.test.js index 77a201cbcd..670b4ed85a 100644 --- a/plugins/main/public/components/visualize/components/sample-data-warning.test.js +++ b/plugins/main/public/components/visualize/components/sample-data-warning.test.js @@ -18,25 +18,34 @@ import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/ import { UI_LOGGER_LEVELS } from '../../../../common/constants'; import React from 'react'; -const awaitForMyComponent = async (wrapper) => { +const awaitForMyComponent = async wrapper => { await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)); + await new Promise(resolve => setTimeout(resolve, 0)); wrapper.update(); }); }; jest.mock('../../../react-services'); jest.mock('../../../react-services/common-services'); +jest.mock('../../../react-services/navigate-app', () => ({ + getNavigationAppURL: url => url, +})); describe('Check sample data component', () => { it('should render if there is sample data', async () => { - WzRequest.genericReq.mockResolvedValue({ data: { sampleAlertsInstalled: true } }); + WzRequest.genericReq.mockResolvedValue({ + data: { sampleAlertsInstalled: true }, + }); const wrapper = await mount(); await awaitForMyComponent(wrapper); expect(wrapper.find('EuiCallOut').exists()); - expect(wrapper.find('EuiCallOut').props().title).toEqual("This dashboard contains sample data"); + expect(wrapper.find('EuiCallOut').props().title).toEqual( + 'This dashboard contains sample data', + ); }); it('should not render if there is no sample data', async () => { - WzRequest.genericReq.mockResolvedValue({ data: { sampleAlertsInstalled: false } }); + WzRequest.genericReq.mockResolvedValue({ + data: { sampleAlertsInstalled: false }, + }); const wrapper = await mount(); await awaitForMyComponent(wrapper); expect(wrapper.contains('EuiCallOut')).toBe(false); @@ -59,7 +68,7 @@ describe('Check sample data component', () => { }; getErrorOrchestrator.mockImplementation(() => { return { - handleError: (options) => { + handleError: options => { expect(options).toEqual(mockOptions); }, }; diff --git a/plugins/main/public/controllers/agent/components/agents-table.js b/plugins/main/public/controllers/agent/components/agents-table.js index cbb999b55b..801d028167 100644 --- a/plugins/main/public/controllers/agent/components/agents-table.js +++ b/plugins/main/public/controllers/agent/components/agents-table.js @@ -49,6 +49,7 @@ import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/ import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentStatus } from '../../../components/agents/agent-status'; import { AgentSynced } from '../../../components/agents/agent-synced'; +import { navigateAppURL } from '../../../react-services/navigate-app'; export const AgentsTable = withErrorBoundary( class AgentsTable extends Component { @@ -345,10 +346,9 @@ export const AgentsTable = withErrorBoundary( { ev.stopPropagation(); - AppNavigate.navigateToModule(ev, 'agents', { - tab: 'welcome', - agent: agent.id, - }); + navigateAppURL( + `/app/it-hygiene#/tab=welcome&agent=${agent.id}`, + ); }} iconType='eye' color={'primary'} @@ -754,11 +754,8 @@ export const AgentsTable = withErrorBoundary( } return { onClick: ev => { - AppNavigate.navigateToModule(ev, 'agents', { - tab: 'welcome', - agent: item.id, - }); ev.stopPropagation(); + navigateAppURL(`/app/it-hygiene#/tab=welcome&agent=${item.id}`); }, }; }; diff --git a/plugins/main/public/controllers/management/components/management/configuration/configuration-no-agent.js b/plugins/main/public/controllers/management/components/management/configuration/configuration-no-agent.js index 030df13845..5095087997 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/configuration-no-agent.js +++ b/plugins/main/public/controllers/management/components/management/configuration/configuration-no-agent.js @@ -13,23 +13,27 @@ import React, { Fragment } from 'react'; import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui'; import { webDocumentationLink } from '../../../../../../common/services/web_documentation'; +import { getNavigationAppURL } from '../../../../../react-services/navigate-app'; -const documentationLink = webDocumentationLink('user-manual/agents/agent-connection.html'); +const documentationLink = webDocumentationLink( + 'user-manual/agents/agent-connection.html', +); export const WzAgentNeverConnectedPrompt = () => ( Agent has never connected.} body={

- The agent has been registered but has not yet connected to the manager. + The agent has been registered but has not yet connected to the + manager.

Checking connection with the Wazuh server @@ -37,8 +41,13 @@ export const WzAgentNeverConnectedPrompt = () => ( } actions={ - + Back - } - />) + } + /> +); diff --git a/plugins/main/public/controllers/management/components/management/configuration/configuration-switch.js b/plugins/main/public/controllers/management/components/management/configuration/configuration-switch.js index 9d843f0626..d4142063b2 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/configuration-switch.js +++ b/plugins/main/public/controllers/management/components/management/configuration/configuration-switch.js @@ -44,7 +44,7 @@ import WzConfigurationAzureLogs from './azure-logs/azure-logs'; import WzConfigurationGoogleCloudPubSub from './google-cloud-pub-sub/google-cloud-pub-sub'; import { WzConfigurationGitHub } from './github/github'; import WzViewSelector, { - WzViewSelectorSwitch + WzViewSelectorSwitch, } from './util-components/view-selector'; import WzLoading from './util-components/loading'; import { withRenderIfOrWrapped } from './util-hocs/render-if'; @@ -66,13 +66,23 @@ import { import { connect } from 'react-redux'; import { compose } from 'redux'; -import { EuiPage, EuiPanel, EuiSpacer, EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; +import { + EuiPage, + EuiPanel, + EuiSpacer, + EuiButtonEmpty, + EuiFlexItem, +} from '@elastic/eui'; import { WzRequest } from '../../../../../react-services/wz-request'; -import { API_NAME_AGENT_STATUS, UI_LOGGER_LEVELS } from '../../../../../../common/constants'; +import { + API_NAME_AGENT_STATUS, + UI_LOGGER_LEVELS, +} from '../../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../../react-services/common-services'; import { WzConfigurationOffice365 } from './office365/office365'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; class WzConfigurationSwitch extends Component { constructor(props) { @@ -92,7 +102,7 @@ class WzConfigurationSwitch extends Component { updateConfigurationSection = (view, title, description) => { this.setState({ view, viewProps: { title: title, description } }); }; - updateBadge = (badgeStatus) => { + updateBadge = badgeStatus => { // default value false? this.setState({ viewProps: { ...this.state.viewProps, badge: badgeStatus }, @@ -157,9 +167,13 @@ class WzConfigurationSwitch extends Component { // If manager/cluster require agent platform info to filter sections in overview. It isn't coming from props for Management/Configuration try { this.setState({ loadingOverview: true }); - const masterNodeInfo = await WzRequest.apiReq('GET', '/agents?agents_list=000', {}); + const masterNodeInfo = await WzRequest.apiReq( + 'GET', + '/agents?agents_list=000', + {}, + ); this.setState({ - masterNodeInfo: masterNodeInfo.data.data.affected_items[0] + masterNodeInfo: masterNodeInfo.data.data.affected_items[0], }); this.setState({ loadingOverview: false }); } catch (error) { @@ -185,7 +199,7 @@ class WzConfigurationSwitch extends Component { agentSynchronized, masterNodeInfo, } = this.state; - const { agent, goGroups } = this.props; // TODO: goGroups and exportConfiguration is used for Manager and depends of AngularJS + const { agent } = this.props; // TODO: goGroups and exportConfiguration is used for Manager and depends of AngularJS return ( @@ -193,11 +207,18 @@ class WzConfigurationSwitch extends Component { Groups: {agent.group.map((group, key) => ( - goGroups(agent, key)}> + { + navigateAppURL( + `/app/endpoint-groups#/manager/?tab=groups&group=${group}`, + ); + }} + > {group} ))} - + ) : null} {view !== '' && view !== 'edit-configuration' && ( @@ -232,28 +253,28 @@ class WzConfigurationSwitch extends Component { )} {view !== '' && ( - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + ({ +const mapStateToProps = state => ({ clusterNodes: state.configurationReducers.clusterNodes, clusterNodeSelected: state.configurationReducers.clusterNodeSelected, wazuhNotReadyYet: state.appStateReducers.wazuhNotReadyYet, }); -const mapDispatchToProps = (dispatch) => ({ - updateClusterNodes: (clusterNodes) => dispatch(updateClusterNodes(clusterNodes)), - updateClusterNodeSelected: (clusterNodeSelected) => +const mapDispatchToProps = dispatch => ({ + updateClusterNodes: clusterNodes => + dispatch(updateClusterNodes(clusterNodes)), + updateClusterNodeSelected: clusterNodeSelected => dispatch(updateClusterNodeSelected(clusterNodeSelected)), }); export default compose( - withUserAuthorizationPrompt((props) => [props.agent.id === '000' ? - {action: 'manager:read', resource: '*:*:*'} : - [ - {action: 'agent:read', resource: `agent:id:${props.agent.id}`}, - ...(props.agent.group || []).map(group => ({ action: 'agent:read', resource: `agent:group:${group}` })) - ]]), //TODO: this need cluster:read permission but manager/cluster is managed in WzConfigurationSwitch component - withRenderIfOrWrapped((props) => props.agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED, WzAgentNeverConnectedPrompt), - connect( - mapStateToProps, - mapDispatchToProps -))(WzConfigurationSwitch); + withUserAuthorizationPrompt(props => [ + props.agent.id === '000' + ? { action: 'manager:read', resource: '*:*:*' } + : [ + { action: 'agent:read', resource: `agent:id:${props.agent.id}` }, + ...(props.agent.group || []).map(group => ({ + action: 'agent:read', + resource: `agent:group:${group}`, + })), + ], + ]), //TODO: this need cluster:read permission but manager/cluster is managed in WzConfigurationSwitch component + withRenderIfOrWrapped( + props => props.agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED, + WzAgentNeverConnectedPrompt, + ), + connect(mapStateToProps, mapDispatchToProps), +)(WzConfigurationSwitch); diff --git a/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js b/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js index e7c4a11aba..1c5ba47837 100644 --- a/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js +++ b/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js @@ -36,6 +36,7 @@ import { } from '../../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../../react-services/common-services'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; class WzGroupAgentsTable extends Component { _isMounted = false; @@ -208,7 +209,7 @@ class WzGroupAgentsTable extends Component { aria-label='Go to the agent' iconType='eye' onClick={async () => { - this.props.groupsProps.showAgent(item); + navigateAppURL(`/app/it-hygiene#/agents?agent=${item.id}`); }} color='primary' /> diff --git a/plugins/main/public/controllers/management/components/management/ruleset/views/rule-info.tsx b/plugins/main/public/controllers/management/components/management/ruleset/views/rule-info.tsx index 3c95a77f3f..e02f9a0b09 100644 --- a/plugins/main/public/controllers/management/components/management/ruleset/views/rule-info.tsx +++ b/plugins/main/public/controllers/management/components/management/ruleset/views/rule-info.tsx @@ -18,12 +18,16 @@ import { import { WzRequest } from '../../../../../../react-services/wz-request'; -import { ResourcesHandler, ResourcesConstants } from '../../common/resources-handler'; +import { + ResourcesHandler, + ResourcesConstants, +} from '../../common/resources-handler'; import WzTextWithTooltipTruncated from '../../../../../../components/common/wz-text-with-tooltip-if-truncated'; import { UI_ERROR_SEVERITIES } from '../../../../../../react-services/error-orchestrator/types'; import { UI_LOGGER_LEVELS } from '../../../../../../../common/constants'; import { TableWzAPI } from '../../../../../../components/common/tables'; import { getErrorOrchestrator } from '../../../../../../react-services/common-services'; +import { getNavigationAppURL } from '../../../../../../react-services/navigate-app'; export default class WzRuleInfo extends Component { constructor(props) { @@ -46,7 +50,7 @@ export default class WzRuleInfo extends Component { mitreRuleId: '', mitreIds: [], currentRuleInfo: {}, - isLoading: true + isLoading: true, }; this.resourcesHandler = new ResourcesHandler(ResourcesConstants.RULES); @@ -95,7 +99,10 @@ export default class WzRuleInfo extends Component { let result = value.match(regex); if (result !== null) { for (const oldValue of result) { - let newValue = oldValue.replace('$(', ``); + let newValue = oldValue.replace( + '$(', + ``, + ); newValue = newValue.replace(')', ' '); value = value.replace(oldValue, newValue); } @@ -133,8 +140,10 @@ export default class WzRuleInfo extends Component { width: '15%', render: (value, item) => { return ( - - handleFileClick(event, item)}>{value} + + handleFileClick(event, item)}> + {value} + ); }, @@ -155,7 +164,7 @@ export default class WzRuleInfo extends Component { async componentDidUpdate(prevProps, prevState) { if (prevState.currentRuleId !== this.state.currentRuleId) - await this.loadRule() + await this.loadRule(); } async loadRule() { @@ -167,7 +176,9 @@ export default class WzRuleInfo extends Component { rule_ids: currentRuleId, }, }); - const currentRule = result?.data?.affected_items?.length ? result.data.affected_items[0] : {}; + const currentRule = result?.data?.affected_items?.length + ? result.data.affected_items[0] + : {}; const compliance = this.buildCompliance(currentRule); if (compliance?.mitre?.length && currentRuleId !== mitreRuleId) { @@ -179,14 +190,14 @@ export default class WzRuleInfo extends Component { mitreIds: [], mitreTactics: [], mitreTechniques: [], - } + }; } this.setState({ currentRuleInfo: currentRule, compliance: compliance, isLoading: false, - ...mitreState + ...mitreState, }); } catch (error) { const options = { @@ -201,7 +212,6 @@ export default class WzRuleInfo extends Component { }; getErrorOrchestrator().handleError(options); } - } /** * Build an object with the compliance info about a rule @@ -209,25 +219,45 @@ export default class WzRuleInfo extends Component { */ buildCompliance(ruleInfo) { const compliance = {}; - const complianceKeys = ['gdpr', 'gpg13', 'hipaa', 'nist-800-53', 'pci', 'tsc', 'mitre']; - Object.keys(ruleInfo).forEach((key) => { - if (complianceKeys.includes(key) && ruleInfo[key].length) compliance[key] = ruleInfo[key]; + const complianceKeys = [ + 'gdpr', + 'gpg13', + 'hipaa', + 'nist-800-53', + 'pci', + 'tsc', + 'mitre', + ]; + Object.keys(ruleInfo).forEach(key => { + if (complianceKeys.includes(key) && ruleInfo[key].length) + compliance[key] = ruleInfo[key]; }); return compliance || {}; } buildComplianceBadges(item) { const badgeList = []; - const fields = ['pci_dss', 'gpg13', 'hipaa', 'gdpr', 'nist_800_53', 'tsc', 'mitre']; - const buildBadge = (field) => { - + const fields = [ + 'pci_dss', + 'gpg13', + 'hipaa', + 'gdpr', + 'nist_800_53', + 'tsc', + 'mitre', + ]; + const buildBadge = field => { return ( - + ev.stopPropagation()} + onClick={ev => ev.stopPropagation()} onClickAriaLabel={field.toUpperCase()} - color="hollow" + color='hollow' style={{ margin: '1px 2px' }} > {field.toUpperCase()} @@ -250,7 +280,7 @@ export default class WzRuleInfo extends Component { error: error, message: error.message || error, title: error.name || error, - } + }, }; getErrorOrchestrator().handleError(options); } @@ -262,9 +292,10 @@ export default class WzRuleInfo extends Component { * Clean the existing filters and sets the new ones and back to the previous section */ setNewFiltersAndBack(filters) { - window.history.pushState("", + window.history.pushState( + '', window.document.title, - window.location.href.replace(new RegExp('&redirectRule=' + '[^&]*'), '') + window.location.href.replace(new RegExp('&redirectRule=' + '[^&]*'), ''), ); this.props.cleanFilters(); this.props.onFiltersChange(filters); @@ -281,37 +312,49 @@ export default class WzRuleInfo extends Component { renderInfo(id = '', level = '', file = '', path = '', groups = []) { return ( - + ID - + this.setNewFiltersAndBack([{ field: 'rule_ids', value: id }])} + onClick={async () => + this.setNewFiltersAndBack([{ field: 'rule_ids', value: id }]) + } > {id} - + Level - + this.setNewFiltersAndBack([{ field: 'level', value: level }])} + onClick={async () => + this.setNewFiltersAndBack([{ field: 'level', value: level }]) + } > {level} - + File - + - this.setNewFiltersAndBack([{ field: 'filename', value: file }]) + this.setNewFiltersAndBack([ + { field: 'filename', value: file }, + ]) } > {file} @@ -319,13 +362,15 @@ export default class WzRuleInfo extends Component { - + Path - + - this.setNewFiltersAndBack([{ field: 'relative_dirname', value: path }]) + this.setNewFiltersAndBack([ + { field: 'relative_dirname', value: path }, + ]) } > {path} @@ -333,11 +378,11 @@ export default class WzRuleInfo extends Component { - + Groups {this.renderGroups(groups)} - + ); } @@ -347,16 +392,16 @@ export default class WzRuleInfo extends Component { let link = ''; let name = ''; - value.forEach((item) => { - if (item.type === 'cve'){ + value.forEach(item => { + if (item.type === 'cve') { name = item.name; } - if (item.type === 'link'){ + if (item.type === 'link') { link = ( {item.name} @@ -369,7 +414,11 @@ export default class WzRuleInfo extends Component { {name}: {link} ); - } else if (value && typeof value === 'object' && value.constructor === Object) { + } else if ( + value && + typeof value === 'object' && + value.constructor === Object + ) { let list = []; Object.keys(value).forEach((key, idx) => { list.push( @@ -378,7 +427,7 @@ export default class WzRuleInfo extends Component { {value[key]} {idx < Object.keys(value).length - 1 && ', '}
- + , ); }); return ( @@ -387,7 +436,11 @@ export default class WzRuleInfo extends Component { ); } else { - return {value}; + return ( + + {value} + + ); } } @@ -400,13 +453,21 @@ export default class WzRuleInfo extends Component { // Exclude group key of details Object.keys(details) - .filter((key) => key !== 'group') - .forEach((key) => { + .filter(key => key !== 'group') + .forEach(key => { detailsToRender.push( - - {key} - {details[key] === '' ? 'true' : this.getFormattedDetails(details[key])} - + + + {key} + + {details[key] === '' + ? 'true' + : this.getFormattedDetails(details[key])} + , ); }); return {detailsToRender}; @@ -422,14 +483,19 @@ export default class WzRuleInfo extends Component { listGroups.push( this.setNewFiltersAndBack([{ field: 'group', value: group }])} + onClick={async () => + this.setNewFiltersAndBack([{ field: 'group', value: group }]) + } > - + {group} {index < groups.length - 1 && ', '} - + , ); }); return ( @@ -447,9 +513,10 @@ export default class WzRuleInfo extends Component { tactic_ids: tactics.toString(), }, }); - const formattedData = ((data || {}).data.data || {}).affected_items || [] || {}; + const formattedData = + ((data || {}).data.data || {}).affected_items || [] || {}; formattedData && - formattedData.forEach((item) => { + formattedData.forEach(item => { tacticsObj.push(item.name); }); return tacticsObj; @@ -465,21 +532,23 @@ export default class WzRuleInfo extends Component { const mitreName = []; const mitreIds = []; const mitreTactics = await Promise.all( - compliance.map(async (i) => { + compliance.map(async i => { const data = await WzRequest.apiReq('GET', '/mitre/techniques', { params: { q: `external_id=${i}`, }, }); - const formattedData = (((data || {}).data.data || {}).affected_items || [])[0] || {}; + const formattedData = + (((data || {}).data.data || {}).affected_items || [])[0] || {}; const tactics = this.getTacticsNames(formattedData.tactics) || []; mitreName.push(formattedData.name); mitreIds.push(i); return tactics; - }) + }), ); if (mitreTactics.length) { - let removeDuplicates = (arr) => arr.filter((v, i) => arr.indexOf(v) === i); + let removeDuplicates = arr => + arr.filter((v, i) => arr.indexOf(v) === i); const uniqueTactics = removeDuplicates(mitreTactics.flat()); Object.assign(newMitreState, { mitreRuleId: currentRuleId, @@ -515,8 +584,6 @@ export default class WzRuleInfo extends Component { ? this.state.currentRuleId : this.props.state.ruleInfo.current; - - const listCompliance = []; if (compliance.mitre) delete compliance.mitre; const keys = Object.keys(compliance); @@ -527,9 +594,11 @@ export default class WzRuleInfo extends Component { return ( this.setNewFiltersAndBack([{ field: key, value: element }])} + onClick={async () => + this.setNewFiltersAndBack([{ field: key, value: element }]) + } > - + {element} @@ -539,11 +608,15 @@ export default class WzRuleInfo extends Component { }); listCompliance.push( - + {this.complianceEquivalences[key]}

{values}

- -
+ +
, ); } @@ -553,10 +626,12 @@ export default class WzRuleInfo extends Component { - this.setNewFiltersAndBack([{ field: 'mitre', value: this.state.mitreIds[index] }]) + this.setNewFiltersAndBack([ + { field: 'mitre', value: this.state.mitreIds[index] }, + ]) } > - + {element} @@ -565,21 +640,35 @@ export default class WzRuleInfo extends Component { ); }); listCompliance.push( - - {this.complianceEquivalences['mitreTechniques']} - {(this.state.mitreLoading && ) ||

{values}

} - -
+ + + {this.complianceEquivalences['mitreTechniques']} + + {(this.state.mitreLoading && ) || ( +

{values}

+ )} + +
, ); } if (this.state.mitreTactics && this.state.mitreTactics.length) { listCompliance.push( - - {this.complianceEquivalences['mitreTactics']} + + + {this.complianceEquivalences['mitreTactics']} +

{this.state.mitreTactics.toString()}

- -
+ +
, ); } @@ -598,7 +687,7 @@ export default class WzRuleInfo extends Component { window.location.href = window.location.href.replace( new RegExp('redirectRule=' + '[^&]*'), - `redirectRule=${ruleId}` + `redirectRule=${ruleId}`, ); this.setState({ currentRuleId: ruleId, isLoading: true }); } @@ -621,7 +710,7 @@ export default class WzRuleInfo extends Component { return value; } - onClickRow = (item) => { + onClickRow = item => { return { onClick: () => { this.changeBetweenRules(item.id); @@ -630,38 +719,49 @@ export default class WzRuleInfo extends Component { }; render() { - const { description, details, filename, relative_dirname, level, id, groups } = this.state.currentRuleInfo; + const { + description, + details, + filename, + relative_dirname, + level, + id, + groups, + } = this.state.currentRuleInfo; const compliance = this.buildCompliance(this.state.currentRuleInfo); return ( <> - + - { - description && ( - ) - } + {description && ( + + )} View alerts of this Rule - +
- + {/* Cards */} @@ -669,19 +769,25 @@ export default class WzRuleInfo extends Component { {/* General info */} +

Information

} - paddingSize="none" + paddingSize='none' initialIsOpen={true} isLoading={this.state.isLoading} isLoadingMessage={''} > - - {this.renderInfo(id, level, filename, relative_dirname, groups)} + + {this.renderInfo( + id, + level, + filename, + relative_dirname, + groups, + )}
@@ -690,18 +796,20 @@ export default class WzRuleInfo extends Component { +

Details

} - paddingSize="none" + paddingSize='none' initialIsOpen={true} isLoading={this.state.isLoading} isLoadingMessage={''} > - {this.renderDetails(details)} + + {this.renderDetails(details)} +
@@ -710,18 +818,18 @@ export default class WzRuleInfo extends Component { +

Compliance

} - paddingSize="none" + paddingSize='none' initialIsOpen={true} isLoading={this.state.isLoading} isLoadingMessage={''} > - + {this.renderCompliance(compliance)}
@@ -732,29 +840,32 @@ export default class WzRuleInfo extends Component { +

Related rules

} isLoading={this.state.isLoading} isLoadingMessage={''} - paddingSize="none" + paddingSize='none' initialIsOpen={true} > - + - {this.state.currentRuleInfo?.filename && + {this.state.currentRuleInfo?.filename && ( - } + )} diff --git a/plugins/main/public/controllers/management/components/management/statistics/statistics-overview.js b/plugins/main/public/controllers/management/components/management/statistics/statistics-overview.js index 3742b4265e..d571d73474 100644 --- a/plugins/main/public/controllers/management/components/management/statistics/statistics-overview.js +++ b/plugins/main/public/controllers/management/components/management/statistics/statistics-overview.js @@ -10,7 +10,7 @@ * * Find more information about this on the LICENSE file. */ -import React, { Component, useState, useEffect } from "react"; +import React, { Component, useState, useEffect } from 'react'; import { EuiFlexItem, EuiFlexGroup, @@ -24,23 +24,27 @@ import { EuiTab, EuiSpacer, EuiSelect, - EuiProgress -} from "@elastic/eui"; + EuiProgress, +} from '@elastic/eui'; -import { clusterNodes } from "../configuration/utils/wz-fetch"; -import { WzStatisticsRemoted } from "./statistics-dashboard-remoted"; -import { WzStatisticsAnalysisd } from "./statistics-dashboard-analysisd"; -import { WzDatePicker } from "../../../../../components/wz-date-picker/wz-date-picker"; -import { AppNavigate } from "../../../../../react-services/app-navigate"; +import { clusterNodes } from '../configuration/utils/wz-fetch'; +import { WzStatisticsRemoted } from './statistics-dashboard-remoted'; +import { WzStatisticsAnalysisd } from './statistics-dashboard-analysisd'; +import { WzDatePicker } from '../../../../../components/wz-date-picker/wz-date-picker'; +import { AppNavigate } from '../../../../../react-services/app-navigate'; import { compose } from 'redux'; -import { withGuard, withGlobalBreadcrumb } from "../../../../../components/common/hocs"; +import { + withGuard, + withGlobalBreadcrumb, +} from '../../../../../components/common/hocs'; import { PromptStatisticsDisabled } from './prompt-statistics-disabled'; import { PromptStatisticsNoIndices } from './prompt-statistics-no-indices'; -import { WazuhConfig } from "../../../../../react-services/wazuh-config"; +import { WazuhConfig } from '../../../../../react-services/wazuh-config'; import { WzRequest } from '../../../../../react-services/wz-request'; import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types'; import { UI_LOGGER_LEVELS } from '../../../../../../common/constants'; import { getErrorOrchestrator } from '../../../../../react-services/common-services'; +import { navigateAppURL } from '../../../../../react-services/navigate-app'; const wzConfig = new WazuhConfig(); export class WzStatisticsOverview extends Component { @@ -48,28 +52,28 @@ export class WzStatisticsOverview extends Component { constructor(props) { super(props); this.state = { - selectedTabId: "remoted", + selectedTabId: 'remoted', stats: {}, isLoading: false, loadingNode: false, - searchvalue: "", + searchvalue: '', clusterNodeSelected: 'all', - refreshVisualizations: Date.now() + refreshVisualizations: Date.now(), }; this.tabs = [ { - id: "remoted", - name: "Listener Engine", + id: 'remoted', + name: 'Listener Engine', }, { - id: "analysisd", - name: "Analysis Engine", + id: 'analysisd', + name: 'Analysis Engine', }, ]; this.info = { remoted: - "Remoted statistics are cumulative, this means that the information shown is since the data exists.", + 'Remoted statistics are cumulative, this means that the information shown is since the data exists.', analysisd: "Analysisd statistics refer to the data stored from the period indicated in the variable 'analysisd.state_interval'.", }; @@ -79,10 +83,10 @@ export class WzStatisticsOverview extends Component { this._isMounted = true; try { const data = await clusterNodes(); - const nodes = data.data.data.affected_items.map((item) => { + const nodes = data.data.data.affected_items.map(item => { return { value: item.name, text: `${item.name} (${item.type})` }; }); - nodes.unshift({ value: 'all', text: 'All' }) + nodes.unshift({ value: 'all', text: 'All' }); this.setState({ clusterNodes: nodes, clusterNodeSelected: nodes[0].value, @@ -111,13 +115,11 @@ export class WzStatisticsOverview extends Component { this._isMounted = false; } - onSelectedTabChanged = (id) => { - this.setState( - { - selectedTabId: id, - searchvalue: "", - } - ); + onSelectedTabChanged = id => { + this.setState({ + selectedTabId: id, + searchvalue: '', + }); }; renderTabs() { @@ -132,21 +134,21 @@ export class WzStatisticsOverview extends Component { )); } - onSelectNode = (e) => { + onSelectNode = e => { const newValue = e.target.value; this.setState( { - loadingNode: true + loadingNode: true, }, () => { - this.setState({ clusterNodeSelected: newValue, loadingNode: false }) - } + this.setState({ clusterNodeSelected: newValue, loadingNode: false }); + }, ); }; refreshVisualizations = () => { - this.setState({ refreshVisualizations: Date.now() }) - } + this.setState({ refreshVisualizations: Date.now() }); + }; render() { const search = { @@ -156,7 +158,7 @@ export class WzStatisticsOverview extends Component { }, }; return ( - + @@ -170,7 +172,7 @@ export class WzStatisticsOverview extends Component { Refresh @@ -181,31 +183,36 @@ export class WzStatisticsOverview extends Component { this.state.clusterNodes.length && this.state.clusterNodeSelected ) && ( - - - - )} + + + + )} - { }} /> + {}} /> AppNavigate.navigateToModule(e, 'settings', { tab: 'configuration', category: 'statistics' })} - iconType="gear" - iconSide="left" > + onMouseDown={e => + navigateAppURL( + '/app/wazuh-plugin-settings#/settings?tab=configuration&category=statistics', + ) + } + iconType='gear' + iconSide='left' + > Settings - + From here you can see daemon statistics. @@ -215,33 +222,41 @@ export class WzStatisticsOverview extends Component { {this.renderTabs()}
- - {( + + { <> - {this.state.selectedTabId === "remoted" && !this.state.loadingNode && ( -
- - - - -
- )} - {this.state.selectedTabId === "analysisd" && !this.state.loadingNode && ( -
- - - - -
- )} + {this.state.selectedTabId === 'remoted' && + !this.state.loadingNode && ( +
+ + + + +
+ )} + {this.state.selectedTabId === 'analysisd' && + !this.state.loadingNode && ( +
+ + + + +
+ )} - )} + } ); @@ -249,45 +264,44 @@ export class WzStatisticsOverview extends Component { } export default compose( - withGlobalBreadcrumb([ - { text: '' }, - { text: 'Statistics' } - ]), + withGlobalBreadcrumb([{ text: '' }, { text: 'Statistics' }]), withGuard(props => { - return !((wzConfig.getConfig() || {})['cron.statistics.status']); // if 'cron.statistics.status' is false, then it renders PromptStatisticsDisabled component - }, PromptStatisticsDisabled) -)( - props => { - const [loading, setLoading] = useState(false); - const [existStatisticsIndices, setExistStatisticsIndices] = useState(false); - useEffect(() => { - const fetchData = async () => { - try{ - setLoading(true); - const data = await WzRequest.genericReq('GET', '/elastic/statistics'); - setExistStatisticsIndices(data.data); - }catch(error){ - setLoading(false); - const options = { - context: `${WzStatisticsOverview.name}.fetchData`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - error: { - error: error, - message: error.message || error, - title: `${error.name}: Error when fetching data` - }, - }; - getErrorOrchestrator().handleError(options); - } + return !(wzConfig.getConfig() || {})['cron.statistics.status']; // if 'cron.statistics.status' is false, then it renders PromptStatisticsDisabled component + }, PromptStatisticsDisabled), +)(props => { + const [loading, setLoading] = useState(false); + const [existStatisticsIndices, setExistStatisticsIndices] = useState(false); + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const data = await WzRequest.genericReq('GET', '/elastic/statistics'); + setExistStatisticsIndices(data.data); + } catch (error) { setLoading(false); - }; + const options = { + context: `${WzStatisticsOverview.name}.fetchData`, + level: UI_LOGGER_LEVELS.ERROR, + severity: UI_ERROR_SEVERITIES.BUSINESS, + error: { + error: error, + message: error.message || error, + title: `${error.name}: Error when fetching data`, + }, + }; + getErrorOrchestrator().handleError(options); + } + setLoading(false); + }; - fetchData(); - }, []); - if(loading){ - return - } - return existStatisticsIndices ? : + fetchData(); + }, []); + if (loading) { + return ; } -); + return existStatisticsIndices ? ( + + ) : ( + + ); +}); diff --git a/plugins/main/public/controllers/management/groups.js b/plugins/main/public/controllers/management/groups.js index f815d90739..caf3bf29f1 100644 --- a/plugins/main/public/controllers/management/groups.js +++ b/plugins/main/public/controllers/management/groups.js @@ -37,7 +37,7 @@ export class GroupsController { // Listeners // Resetting the factory configuration - this.scope.$on('$destroy', () => { }); + this.scope.$on('$destroy', () => {}); this.scope.$watch('lookingGroup', value => { this.addingAgents = false; @@ -51,11 +51,11 @@ export class GroupsController { this.exportConfigurationProps = { exportConfiguration: enabledComponents => this.exportConfiguration(enabledComponents), - type: 'group' + type: 'group', }; this.filesInGroupTableProps = { - exportConfigurationProps: this.exportConfigurationProps + exportConfigurationProps: this.exportConfigurationProps, }; return; @@ -72,15 +72,18 @@ export class GroupsController { // If come from agents // Store a boolean variable to check if come from agents this.globalAgent = this.shareAgent.getAgent(); - if (this.globalAgent) { - const globalGroup = this.shareAgent.getSelectedGroup(); + if (this.globalAgent || this.location.search()?.group) { + const globalGroup = + this.shareAgent.getSelectedGroup() || this.location.search().group; // Get ALL groups const data = await WzRequest.apiReq('GET', '/groups', {}); - const filtered = data.data.data.affected_items.filter(group => group.name === globalGroup); + const filtered = data.data.data.affected_items.filter( + group => group.name === globalGroup, + ); if (Array.isArray(filtered) && filtered.length) { // Load that our group this.buildGroupsTableProps(data.data.data.items, { - group: filtered[0] + group: filtered[0], }); } else { throw Error(`Group ${globalGroup} not found`); @@ -121,7 +124,7 @@ export class GroupsController { this.reportingService.startConfigReport( group, 'groupConfig', - enabledComponents + enabledComponents, ); } @@ -145,7 +148,7 @@ export class GroupsController { exportConfigurationProps: { exportConfiguration: (enabledComponents, group) => this.exportConfiguration(enabledComponents, group), - type: 'group' + type: 'group', }, currentGroup: group => { this.currentGroup = group; @@ -156,7 +159,7 @@ export class GroupsController { showAgent: agent => { this.showAgent(agent); }, - selectedGroup: this.redirectGroup + selectedGroup: this.redirectGroup, }; this.mctrl.managementProps.groupsProps = this.groupsTableProps; } diff --git a/plugins/main/public/controllers/management/monitoring.js b/plugins/main/public/controllers/management/monitoring.js index 79cb2593ff..e182bdfb48 100644 --- a/plugins/main/public/controllers/management/monitoring.js +++ b/plugins/main/public/controllers/management/monitoring.js @@ -20,6 +20,7 @@ import { updateGlobalBreadcrumb } from '../../redux/actions/globalBreadcrumbActi import { ModulesHelper } from '../../components/common/modules/modules-helper'; import { WAZUH_ROLE_ADMINISTRATOR_NAME } from '../../../common/constants'; import { getDataPlugin } from '../../kibana-services'; +import { navigateAppURL } from '../../react-services/navigate-app'; export function ClusterController( $scope, @@ -31,10 +32,13 @@ export function ClusterController( discoverPendingUpdates, rawVisualizations, loadedVisualizations, - visHandlers + visHandlers, ) { const tabVisualizations = new TabVisualizations(); - getDataPlugin().query.timefilter.timefilter.setRefreshInterval({ pause: true, value: 0 }); + getDataPlugin().query.timefilter.timefilter.setRefreshInterval({ + pause: true, + value: 0, + }); $scope.search = term => { $scope.$broadcast('wazuhSearch', { term }); }; @@ -54,7 +58,7 @@ export function ClusterController( loadedVisualizations.removeAll(); tabVisualizations.setTab('monitoring'); tabVisualizations.assign({ - monitoring: 2 + monitoring: 2, }); $scope.loading = true; @@ -77,7 +81,7 @@ export function ClusterController( * This navigates to agents preview */ $scope.goAgents = () => { - $window.location.href = '#/agents-preview'; + navigateAppURL('/app/endpoints-summary#/agents-preview/?'); }; /** @@ -86,7 +90,7 @@ export function ClusterController( $scope.goConfiguration = () => { setBooleans('showConfig'); tabVisualizations.assign({ - monitoring: 1 + monitoring: 1, }); assignFilters(); $rootScope.$broadcast('updateVis'); @@ -98,10 +102,10 @@ export function ClusterController( $scope.goNodes = () => { setBooleans('showNodes'); tabVisualizations.assign({ - monitoring: 1 + monitoring: 1, }); assignFilters(); - $scope.nodeProps = { goBack: () => $scope.goBack() } + $scope.nodeProps = { goBack: () => $scope.goBack() }; $rootScope.$broadcast('updateVis'); }; @@ -111,7 +115,7 @@ export function ClusterController( $scope.goBack = () => { setBooleans(null); tabVisualizations.assign({ - monitoring: 2 + monitoring: 2, }); assignFilters(); $rootScope.$broadcast('updateVis'); @@ -121,15 +125,14 @@ export function ClusterController( $scope.$on('wazuhShowClusterNode', async (event, parameters) => { try { tabVisualizations.assign({ - monitoring: 1 + monitoring: 1, }); $scope.currentNode = parameters.node; const data = await WzRequest.apiReq('GET', '/cluster/healthcheck', { - node: $scope.currentNode.name + node: $scope.currentNode.name, }); - $scope.currentNode.healthCheck = - data.data.data.affected_items[0]; + $scope.currentNode.healthCheck = data.data.data.affected_items[0]; if ( $scope.currentNode.healthCheck && @@ -149,14 +152,14 @@ export function ClusterController( .date_end_master !== 'n/a' ) { const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_end_master + $scope.currentNode.healthCheck.status.last_sync_integrity.date_end_master, ); const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_start_master + $scope.currentNode.healthCheck.status.last_sync_integrity.date_start_master, ); - $scope.currentNode.healthCheck.status.last_sync_integrity.duration = `${(end - - start) / - 1000}s`; + $scope.currentNode.healthCheck.status.last_sync_integrity.duration = `${ + (end - start) / 1000 + }s`; } if ( @@ -166,14 +169,14 @@ export function ClusterController( .date_end_master !== 'n/a' ) { const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_end_master + $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_end_master, ); const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_start_master + $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_start_master, ); - $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = `${(end - - start) / - 1000}s`; + $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = `${ + (end - start) / 1000 + }s`; } if ( @@ -183,14 +186,14 @@ export function ClusterController( .date_end_master !== 'n/a' ) { const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_end_master + $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_end_master, ); const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_start_master + $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_start_master, ); - $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = `${(end - - start) / - 1000}s`; + $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = `${ + (end - start) / 1000 + }s`; } } @@ -212,7 +215,7 @@ export function ClusterController( try { filters = []; filters.push( - filterHandler.managerQuery(AppState.getClusterInfo().cluster, true) + filterHandler.managerQuery(AppState.getClusterInfo().cluster, true), ); if (node) { filters.push(filterHandler.nodeQuery(node)); @@ -223,7 +226,7 @@ export function ClusterController( ErrorHandler.handle( 'An error occurred while creating custom filters for visualizations', 'Cluster', - { warning: true } + { warning: true }, ); } }; @@ -234,16 +237,15 @@ export function ClusterController( $scope.authorized = true; return status; } catch (error) { - if(error === '3013 - Permission denied: Resource type: *:*') - $scope.authorized = false + if (error === '3013 - Permission denied: Resource type: *:*') + $scope.authorized = false; } - } + }; /** * This set some required settings at init */ const load = async () => { - try { visHandlers.removeAll(); discoverPendingUpdates.removeAll(); @@ -251,9 +253,9 @@ export function ClusterController( loadedVisualizations.removeAll(); const status = await clusterStatus(); if (!status) { - $scope.permissions = [{action: 'cluster:status', resource: "*:*:*"}]; + $scope.permissions = [{ action: 'cluster:status', resource: '*:*:*' }]; $scope.loading = false; - $scope.$applyAsync() + $scope.$applyAsync(); return; } $scope.status = status.data.data.running; @@ -268,14 +270,14 @@ export function ClusterController( WzRequest.apiReq('GET', '/cluster/local/config', {}), WzRequest.apiReq('GET', '/', {}), WzRequest.apiReq('GET', '/agents', { limit: 1 }), - WzRequest.apiReq('GET', '/cluster/healthcheck', {}) + WzRequest.apiReq('GET', '/cluster/healthcheck', {}), ]); - const nodeList = (((data[0] || {}).data || {}).data || {}) || false; - const clusterConfig = ((((data[1] || {}).data || {}).data || {}) || false); - const version = (((data[2] || {}).data || {}).data || {}).api_version || false; - const agents = ((((data[3] || {}).data || {}).data || {}) || false); - + const nodeList = ((data[0] || {}).data || {}).data || {} || false; + const clusterConfig = ((data[1] || {}).data || {}).data || {} || false; + const version = + (((data[2] || {}).data || {}).data || {}).api_version || false; + const agents = ((data[3] || {}).data || {}).data || {} || false; $scope.nodesCount = nodeList.total_affected_items; $scope.configuration = clusterConfig.affected_items[0]; @@ -284,12 +286,14 @@ export function ClusterController( nodeList.name = $scope.configuration.name; nodeList.master_node = $scope.configuration.node_name; - const {id, title} = await getDataPlugin().indexPatterns.get(AppState.getCurrentPattern()); + const { id, title } = await getDataPlugin().indexPatterns.get( + AppState.getCurrentPattern(), + ); const visData = await GenericRequest.request( 'POST', `/elastic/visualizations/cluster-monitoring/${AppState.getCurrentPattern()}`, - { nodes: nodeList, pattern: {id, title} } + { nodes: nodeList, pattern: { id, title } }, ); rawVisualizations.assignItems(visData.data.raw); @@ -317,10 +321,7 @@ export function ClusterController( $scope.expandArray = [false, false]; - const breadcrumb = [ - { text: '' }, - { text: 'Cluster' } - ]; + const breadcrumb = [{ text: '' }, { text: 'Cluster' }]; store.dispatch(updateGlobalBreadcrumb(breadcrumb)); if (clusterEnabled) load(); diff --git a/plugins/main/public/controllers/overview/components/stats.js b/plugins/main/public/controllers/overview/components/stats.js index a9b1786250..faab694138 100644 --- a/plugins/main/public/controllers/overview/components/stats.js +++ b/plugins/main/public/controllers/overview/components/stats.js @@ -12,80 +12,101 @@ */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { EuiStat, EuiFlexItem, EuiFlexGroup, EuiPage, EuiToolTip } from '@elastic/eui'; +import { + EuiStat, + EuiFlexItem, + EuiFlexGroup, + EuiPage, + EuiToolTip, +} from '@elastic/eui'; import { withErrorBoundary } from '../../../components/common/hocs'; import { UI_ORDER_AGENT_STATUS } from '../../../../common/constants'; -import { agentStatusLabelByAgentStatus, agentStatusColorByAgentStatus } from '../../../../common/services/wz_agent_status'; +import { + agentStatusLabelByAgentStatus, + agentStatusColorByAgentStatus, +} from '../../../../common/services/wz_agent_status'; +import { navigateAppURL } from '../../../react-services/navigate-app'; -export const Stats = withErrorBoundary (class Stats extends Component { - constructor(props) { - super(props); +export const Stats = withErrorBoundary( + class Stats extends Component { + constructor(props) { + super(props); - this.state = {}; - this.agentStatus = ['total', ...UI_ORDER_AGENT_STATUS].map(status => ({ - status, - label: status !== 'total' ? agentStatusLabelByAgentStatus(status) : 'Total', - onClick: () => this.goToAgents(status !== 'total' ? status : null), - color: status !== 'total' ? agentStatusColorByAgentStatus(status) : 'primary' - })); - } + this.state = {}; + this.agentStatus = ['total', ...UI_ORDER_AGENT_STATUS].map(status => ({ + status, + label: + status !== 'total' ? agentStatusLabelByAgentStatus(status) : 'Total', + onClick: () => this.goToAgents(status !== 'total' ? status : null), + color: + status !== 'total' + ? agentStatusColorByAgentStatus(status) + : 'primary', + })); + } - goToAgents(status) { - if(status){ - sessionStorage.setItem( - 'agents_preview_selected_options', - JSON.stringify([{field: 'q', value: `status=${status}`}]) - ); - }else if(sessionStorage.getItem('agents_preview_selected_options')){ - sessionStorage.removeItem('agents_preview_selected_options'); + goToAgents(status) { + if (status) { + sessionStorage.setItem( + 'agents_preview_selected_options', + JSON.stringify([{ field: 'q', value: `status=${status}` }]), + ); + } else if (sessionStorage.getItem('agents_preview_selected_options')) { + sessionStorage.removeItem('agents_preview_selected_options'); + } + navigateAppURL('/app/endpoints-summary#/agents-preview'); } - window.location.href = '#/agents-preview'; - } - renderTitle(total) { - return - - {total} - - - } + renderTitle(total) { + return ( + + {total} + + ); + } - render() { - return ( - - - - {this.agentStatus.map(({status, label, onClick, color}) => ( + render() { + return ( + + + + {this.agentStatus.map(({ status, label, onClick, color }) => ( + - {typeof this.props[status] !== 'undefined' ? this.props[status] : '-'} + {typeof this.props[status] !== 'undefined' + ? this.props[status] + : '-'} } description={`${label} agents`} titleColor={color} - textAlign="center" + textAlign='center' /> ))} - - - - ); - } -}); + + + + ); + } + }, +); Stats.propTypes = { total: PropTypes.any, active: PropTypes.any, disconnected: PropTypes.any, pending: PropTypes.any, - never_connected: PropTypes.any + never_connected: PropTypes.any, }; diff --git a/plugins/main/public/services/resolves/get-ip.js b/plugins/main/public/services/resolves/get-ip.js index b0f947bd64..5628b34fe7 100644 --- a/plugins/main/public/services/resolves/get-ip.js +++ b/plugins/main/public/services/resolves/get-ip.js @@ -20,27 +20,20 @@ import { UI_LOGGER_LEVELS } from '../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../react-services/common-services'; -export function getIp( - $q, - $window, - $location, - wzMisc -) { +export function getIp($q, $window, $location, wzMisc) { const deferred = $q.defer(); - const checkWazuhPatterns = async (indexPatterns) => { + const checkWazuhPatterns = async indexPatterns => { const wazuhConfig = new WazuhConfig(); const configuration = await getWzConfig($q, GenericRequest, wazuhConfig); const wazuhPatterns = [ `${configuration['wazuh.monitoring.pattern']}`, - `${configuration['cron.prefix']}-${configuration['cron.statistics.index.name']}-*` + `${configuration['cron.prefix']}-${configuration['cron.statistics.index.name']}-*`, ]; return wazuhPatterns.every(pattern => { - return indexPatterns.find( - element => element.id === pattern - ); + return indexPatterns.find(element => element.id === pattern); }); - } + }; const buildSavedObjectsClient = async () => { try { @@ -49,7 +42,7 @@ export function getIp( const savedObjectsData = await savedObjectsClient.find({ type: 'index-pattern', fields: ['title'], - perPage: 10000 + perPage: 10000, }); const { savedObjects } = savedObjectsData; @@ -58,19 +51,16 @@ export function getIp( if ( !currentPattern || - !savedObjects.find( - element => element.id === currentPattern - ) || + !savedObjects.find(element => element.id === currentPattern) || !(await checkWazuhPatterns(savedObjects)) ) { if (!$location.path().includes('/health-check')) { - $location.search('tab', null); $location.path('/health-check'); } } const onlyWazuhAlerts = savedObjects.filter( - element => element.id === currentPattern + element => element.id === currentPattern, ); if (!onlyWazuhAlerts || !onlyWazuhAlerts.length) { @@ -80,13 +70,15 @@ export function getIp( return; } - const courierData = await getDataPlugin().indexPatterns.get(currentPattern); + const courierData = await getDataPlugin().indexPatterns.get( + currentPattern, + ); deferred.resolve({ list: onlyWazuhAlerts, loaded: courierData, stateVal: null, - stateValFound: false + stateValFound: false, }); } catch (error) { deferred.reject(error); @@ -97,7 +89,11 @@ export function getIp( store: true, error: { error: error, - message: (error?.body?.message?.includes('no permissions for') && `You have no permissions. Contact to an administrator:\n${error?.body?.message}`) || error.message || error, + message: + (error?.body?.message?.includes('no permissions for') && + `You have no permissions. Contact to an administrator:\n${error?.body?.message}`) || + error.message || + error, title: error.name || error, }, }; diff --git a/plugins/main/server/routes/wazuh-reporting.test.ts b/plugins/main/server/routes/wazuh-reporting.test.ts index 45a9482c24..495ae16cbe 100644 --- a/plugins/main/server/routes/wazuh-reporting.test.ts +++ b/plugins/main/server/routes/wazuh-reporting.test.ts @@ -204,11 +204,11 @@ describe('[endpoint] PUT /utils/configuration', () => { // If any of the parameters is changed this variable should be updated with the new md5 it.each` footer | header | responseStatusCode | expectedMD5 | tab - ${null} | ${null} | ${200} | ${'a261be6b2e5fb18bb7434ee46a01e174'} | ${'pm'} - ${'Custom\nFooter'} | ${'info@company.com\nFake Avenue 123'} | ${200} | ${'51b268066bb5107e5eb0a9d791a89d0c'} | ${'general'} - ${''} | ${''} | ${200} | ${'8e8fbd90e08b810f700fcafbfdcdf638'} | ${'fim'} - ${'Custom Footer'} | ${null} | ${200} | ${'2b16be2ea88d3891cda7acb6075826d9'} | ${'aws'} - ${null} | ${'Custom Header'} | ${200} | ${'4a55136aaf8b5f6b544a03fe46917552'} | ${'gcp'} + ${null} | ${null} | ${200} | ${'301281824427c6ea8546fd14ee1aa5d8'} | ${'pm'} + ${'Custom\nFooter'} | ${'info@company.com\nFake Avenue 123'} | ${200} | ${'c2adfd7ab05ae3ed1548abd3c8be8f7e'} | ${'general'} + ${''} | ${''} | ${200} | ${'06726f42a4129dd47262ea7228939006'} | ${'fim'} + ${'Custom Footer'} | ${null} | ${200} | ${'1ea187181c307a4be5e90a38f614c42d'} | ${'aws'} + ${null} | ${'Custom Header'} | ${200} | ${'f2fc0804eb52ebca21291eb5a40dec35'} | ${'gcp'} `( `Set custom report header and footer - Verify PDF output`, async ({ footer, header, responseStatusCode, expectedMD5, tab }) => { From 2af4c5328e43ea2d2f872f68094d9f3f29447fcc Mon Sep 17 00:00:00 2001 From: JuanGarriuz Date: Mon, 18 Sep 2023 16:01:29 +0200 Subject: [PATCH 2/2] Modify Overview application to display the new menu (#5852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix endpoint summary and server managment redirections * fix: adapt the redirections to applications - Endpoints summary - IT Hygiene - Groups * fix(health-check): fix redirection to health check the removed the tab query parameter from URL and then the previous URL could not be rebuilt * fix(redirections): redirection from Overview app to Endpoints summary * fix(redirections): removed wz- prefix from the redirections to applications * Change overview page, linked to the new menu estructure * fix: move definitions of applications in the home application * fix: application descriptions --------- Co-authored-by: Antonio David GutiƩrrez --- .../common/welcome/overview-welcome.js | 177 ++--- plugins/main/public/plugin.ts | 574 +-------------- plugins/main/public/utils/applications.ts | 661 ++++++++++++++++++ 3 files changed, 724 insertions(+), 688 deletions(-) create mode 100644 plugins/main/public/utils/applications.ts diff --git a/plugins/main/public/components/common/welcome/overview-welcome.js b/plugins/main/public/components/common/welcome/overview-welcome.js index f09615632b..a8332ee620 100644 --- a/plugins/main/public/components/common/welcome/overview-welcome.js +++ b/plugins/main/public/components/common/welcome/overview-welcome.js @@ -33,14 +33,27 @@ import { withGlobalBreadcrumb, withReduxProvider, } from '../hocs'; -import { - LogoDocker, - LogoGitHub, - LogoGoogleCloud, - LogoOffice365, -} from '../logos'; import { compose } from 'redux'; -import { getNavigationAppURL } from '../../../react-services/navigate-app'; +import { + getNavigationAppURL, + navigateAppURL, +} from '../../../react-services/navigate-app'; +import { Applications, Categories } from '../../../utils/applications'; + +const appCategories = Applications.reduce((categories, app) => { + const existingCategory = categories.find( + category => category.label === app.category, + ); + if (existingCategory) { + existingCategory.apps.push(app); + } else { + categories.push({ + label: app.category, + apps: [app], + }); + } + return categories; +}, []); export const OverviewWelcome = compose( withReduxProvider, @@ -108,123 +121,49 @@ export const OverviewWelcome = compose( return ( - + + {this.props.agentsCountTotal == 0 && this.addAgent()} - {this.props.agentsCountTotal == 0 && this.addAgent()} - - - - - - {this.buildTabCard('general', 'dashboardApp')} - {this.buildTabCard('fim', 'filebeatApp')} - {this.props.extensions.aws && - this.buildTabCard('aws', 'logoAWSMono')} - {this.props.extensions.office && - this.buildTabCard('office', LogoOffice365)} - {this.props.extensions.gcp && - this.buildTabCard('gcp', LogoGoogleCloud)} - {this.props.extensions.github && - this.buildTabCard('github', LogoGitHub)} - - - - - - - - {this.buildTabCard('pm', 'advancedSettingsApp')} - {this.props.extensions.audit && - this.buildTabCard('audit', 'monitoringApp')} - {this.props.extensions.oscap && - this.buildTabCard('oscap', 'codeApp')} - {this.props.extensions.ciscat && - this.buildTabCard('ciscat', 'auditbeatApp')} - {this.buildTabCard('sca', 'securityAnalyticsApp')} - - - - - - - - - - - - {this.buildTabCard('vuls', 'securityApp')} - {this.props.extensions.virustotal && - this.buildTabCard('virustotal', 'savedObjectsApp')} - {this.props.extensions.osquery && - this.buildTabCard('osquery', 'searchProfilerApp')} - {this.props.extensions.docker && - this.buildTabCard('docker', LogoDocker)} - {this.buildTabCard('mitre', 'spacesApp')} - {/* TODO- Change "spacesApp" icon*/} - - - - - - - - {!this.props.extensions.pci && - !this.props.extensions.gdpr && - !this.props.extensions.hipaa && - !this.props.extensions.tsc && - !this.props.extensions.nist && ( - - - - Click the icon to - show regulatory compliance extensions. -

+ + {appCategories.map(({ label, apps }) => ( + + category.id === label) + ?.label + } + > + + + {apps.map(app => ( + + } - color='success' - iconType='help' + className='homSynopsis__card' + title={app.title} + onClick={() => navigateAppURL(`/app/${app.id}`)} + data-test-subj={`overviewWelcome${this.strtools.capitalize( + app.id, + )}`} + description={app.description} /> -
- )} - {(this.props.extensions.pci || - this.props.extensions.gdpr || - this.props.extensions.hipaa || - this.props.extensions.tsc || - this.props.extensions.nist) && ( - - {this.props.extensions.pci && - this.buildTabCard('pci', 'visTagCloud')} - {this.props.extensions.nist && - this.buildTabCard('nist', 'apmApp')} - {this.props.extensions.tsc && - this.buildTabCard('tsc', 'apmApp')} - {this.props.extensions.gdpr && - this.buildTabCard('gdpr', 'visBarVertical')} - {this.props.extensions.hipaa && - this.buildTabCard('hipaa', 'emsApp')} + ))} - )} -
-
-
+ +
+ ))} +
diff --git a/plugins/main/public/plugin.ts b/plugins/main/public/plugin.ts index 71b565163e..0d862f36af 100644 --- a/plugins/main/public/plugin.ts +++ b/plugins/main/public/plugin.ts @@ -25,7 +25,6 @@ import { setWzMainParams, setWzCurrentAppID, } from './kibana-services'; -import { i18n } from '@osd/i18n'; import { AppPluginStartDependencies, WazuhSetup, @@ -44,7 +43,7 @@ import { initializeInterceptor, unregisterInterceptor, } from './services/request-handler'; -import { DEFAULT_APP_CATEGORIES } from '../../../src/core/public'; +import { Applications, Categories } from './utils/applications'; const SIDEBAR_LOGO = 'customization.logo.sidebar'; const innerAngularName = 'app/wazuh'; @@ -157,573 +156,8 @@ export class WazuhPlugin updater$: this.stateUpdater, }); - // Define the app categories - const categoryEndpointSecurity = { - id: 'wz-category-endpoint-security', - label: i18n.translate('wz-app-category-endpoint-security', { - defaultMessage: 'Endpoint security', - }), - order: 1, - euiIconType: core.http.basePath.prepend( - logosInitialState?.logos?.[SIDEBAR_LOGO] - ? getAssetURL(logosInitialState?.logos?.[SIDEBAR_LOGO]) - : getThemeAssetURL('icon.svg', UI_THEME), - ), - }; - - const categoryThreadIntelligence = { - id: 'wz-category-thread-intelligence', - label: i18n.translate('wz-app-category-thread-intelligence', { - defaultMessage: 'Thread intelligence', - }), - order: 2, - euiIconType: core.http.basePath.prepend( - logosInitialState?.logos?.[SIDEBAR_LOGO] - ? getAssetURL(logosInitialState?.logos?.[SIDEBAR_LOGO]) - : getThemeAssetURL('icon.svg', UI_THEME), - ), - }; - - const categorySecurityOperations = { - id: 'wz-category-security-operations', - label: i18n.translate('wz-app-category-security-operations', { - defaultMessage: 'Security operations', - }), - order: 3, - euiIconType: core.http.basePath.prepend( - logosInitialState?.logos?.[SIDEBAR_LOGO] - ? getAssetURL(logosInitialState?.logos?.[SIDEBAR_LOGO]) - : getThemeAssetURL('icon.svg', UI_THEME), - ), - }; - - const categoryCloudSecurity = { - id: 'wz-category-cloud-security', - label: i18n.translate('wz-app-category-cloud-security', { - defaultMessage: 'Cloud security', - }), - order: 4, - euiIconType: core.http.basePath.prepend( - logosInitialState?.logos?.[SIDEBAR_LOGO] - ? getAssetURL(logosInitialState?.logos?.[SIDEBAR_LOGO]) - : getThemeAssetURL('icon.svg', UI_THEME), - ), - }; - - const categoryServerManagement = { - id: 'wz-category-server-management', - label: i18n.translate('wz-app-category-server-management', { - defaultMessage: 'Server management', - }), - order: 5, - euiIconType: core.http.basePath.prepend( - logosInitialState?.logos?.[SIDEBAR_LOGO] - ? getAssetURL(logosInitialState?.logos?.[SIDEBAR_LOGO]) - : getThemeAssetURL('icon.svg', UI_THEME), - ), - }; - // Register the applications - [ - { - category: categoryEndpointSecurity, - id: 'wz-home', - title: i18n.translate('wz-app-home', { - defaultMessage: 'Overview', - }), - redirectTo: () => '/overview/', - }, - { - category: categoryEndpointSecurity, - id: 'endpoints-summary', - title: i18n.translate('wz-app-endpoints-summary', { - defaultMessage: 'Endpoints summary', - }), - redirectTo: () => '/agents-preview/', - }, - { - category: categoryEndpointSecurity, - id: 'integrity-monitoring', - title: i18n.translate('wz-app-integrity-monitoring', { - defaultMessage: 'Integrity monitoring', - }), - redirectTo: () => - `/overview/?tab=fim&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryEndpointSecurity, - id: 'policy-monitoring', - title: i18n.translate('wz-app-policy-monitoring', { - defaultMessage: 'Policy monitoring', - }), - redirectTo: () => - `/overview/?tab=pm&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryEndpointSecurity, - id: 'security-configuration-assessment', - title: i18n.translate('wz-app-security-configuration-assessment', { - defaultMessage: 'Security configuration assessment', - }), - redirectTo: () => - `/overview/?tab=sca&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryEndpointSecurity, - id: 'system-auditing', - title: i18n.translate('wz-app-system-auditing', { - defaultMessage: 'System auditing', - }), - redirectTo: () => - `/overview/?tab=audit&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryEndpointSecurity, - id: 'openscap', - title: i18n.translate('wz-app-openscap', { - defaultMessage: 'OpenSCAP', - }), - redirectTo: () => - `/overview/?tab=oscap&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryEndpointSecurity, - id: 'ciscat', - title: i18n.translate('wz-app-ciscat', { - defaultMessage: 'CIS-CAT', - }), - redirectTo: () => - `/overview/?tab=ciscat&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryThreadIntelligence, - id: 'security-events', - title: i18n.translate('wz-app-security-events', { - defaultMessage: 'Security events', - }), - redirectTo: () => - `/overview/?tab=general&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryThreadIntelligence, - id: 'vulnerabilities', - title: i18n.translate('wz-app-vulnerabilities', { - defaultMessage: 'Vulnerabilities', - }), - redirectTo: () => - `/overview/?tab=vuls&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryThreadIntelligence, - id: 'mitre-attack', - title: i18n.translate('wz-app-mitre-attack', { - defaultMessage: 'MITRE ATT&CK', - }), - redirectTo: () => - `/overview/?tab=mitre&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryThreadIntelligence, - id: 'virustotal', - title: i18n.translate('wz-app-virustotal', { - defaultMessage: 'Virustotal', - }), - redirectTo: () => - `/overview/?tab=virustotal&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'pci-dss', - title: i18n.translate('wz-app-pci-dss', { - defaultMessage: 'PCI DSS', - }), - redirectTo: () => - `/overview/?tab=pci&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'hipaa', - title: i18n.translate('wz-app-hipaa', { - defaultMessage: 'HIPAA', - }), - redirectTo: () => - `/overview/?tab=hipaa&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'gdpr', - title: i18n.translate('wz-app-gdpr', { - defaultMessage: 'GDPR', - }), - redirectTo: () => - `/overview/?tab=gdpr&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'nist-800-53', - title: i18n.translate('wz-app-nist-800-53', { - defaultMessage: 'NIST 800-53', - }), - redirectTo: () => - `/overview/?tab=nist&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'tsc', - title: i18n.translate('wz-app-tsc', { - defaultMessage: 'TSC', - }), - redirectTo: () => - `/overview/?tab=tsc&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'it-hygiene', - title: i18n.translate('wz-app-it-hygiene', { - defaultMessage: 'IT Hygiene', - }), - redirectTo: () => - `/agents/?tab=welcome${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agent=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categorySecurityOperations, - id: 'osquery', - title: i18n.translate('wz-app-osquery', { - defaultMessage: 'Osquery', - }), - redirectTo: () => - `/overview/?tab=osquery&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryCloudSecurity, - id: 'amazon-web-services', - title: i18n.translate('wz-app-amazon-web-services', { - defaultMessage: 'Amazon Web Services', - }), - redirectTo: () => - `/overview/?tab=aws&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryCloudSecurity, - id: 'google-cloud', - title: i18n.translate('wz-app-google-cloud', { - defaultMessage: 'Google Cloud', - }), - redirectTo: () => - `/overview/?tab=gcp&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryCloudSecurity, - id: 'github', - title: i18n.translate('wz-app-github', { - defaultMessage: 'GitHub', - }), - redirectTo: () => - `/overview/?tab=github&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryCloudSecurity, - id: 'office365', - title: i18n.translate('wz-app-office365', { - defaultMessage: 'Office 365', - }), - redirectTo: () => - `/overview/?tab=office&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryCloudSecurity, - id: 'docker-listener', - title: i18n.translate('wz-app-docker-listener', { - defaultMessage: 'Docker listener', - }), - redirectTo: () => - `/overview/?tab=docker&tabView=panels${ - store.getState()?.appStateReducers?.currentAgentData?.id - ? `&agentId=${ - store.getState()?.appStateReducers?.currentAgentData?.id - }` - : '' - }`, - }, - { - category: categoryServerManagement, - id: 'rules', - title: i18n.translate('wz-app-rules', { - defaultMessage: 'Rules', - }), - redirectTo: () => '/manager/?tab=ruleset', - }, - { - category: categoryServerManagement, - id: 'decoders', - title: i18n.translate('wz-app-decoders', { - defaultMessage: 'Decoders', - }), - redirectTo: () => '/manager/?tab=decoders', - }, - { - category: categoryServerManagement, - id: 'cdb-lists', - title: i18n.translate('wz-app-lists', { - defaultMessage: 'CDB Lists', - }), - redirectTo: () => '/manager/?tab=lists', - }, - { - category: categoryServerManagement, - id: 'groups', - title: i18n.translate('wz-app-groups', { - defaultMessage: 'Groups', - }), - redirectTo: () => '/manager/?tab=groups', - }, - { - category: categoryServerManagement, - id: 'server-status', - title: i18n.translate('wz-app-status', { - defaultMessage: 'Status', - }), - redirectTo: () => '/manager/?tab=status', - }, - { - category: categoryServerManagement, - id: 'cluster', - title: i18n.translate('wz-app-cluster', { - defaultMessage: 'Cluster', - }), - redirectTo: () => '/manager/?tab=monitoring', - }, - { - category: categoryServerManagement, - id: 'statistics', - title: i18n.translate('wz-app-statistics', { - defaultMessage: 'Statistics', - }), - redirectTo: () => '/manager/?tab=statistics', - }, - { - category: categoryServerManagement, - id: 'logs', - title: i18n.translate('wz-app-logs', { - defaultMessage: 'Logs', - }), - redirectTo: () => '/manager/?tab=logs', - }, - { - category: categoryServerManagement, - id: 'reporting', - title: i18n.translate('wz-app-reporting', { - defaultMessage: 'Reporting', - }), - redirectTo: () => '/manager/?tab=reporting', - }, - { - category: categoryServerManagement, - id: 'settings', - title: i18n.translate('wz-app-settings', { - defaultMessage: 'Settings', - }), - redirectTo: () => '/manager/?tab=configuration', - }, - { - category: categoryServerManagement, - id: 'api-console', - title: i18n.translate('wz-app-api-console', { - defaultMessage: 'API console', - }), - redirectTo: () => '/wazuh-dev/?tab=devTools', - }, - { - category: categoryServerManagement, - id: 'ruleset-test', - title: i18n.translate('wz-app-ruleset-test', { - defaultMessage: 'Ruleset test', - }), - redirectTo: () => '/wazuh-dev/?tab=logtest', - }, - { - category: categoryServerManagement, - id: 'rbac', - title: i18n.translate('wz-app-rbac', { - defaultMessage: 'RBAC', - }), - redirectTo: () => '/security/?tab=users', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'server-api', - title: i18n.translate('wz-app-server-api', { - defaultMessage: 'Server API', - }), - redirectTo: () => '/settings?tab=api', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'modules', - title: i18n.translate('wz-app-modules', { - defaultMessage: 'Modules', - }), - redirectTo: () => '/settings?tab=modules', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'server-data', - title: i18n.translate('wz-app-server-data', { - defaultMessage: 'Server data', - }), - redirectTo: () => '/settings?tab=sample_data', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'configuration', - title: i18n.translate('wz-app-configuration', { - defaultMessage: 'Configuration', - }), - redirectTo: () => '/settings?tab=configuration', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'app-logs', - title: i18n.translate('wz-app-app-logs', { - defaultMessage: 'Logs', - }), - redirectTo: () => '/settings?tab=logs', - }, - { - category: DEFAULT_APP_CATEGORIES.management, - id: 'about', - title: i18n.translate('wz-app-about', { - defaultMessage: 'About', - }), - redirectTo: () => '/settings?tab=about', - }, - ].forEach(({ category, id, title, redirectTo }) => { + Applications.forEach(({ category, id, title, redirectTo }) => { core.application.register({ id, title, @@ -771,7 +205,9 @@ export class WazuhPlugin console.debug(error); } }, - category, + category: Categories.find( + ({ id: categoryID }) => categoryID === category, + ), }); }); } diff --git a/plugins/main/public/utils/applications.ts b/plugins/main/public/utils/applications.ts new file mode 100644 index 0000000000..c1619ee114 --- /dev/null +++ b/plugins/main/public/utils/applications.ts @@ -0,0 +1,661 @@ +import { i18n } from '@osd/i18n'; +import store from '../redux/store'; +import { + LogoDocker, + LogoGitHub, + LogoGoogleCloud, + LogoOffice365, +} from '../components/common/logos'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; + +export const Applications = [ + { + category: 'wz-category-endpoint-security', + id: 'wz-home', + title: i18n.translate('wz-app-home', { + defaultMessage: 'Overview', + }), + description: + 'This application provides you with an overview of Wazuh applications.', + euiIconType: 'lensApp', + redirectTo: () => '/overview/', + }, + { + category: 'wz-category-endpoint-security', + id: 'endpoints-summary', + title: i18n.translate('wz-app-endpoints-summary', { + defaultMessage: 'Endpoints summary', + }), + description: 'Summary of agents and their status.', + euiIconType: 'usersRolesApp', + redirectTo: () => '/agents-preview/', + }, + { + category: 'wz-category-endpoint-security', + id: 'integrity-monitoring', + title: i18n.translate('wz-app-integrity-monitoring', { + defaultMessage: 'Integrity monitoring', + }), + description: + 'Alerts related to file changes, including permissions, content, ownership and attributes.', + euiIconType: 'indexRollupApp', + redirectTo: () => + `/overview/?tab=fim&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-endpoint-security', + id: 'policy-monitoring', + title: i18n.translate('wz-app-policy-monitoring', { + defaultMessage: 'Policy monitoring', + }), + description: + 'Verify that your systems are configured according to your security policies baseline.', + euiIconType: 'indexRollupApp', + redirectTo: () => + `/overview/?tab=pm&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-endpoint-security', + id: 'security-configuration-assessment', + title: i18n.translate('wz-app-security-configuration-assessment', { + defaultMessage: 'Security configuration assessment', + }), + description: + 'Scan your assets as part of a configuration assessment audit.', + euiIconType: 'managementApp', + redirectTo: () => + `/overview/?tab=sca&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-endpoint-security', + id: 'system-auditing', + title: i18n.translate('wz-app-system-auditing', { + defaultMessage: 'System auditing', + }), + description: + 'Audit users behavior, monitoring command execution and alerting on access to critical files.', + euiIconType: 'searchProfilerApp', + redirectTo: () => + `/overview/?tab=audit&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-endpoint-security', + id: 'openscap', + title: i18n.translate('wz-app-openscap', { + defaultMessage: 'OpenSCAP', + }), + description: + 'Configuration assessment and automation of compliance monitoring using SCAP checks.', + euiIconType: 'metricbeatApp', + redirectTo: () => + `/overview/?tab=oscap&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-endpoint-security', + id: 'ciscat', + title: i18n.translate('wz-app-ciscat', { + defaultMessage: 'CIS-CAT', + }), + description: + 'Configuration assessment using Center of Internet Security scanner and SCAP checks.', + euiIconType: 'metricbeatApp', + redirectTo: () => + `/overview/?tab=ciscat&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-thread-intelligence', + id: 'security-events', + title: i18n.translate('wz-app-security-events', { + defaultMessage: 'Security events', + }), + description: + 'Browse through your security alerts, identifying issues and threats in your environment.', + euiIconType: 'securityAnalyticsApp', + redirectTo: () => + `/overview/?tab=general&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-thread-intelligence', + id: 'vulnerabilities', + title: i18n.translate('wz-app-vulnerabilities', { + defaultMessage: 'Vulnerabilities', + }), + description: + 'Discover what applications in your environment are affected by well-known vulnerabilities.', + euiIconType: 'heartbeatApp', + redirectTo: () => + `/overview/?tab=vuls&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-thread-intelligence', + id: 'mitre-attack', + title: i18n.translate('wz-app-mitre-attack', { + defaultMessage: 'MITRE ATT&CK', + }), + description: + 'Security events from the knowledge base of adversary tactics and techniques based on real-world observations.', + euiIconType: 'grokApp', + redirectTo: () => + `/overview/?tab=mitre&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-thread-intelligence', + id: 'virustotal', + title: i18n.translate('wz-app-virustotal', { + defaultMessage: 'Virustotal', + }), + description: + 'Alerts resulting from VirusTotal analysis of suspicious files via an integration with their API.', + euiIconType: 'monitoringApp', + redirectTo: () => + `/overview/?tab=virustotal&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'pci-dss', + title: i18n.translate('wz-app-pci-dss', { + defaultMessage: 'PCI DSS', + }), + description: + 'Global security standard for entities that process, store or transmit payment cardholder data.', + euiIconType: 'sqlApp', + redirectTo: () => + `/overview/?tab=pci&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'hipaa', + title: i18n.translate('wz-app-hipaa', { + defaultMessage: 'HIPAA', + }), + description: + 'Health Insurance Portability and Accountability Act of 1996 (HIPAA) provides data privacy and security provisions for safeguarding medical information.', + euiIconType: 'monitoringApp', + redirectTo: () => + `/overview/?tab=hipaa&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'gdpr', + title: i18n.translate('wz-app-gdpr', { + defaultMessage: 'GDPR', + }), + description: + 'General Data Protection Regulation (GDPR) sets guidelines for processing of personal data.', + euiIconType: 'sqlApp', + redirectTo: () => + `/overview/?tab=gdpr&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'nist-800-53', + title: i18n.translate('wz-app-nist-800-53', { + defaultMessage: 'NIST 800-53', + }), + description: + 'National Institute of Standards and Technology Special Publication 800-53 (NIST 800-53) sets guidelines for federal information systems.', + euiIconType: 'notebookApp', + redirectTo: () => + `/overview/?tab=nist&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'tsc', + title: i18n.translate('wz-app-tsc', { + defaultMessage: 'TSC', + }), + description: + 'Trust Services Criteria for Security, Availability, Processing Integrity, Confidentiality, and Privacy.', + euiIconType: 'packetbeatApp', + redirectTo: () => + `/overview/?tab=tsc&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'it-hygiene', + title: i18n.translate('wz-app-it-hygiene', { + defaultMessage: 'IT Hygiene', + }), + description: + 'Applications, network configuration, open ports and processes running on your monitored systems.', + euiIconType: 'visualizeApp', + redirectTo: () => + `/agents/?tab=welcome${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agent=${store.getState()?.appStateReducers?.currentAgentData?.id}` + : '' + }`, + }, + { + category: 'wz-category-security-operations', + id: 'osquery', + title: i18n.translate('wz-app-osquery', { + defaultMessage: 'Osquery', + }), + description: + 'Osquery can be used to expose an operating system as a high-performance relational database.', + euiIconType: 'sqlApp', + redirectTo: () => + `/overview/?tab=osquery&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-cloud-security', + id: 'amazon-web-services', + title: i18n.translate('wz-app-amazon-web-services', { + defaultMessage: 'Amazon Web Services', + }), + description: + 'Security events related to your Amazon AWS services, collected directly via AWS API.', + euiIconType: 'logoAWSMono', + redirectTo: () => + `/overview/?tab=aws&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-cloud-security', + id: 'google-cloud', + title: i18n.translate('wz-app-google-cloud', { + defaultMessage: 'Google Cloud', + }), + description: + 'Security events related to your Google Cloud Platform services, collected directly via GCP API.', + euiIconType: LogoGoogleCloud, + redirectTo: () => + `/overview/?tab=gcp&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-cloud-security', + id: 'github', + title: i18n.translate('wz-app-github', { + defaultMessage: 'GitHub', + }), + description: + 'Monitoring events from audit logs of your GitHub organitations', + euiIconType: LogoGitHub, + redirectTo: () => + `/overview/?tab=github&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-cloud-security', + id: 'office365', + title: i18n.translate('wz-app-office365', { + defaultMessage: 'Office 365', + }), + description: 'Security events related to your Office 365 services.', + euiIconType: LogoOffice365, + redirectTo: () => + `/overview/?tab=office&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-cloud-security', + id: 'docker-listener', + title: i18n.translate('wz-app-docker-listener', { + defaultMessage: 'Docker listener', + }), + description: + 'Monitor and collect the activity from Docker containers such as creation, running, starting, stopping or pausing events.', + euiIconType: LogoDocker, + redirectTo: () => + `/overview/?tab=docker&tabView=panels${ + store.getState()?.appStateReducers?.currentAgentData?.id + ? `&agentId=${ + store.getState()?.appStateReducers?.currentAgentData?.id + }` + : '' + }`, + }, + { + category: 'wz-category-server-management', + id: 'rules', + title: i18n.translate('wz-app-rules', { + defaultMessage: 'Rules', + }), + description: 'Manage your Wazuh cluster rules.', + euiIconType: 'indexRollupApp', + redirectTo: () => '/manager/?tab=ruleset', + }, + { + category: 'wz-category-server-management', + id: 'decoders', + title: i18n.translate('wz-app-decoders', { + defaultMessage: 'Decoders', + }), + description: 'Manage your Wazuh cluster decoders.', + euiIconType: 'indexRollupApp', + redirectTo: () => '/manager/?tab=decoders', + }, + { + category: 'wz-category-server-management', + id: 'cdb-lists', + title: i18n.translate('wz-app-lists', { + defaultMessage: 'CDB Lists', + }), + description: 'Manage your Wazuh cluster CDB list.', + euiIconType: 'indexRollupApp', + redirectTo: () => '/manager/?tab=lists', + }, + { + category: 'wz-category-server-management', + id: 'groups', + title: i18n.translate('wz-app-groups', { + defaultMessage: 'Groups', + }), + description: 'Manage your agent groups.', + euiIconType: 'usersRolesApp', + redirectTo: () => '/manager/?tab=groups', + }, + { + category: 'wz-category-server-management', + id: 'server-status', + title: i18n.translate('wz-app-status', { + defaultMessage: 'Status', + }), + description: 'Manage your Wazuh cluster status.', + euiIconType: 'uptimeApp', + redirectTo: () => '/manager/?tab=status', + }, + { + category: 'wz-category-server-management', + id: 'cluster', + title: i18n.translate('wz-app-cluster', { + defaultMessage: 'Cluster', + }), + description: 'Visualize your Wazuh cluster.', + euiIconType: 'indexPatternApp', + redirectTo: () => '/manager/?tab=monitoring', + }, + { + category: 'wz-category-server-management', + id: 'statistics', + title: i18n.translate('wz-app-statistics', { + defaultMessage: 'Statistics', + }), + description: 'Information about the Wazuh enviroment.', + euiIconType: 'visualizeApp', + redirectTo: () => '/manager/?tab=statistics', + }, + { + category: 'wz-category-server-management', + id: 'logs', + title: i18n.translate('wz-app-logs', { + defaultMessage: 'Logs', + }), + description: 'Logs from your Wazuh cluster.', + euiIconType: 'filebeatApp', + redirectTo: () => '/manager/?tab=logs', + }, + { + category: 'wz-category-server-management', + id: 'reporting', + title: i18n.translate('wz-app-reporting', { + defaultMessage: 'Reporting', + }), + description: 'Check your stored Wazuh reports.', + euiIconType: 'reportingApp', + redirectTo: () => '/manager/?tab=reporting', + }, + { + category: 'wz-category-server-management', + id: 'settings', + title: i18n.translate('wz-app-settings', { + defaultMessage: 'Settings', + }), + description: 'Manage your Wazuh cluster configuration.', + euiIconType: 'managementApp', + redirectTo: () => '/manager/?tab=configuration', + }, + { + category: 'wz-category-server-management', + id: 'api-console', + title: i18n.translate('wz-app-api-console', { + defaultMessage: 'API console', + }), + description: 'Test the Wazuh API endpoints.', + euiIconType: 'uptimeApp', + redirectTo: () => '/wazuh-dev/?tab=devTools', + }, + { + category: 'wz-category-server-management', + id: 'ruleset-test', + title: i18n.translate('wz-app-ruleset-test', { + defaultMessage: 'Ruleset test', + }), + description: 'Check your ruleset testing logs.', + euiIconType: 'consoleApp', + redirectTo: () => '/wazuh-dev/?tab=logtest', + }, + { + category: 'wz-category-server-management', + id: 'rbac', + title: i18n.translate('wz-app-rbac', { + defaultMessage: 'RBAC', + }), + description: + 'Manage permissions to system resources based on the roles and policies.', + euiIconType: 'managementApp', + redirectTo: () => '/security/?tab=users', + }, + { + category: 'management', + id: 'server-api', + title: i18n.translate('wz-app-server-api', { + defaultMessage: 'Server API', + }), + description: 'Manage and configure the API entries.', + euiIconType: 'devToolsApp', + redirectTo: () => '/settings?tab=api', + }, + { + category: 'management', + id: 'modules', + title: i18n.translate('wz-app-modules', { + defaultMessage: 'Modules', + }), + description: 'Manage your Wazuh App preview.', + euiIconType: 'gisApp', + redirectTo: () => '/settings?tab=modules', + }, + { + category: 'management', + id: 'server-data', + title: i18n.translate('wz-app-server-data', { + defaultMessage: 'Server data', + }), + description: 'Add sample data with events to the modules.', + euiIconType: 'spacesApp', + redirectTo: () => '/settings?tab=sample_data', + }, + { + category: 'management', + id: 'configuration', + title: i18n.translate('wz-app-configuration', { + defaultMessage: 'Configuration', + }), + description: 'Manage your Wazuh cluster configuration.', + euiIconType: 'devToolsApp', + redirectTo: () => '/settings?tab=configuration', + }, + { + category: 'management', + id: 'app-logs', + title: i18n.translate('wz-app-app-logs', { + defaultMessage: 'Logs', + }), + description: 'Explore the logs related to the applications.', + euiIconType: 'filebeatApp', + redirectTo: () => '/settings?tab=logs', + }, + { + category: 'management', + id: 'about', + title: i18n.translate('wz-app-about', { + defaultMessage: 'About', + }), + description: 'Show information about App Versions and community links.', + euiIconType: 'graphApp', + redirectTo: () => '/settings?tab=about', + }, +]; + +export const Categories = [ + { + id: 'wz-category-endpoint-security', + label: i18n.translate('wz-app-category-endpoint-security', { + defaultMessage: 'Endpoint security', + }), + order: 1, + euiIconType: 'monitoringApp', + }, + { + id: 'wz-category-thread-intelligence', + label: i18n.translate('wz-app-category-thread-intelligence', { + defaultMessage: 'Thread intelligence', + }), + order: 2, + euiIconType: 'lensApp', + }, + { + id: 'wz-category-security-operations', + label: i18n.translate('wz-app-category-security-operations', { + defaultMessage: 'Security operations', + }), + order: 3, + euiIconType: 'securityApp', + }, + { + id: 'wz-category-cloud-security', + label: i18n.translate('wz-app-category-cloud-security', { + defaultMessage: 'Cloud security', + }), + order: 4, + euiIconType: 'watchesApp', + }, + { + id: 'wz-category-server-management', + label: i18n.translate('wz-app-category-server-management', { + defaultMessage: 'Server management', + }), + order: 5, + euiIconType: 'indexRollupApp', + }, + DEFAULT_APP_CATEGORIES.management, +];