From 7485dc419723cf112171abc0bb10259af2a16679 Mon Sep 17 00:00:00 2001 From: Guido Modarelli <38738725+guidomodarelli@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:02:05 -0300 Subject: [PATCH] Add Wazuh Analysis plugin files and structure (#7242) * Add initial Wazuh Analysis plugin files and structure * Add wazuh-analysis plugin to dev.yml configuration * Set server option to false in Wazuh Analysis plugin configuration * Add initial implementation of the Wazuh Analysis plugin * Enforce no-empty-object-type rule in TypeScript files for Wazuh Analysis plugin * Refactor AnalysisApp component to use EUI layout components * Enable TTY for OSD development environment in dev.yml * Add TypeScript types for React and ReactDOM as dev dependencies * Add translation messages and application categories for Wazuh Analysis plugin * Refactor AnalysisPlugin to use constants for plugin and category IDs * Add navigation links for security categories in AnalysisPlugin * Fix indentation in translation messages for analysis categories in Wazuh Analysis plugin * Refactor translation keys in AnalysisPlugin to use plugin ID for consistency * Add translation messages for new categories in AnalysisPlugin * Refactor AnalysisPlugin to use dynamic keys for application IDs * Add navigation links for configuration assessment, malware detection, and FIM in AnalysisPlugin * Add function to navigate to the first app in a navigation group * Update application IDs in AnalysisPlugin to include endpoint security path * Remove category assignment from threat intelligence application in AnalysisPlugin * Add description for endpoint security category in AnalysisPlugin * Remove category assignment from endpoint security application in AnalysisPlugin * Fix navigation to the first app in the endpoint security group and update core start property assignment * Refactor sub-application ID generation in AnalysisPlugin for improved readability * Add JSDoc comments for generateSubAppId function in plugin.ts * Refactor mount functions in AnalysisPlugin to use arrow function syntax for improved context handling * Implement app startup subject in AnalysisPlugin for navigation handling * Add getCurrentNavGroup function to retrieve the current navigation group * Refactor navigateToFirstAppInNavGroup function to improve parameter handling and simplify navigation logic * Refactor app startup subscription to set current navigation group and navigate accordingly * Add navigation link status management for endpoint security applications * Refactor endpoint security applications to streamline mount logic and manage nav link visibility * Refactor application registration to use an array for better structure and clarity * Refactor translationMessages to use Object.freeze for immutability * Refactor AnalysisPlugin setup to improve app registration structure * Refactor AnalysisPlugin to use Object.freeze for navigation groups * Refactor AnalysisPlugin to separate navigation group registration into its own method * Refactor AnalysisPlugin to extract app startup subscription into a separate method * Refactor AnalysisPlugin to use Object.freeze for CATEGORY definition * Refactor AnalysisPlugin to consolidate endpoint security apps into a single subApps structure * Refactor AnalysisPlugin to improve code organization and maintainability * Refactor AnalysisPlugin to assign CATEGORY to endpoint security and threat intelligence apps * Refactor AnalysisPlugin to update appStatusUpdater type to AppUpdater * Refactor AnalysisPlugin to conditionally update appStatusUpdater based on navGroup status * Refactor AnalysisPlugin to ensure unmount function returns a boolean value * Refactor AnalysisPlugin to simplify appStatusUpdater initialization and remove optional chaining * Refactor package.json to remove unused React type definitions from devDependencies * Refactor yarn.lock to remove unused React type definitions and csstype dependency * Refactor AnalysisPlugin to add threat intelligence sub-apps and update navigation links * Refactor AnalysisPlugin to add new sub-apps for regulatory compliance, IT hygiene, incident response, and cloud security services * Refactor AnalysisPlugin to improve app mount logic and enhance app status updates * Refactor AnalysisPlugin to streamline app mount logic using a loop for improved maintainability * Refactor AnalysisPlugin to register applications directly in setupAppMounts for improved clarity and efficiency * Refactor AnalysisPlugin to rename navigation link visibility functions for improved clarity * Add global search functionality for navigation pages in AnalysisPlugin * Rename global search command ID in AnalysisPlugin for consistency * Rename search pages command file for improved organization and clarity * Move search pages command import to global search directory for improved organization * Rename function generateSubAppId to buildSubAppId for improved clarity * Add internationalization support for Analysis Plugin title * Add constants file and export PLUGIN_ID for analysis plugin * Remove redundant PLUGIN_ID constant from analysis plugin * Add CATEGORY constant for analysis plugin with internationalization support * Add Endpoint Security application with internationalization support * Refactor analysis plugin constants and remove redundant i18n file * Add description constant for Endpoint Security and update references * Add Threat Intelligence application with internationalization support * Add Threat Intelligence application implementation and registration * rename application files to group directory * Refactor plugin imports to group directory structure * Add Security Operations application implementation and registration * Add Cloud Security application implementation and internationalization support * Add GroupsId type definition for security applications * Add navigation groups for security applications * Add Cloud Security application structure and integration * Enable new home page feature in OpenSearch Dashboards configuration * Refactor buildSubAppId function into utils for better modularity * Move navigateToFirstAppInNavGroup function to utils and update imports * Rename `generateSubAppId` function to `buildSubAppId` for consistency * Add endpoint security and threat intelligence applications * Export application IDs and titles for endpoint security and threat intelligence * Add security operations applications with IDs and titles * Add cloud security applications with IDs and titles * Refactor app ID handling to use GroupsId type for improved consistency * Add endpoint security navigation group and refactor registration of nav links * Refactor navigation group handling and improve utility functions for better code organization * Refactor endpoint security navigation group setup for improved organization and clarity * Refactor application management by introducing ApplicationService for improved app lifecycle handling and navigation group integration * Refactor AnalysisPlugin to streamline app updater registration using navGroupsIds array * Refactor endpoint security group by consolidating app registration and navigation link setup into a single module * Refactor cloud security group by consolidating navigation and application handling into a single module * Refactor security operations group by consolidating navigation and application handling into a single module * Refactor Group interface to use generic type for improved type safety * Refactor threat intelligence group by consolidating application logic and removing obsolete file * Refactor cloud security application import for improved clarity * Refactor navigation groups by removing obsolete nav-groups file * Refactor navigation groups to use generic types for improved type safety * Refactor AnalysisPlugin to use nav group classes for improved organization and clarity * Refactor AnalysisPlugin to simplify subApps initialization by removing unnecessary object mapping * Refactor AnalysisPlugin and ApplicationService to streamline nav link visibility management by consolidating related functions and simplifying initialization logic * Refactor AnalysisPlugin to change subApps type from App[] to App[][] for improved type clarity * Refactor ApplicationService to encapsulate navigation logic in navigateToFirstAppInNavGroup method for improved clarity and maintainability * Refactor ApplicationService to enhance documentation with detailed JSDoc comments for improved code clarity and maintainability * Refactor nav-group utility to remove navigateToFirstAppInNavGroup function and introduce getCurrentNavGroup for improved navigation handling * Refactor ApplicationService to enhance unmount logic for improved resource cleanup during application lifecycle * Refactor ApplicationService to rename appOperations methods for improved clarity and consistency in lifecycle management * Refactor AnalysisPlugin to move app updater registration to the start method for improved initialization flow * Refactor DashboardSecurityService types to enhance typing for higher type safety and add optional token property * Refactor DashboardSecurityService to improve type safety and error handling, and enhance localization for user role messages * Refactor LifecycleService to make dependency parameters optional for improved flexibility * Add NoopLogger implementation for logging interface compliance * Add logging to ApplicationService for improved debugging and traceability * Refactor application.service.ts to streamline imports and enhance code organization * Refactor security-related groups to centralize constants and improve code organization * Refactor WazuhCorePlugin to implement NoopLogger for improved logging and type safety * Refactor ApplicationService to use navGroupId for improved navigation group handling * Remove unused navigation group handling from ApplicationService unmount logic * Refactor WazuhCorePlugin and ApplicationService for improved navigation handling and error management * Remove unused getCurrentNavGroup utility function from nav-group.ts * Refactor WazuhCorePlugin and AnalysisPlugin to use applicationService for improved service management * Refactor AnalysisPlugin to streamline imports and enhance type definitions for setup dependencies * Add createSideNavItems function to generate side navigation items for apps * Add createEndpointSecurityNavItems function to generate navigation items for endpoint security * Add Layout component for structured page layout with sidebar navigation * Add new endpoint security applications: Configuration Assessment, FIM, and Malware Detection * Update endpoint security applications imports for configuration assessment, malware detection, and FIM * Update Layout component to accept a single child or an array of children Refactor imports in types.ts to use constants from respective modules Remove empty object parameter from renderApp calls in endpoint security applications Refactor endpoint security applications to use Layout component and pass AppMountParameters refactor: remove history parameter from renderApp calls in endpoint security applications * feat: implement navigation to the first endpoint security app on mount * feat: add plugin services for core getter and setter in Wazuh analysis plugin * feat: update side navigation to use core application navigation * feat: add opensearchDashboardsUtils as a required plugin for Wazuh analysis * refactor: remove order property from navigation groups in Wazuh analysis plugin * feat: implement navigation to the first endpoint security app in the mount function * feat: conditionally render side navigation based on nav group settings * refactor: update import paths for endpoint security apps to use new filenames * feat: add conditional navigation to the first endpoint security app based on nav group settings * refactor: make updater$ parameter optional in getApps method across navigation groups * refactor: replace createEndpointSecurityNavItems with createSideNavItems for navigation consistency * feat: implement regulatory compliance app with dedicated rendering and link to security operations navigation group * feat: add IT hygiene application with dedicated rendering and integrate into security operations navigation group * refactor: remove TODO comments for unimplemented applications in endpoint security apps module * refactor: remove TODO comments for incident response application and update import path in security operations apps module * feat: implement incident response application with rendering logic and integrate into security operations navigation group * fix: update SECURITY_OPERATIONS_ID to SECURITY_OPERATIONS_TITLE in IT Hygiene and Regulatory Compliance apps for consistency * feat: implement MITRE ATT&CK application with rendering logic and integrate into threat intelligence navigation group * feat: enhance threat intelligence nav group to navigate to app based on nav group status in mount function * Fix import path and update title for MITRE attack app * Add threat-hunting application to threat intelligence group * Implement vulnerability detection application structure * Refactor app props naming for consistency across incident response, it hygiene, and compliance apps * Enhance Cloud Security navigation by conditionally redirecting based on nav group status in the mount function * Implement AWS application for cloud security group * Add Docker application implementation and render logic for cloud security group in Wazuh analysis plugin * Implement Google Cloud application and render logic for cloud security group in Wazuh analysis plugin * Add GitHub application implementation and render logic for cloud security group in Wazuh analysis plugin * Implement Office 365 application and render logic for cloud security group in Wazuh analysis plugin * Remove unused appBasePath prop from AnalysisApp component in Wazuh analysis plugin for cleaner code * Enhance ApplicationService with detailed documentation and refine method descriptions for improved clarity and maintainability * refactor: rename match function to hasMatch for clarity * refactor: remove unused Group interface and related methods for cleaner code * refactor: update Group import path for consistency and remove unused addNavLinks method * refactor: enhance Group interface with detailed methods for navigation and application management * refactor: enrich debug logging in registerAppUpdater with appId for better traceability * refactor: reorganize application service methods for clearer structure and enhance navigation group setup logic * refactor: streamline application initialization and navigation group handling for improved clarity and functionality * refactor: rename method and streamline navigation group assignment for improved code clarity and organization * refactor: simplify app registration and enhance navigation group handling for better code clarity and maintainability * refactor: rename and simplify sub-app mounting logic for improved clarity and organization * feat: add global search page item and search pages command components for enhanced navigation and search functionality * refactor: enhance application setup and search command registration for improved modularity and clarity * refactor: simplify application startup method for improved clarity and consistency * refactor: streamline application mount logic for improved clarity and modularity * refactor: replace getCoreStart with getCore for improved consistency in application service * refactor: update ApplicationService to use coreSetup for improved modularity and clarity * refactor: enhance logging in ApplicationService for improved traceability * refactor: enhance debug logging in AnalysisPlugin for improved traceability * refactor: change method visibility to private in ApplicationService for encapsulation * refactor: remove coreSetup parameter from ApplicationService methods for improved clarity * Refactor ApplicationService documentation and methods * refactor: add NavGroupType to security-related navigation groups for consistency * fix: correct translation key for analysis plugin title * refactor: update import paths for constants in cloud security apps * refactor: move configuration assessment constants to a separate file for better organization * refactor: streamline ANALYSIS_PLUGIN_TITLE translation and ensure consistent NavGroupType usage * refactor: reorganize import paths for constants in configuration assessment files * refactor: move FIM constants to a dedicated file for better organization * refactor: move malware detection constants to a dedicated file for better organization * refactor: move incident response constants to a dedicated file for better organization * refactor: move IT hygiene constants to a dedicated file for better organization * refactor: move regulatory compliance constants to a dedicated file for better organization * refactor: move MITRE ATT&CK constants to a dedicated file for better organization * refactor: move threat hunting constants to a dedicated file for better organization * refactor: move vulnerability detection constants to a dedicated file for better organization * Add tasks to initialize index patterns (#7262) * feat: add task to create index pattern fields - Add task to create index pattern fields - Create CLIs to get the static files for the index pattern default fields used when there is no indices on the Wazuh dashboard starts * chore(changelog): add entry * fix(script): uncomment statements in build-static-files-get-from-wildcard tool * Remove .scheduled-commands index pattern creation (#7263) * fix: remove scheduled-commands index pattern creation * feat(changelog): add entry * Change master branch references to main (#7288) * refactor: remove unused UI settings from opensearch_dashboards.yml * refactor: update Group import path for better module resolution * refactor: update PLUGIN_ID for consistency in analysis plugin * refactor: remove NavGroupType from navigation groups for cleaner code * docs: add README for ApplicationService with method explanations and usage in plugins * refactor: simplify mount functions in navigation groups and update ApplicationService methods * docs: update README for ApplicationService with new plugin example and navigation group definitions * docs: add README for plugin structure and example application * docs: enhance README with detailed plugin structure and examples for creating groups and applications * docs: add README for Wazuh Analysis plugin with structure and application examples * refactor: rename onAppStartup to start in ApplicationService and update usage in plugins * refactor: rename setup to register in ApplicationService and update related documentation * refactor: simplify app mount and cleanup logic in ApplicationService * refactor: move layout and createSideNavItems into applicationService and use from it * docs: update README for modifyAppGroupMount method to include subApps parameter * refactor: enhance documentation for getApps method in Group interface * refactor: update getApps and renderApp to utilize ApplicationService for layout management --------- Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Federico Rodriguez --- .eslintrc.js | 3 + docker/osd-dev/dev.yml | 2 + plugins/wazuh-analysis/.i18nrc.json | 7 + plugins/wazuh-analysis/README.md | 252 ++++++++++++ plugins/wazuh-analysis/common/constants.ts | 6 + .../wazuh-analysis/opensearch_dashboards.json | 8 + plugins/wazuh-analysis/package.json | 25 ++ plugins/wazuh-analysis/public/application.tsx | 19 + .../public/components/analysis-app.tsx | 18 + .../wazuh-analysis/public/groups/category.ts | 8 + .../groups/cloud-security/applications.ts | 93 +++++ .../cloud-security/apps/aws/application.tsx | 19 + .../cloud-security/apps/aws/aws-app.tsx | 9 + .../cloud-security/apps/aws/constants.ts | 9 + .../apps/docker/application.tsx | 19 + .../cloud-security/apps/docker/constants.ts | 12 + .../cloud-security/apps/docker/docker-app.tsx | 9 + .../apps/github/application.tsx | 19 + .../cloud-security/apps/github/constants.ts | 12 + .../cloud-security/apps/github/github-app.tsx | 9 + .../apps/google-cloud/application.tsx | 19 + .../apps/google-cloud/constants.ts | 12 + .../apps/google-cloud/google-cloud-app.tsx | 11 + .../apps/office-365/application.tsx | 19 + .../apps/office-365/constants.ts | 12 + .../apps/office-365/office-365-app.tsx | 11 + .../public/groups/cloud-security/constants.ts | 17 + .../public/groups/cloud-security/index.ts | 61 +++ .../groups/endpoint-security/applications.ts | 73 ++++ .../configuration-assesment/application.tsx | 19 + .../configuration-assesment-app.tsx | 11 + .../apps/configuration-assesment/constants.ts | 15 + .../apps/fim/application.tsx | 19 + .../endpoint-security/apps/fim/constants.ts | 9 + .../endpoint-security/apps/fim/fim-app.tsx | 9 + .../apps/malware-detection/application.tsx | 19 + .../apps/malware-detection/constants.ts | 15 + .../malware-detection-app.tsx | 11 + .../groups/endpoint-security/constants.ts | 17 + .../public/groups/endpoint-security/index.ts | 62 +++ .../security-operations/applications.ts | 73 ++++ .../apps/incident-response/application.tsx | 19 + .../apps/incident-response/constants.ts | 15 + .../incident-response-app.tsx | 11 + .../apps/it-hygiene/application.tsx | 19 + .../apps/it-hygiene/constants.ts | 15 + .../apps/it-hygiene/it-hygiene-app.tsx | 11 + .../regulatory-compliance/application.tsx | 19 + .../apps/regulatory-compliance/constants.ts | 15 + .../regulatory-compliance-app.tsx | 11 + .../groups/security-operations/constants.ts | 17 + .../groups/security-operations/index.ts | 62 +++ .../threat-intelligence/applications.ts | 73 ++++ .../apps/mitre-att&ck/application.tsx | 19 + .../apps/mitre-att&ck/constants.ts | 15 + .../apps/mitre-att&ck/mitre-att&ck-app.tsx | 11 + .../apps/threat-hunting/application.tsx | 19 + .../apps/threat-hunting/constants.ts | 15 + .../threat-hunting/threat-hunting-app.tsx | 11 + .../vulnerability-detection/application.tsx | 19 + .../apps/vulnerability-detection/constants.ts | 15 + .../vulnerability-detection-app.tsx | 11 + .../groups/threat-intelligence/constants.ts | 17 + .../groups/threat-intelligence/index.ts | 62 +++ plugins/wazuh-analysis/public/groups/types.ts | 10 + plugins/wazuh-analysis/public/index.ts | 9 + .../wazuh-analysis/public/plugin-services.ts | 4 + plugins/wazuh-analysis/public/plugin.ts | 53 +++ plugins/wazuh-analysis/public/types.ts | 15 + plugins/wazuh-analysis/public/utils/index.ts | 11 + plugins/wazuh-analysis/tsconfig.json | 17 + plugins/wazuh-analysis/yarn.lock | 12 + .../wazuh-core/common/logger/noop-logger.ts | 37 ++ .../global_search/global-search-page-item.tsx | 80 ++++ .../global_search/search-pages-command.tsx | 83 ++++ plugins/wazuh-core/public/plugin.ts | 64 +-- .../public/services/application/README.md | 140 +++++++ .../services/application/application.ts | 373 ++++++++++++++++++ .../services/application/create-layout.tsx | 18 + .../errors/app-updater-not-found-error.ts | 10 + .../public/services/application/layout.tsx | 29 ++ .../public/services/application/types.ts | 76 ++++ .../dashboard-security/dashboard-security.ts | 37 +- .../services/dashboard-security/types.ts | 6 +- plugins/wazuh-core/public/services/types.ts | 6 +- plugins/wazuh-core/public/types.ts | 3 + 86 files changed, 2621 insertions(+), 45 deletions(-) create mode 100644 plugins/wazuh-analysis/.i18nrc.json create mode 100644 plugins/wazuh-analysis/README.md create mode 100644 plugins/wazuh-analysis/common/constants.ts create mode 100644 plugins/wazuh-analysis/opensearch_dashboards.json create mode 100644 plugins/wazuh-analysis/package.json create mode 100644 plugins/wazuh-analysis/public/application.tsx create mode 100644 plugins/wazuh-analysis/public/components/analysis-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/category.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/applications.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/aws-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/docker-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/github/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/github/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/github/github-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/google-cloud-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/office-365-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/cloud-security/index.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/applications.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/configuration-assesment-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/fim-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/malware-detection-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/endpoint-security/index.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/applications.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/incident-response-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/it-hygiene-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/regulatory-compliance-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/security-operations/index.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/applications.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/mitre-att&ck-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/threat-hunting-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/application.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/vulnerability-detection-app.tsx create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/constants.ts create mode 100644 plugins/wazuh-analysis/public/groups/threat-intelligence/index.ts create mode 100644 plugins/wazuh-analysis/public/groups/types.ts create mode 100644 plugins/wazuh-analysis/public/index.ts create mode 100644 plugins/wazuh-analysis/public/plugin-services.ts create mode 100644 plugins/wazuh-analysis/public/plugin.ts create mode 100644 plugins/wazuh-analysis/public/types.ts create mode 100644 plugins/wazuh-analysis/public/utils/index.ts create mode 100644 plugins/wazuh-analysis/tsconfig.json create mode 100644 plugins/wazuh-analysis/yarn.lock create mode 100644 plugins/wazuh-core/common/logger/noop-logger.ts create mode 100644 plugins/wazuh-core/public/components/global_search/global-search-page-item.tsx create mode 100644 plugins/wazuh-core/public/components/global_search/search-pages-command.tsx create mode 100644 plugins/wazuh-core/public/services/application/README.md create mode 100644 plugins/wazuh-core/public/services/application/application.ts create mode 100644 plugins/wazuh-core/public/services/application/create-layout.tsx create mode 100644 plugins/wazuh-core/public/services/application/errors/app-updater-not-found-error.ts create mode 100644 plugins/wazuh-core/public/services/application/layout.tsx create mode 100644 plugins/wazuh-core/public/services/application/types.ts diff --git a/.eslintrc.js b/.eslintrc.js index 52b51c1614..13bdd257f6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -219,6 +219,8 @@ module.exports = { /* -------------------------------------------------------------------------- */ /* @typescript-eslint */ /* -------------------------------------------------------------------------- */ + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/no-unused-vars': [ 'error', @@ -359,6 +361,7 @@ module.exports = { }, }, ], + 'react/prop-types': 'off', }, }, { diff --git a/docker/osd-dev/dev.yml b/docker/osd-dev/dev.yml index c07d2b6c6d..29e34b139c 100755 --- a/docker/osd-dev/dev.yml +++ b/docker/osd-dev/dev.yml @@ -239,6 +239,7 @@ services: - ${OSD_PORT}:5601 environment: - 'LOGS=/proc/1/fd/1' + tty: true volumes: - osd_cache:/home/node/.cache - '${SRC}/main:/home/node/kbn/plugins/main' @@ -246,6 +247,7 @@ services: - '${SRC}/wazuh-check-updates:/home/node/kbn/plugins/wazuh-check-updates' - '${SRC}/wazuh-engine:/home/node/kbn/plugins/wazuh-engine' - '${SRC}/wazuh-fleet:/home/node/kbn/plugins/wazuh-fleet' + - '${SRC}/wazuh-analysis:/home/node/kbn/plugins/wazuh-analysis' - wd_certs:/home/node/kbn/certs/ - ${WAZUH_DASHBOARD_CONF}:/home/node/kbn/config/opensearch_dashboards.yml - ./config/${OSD_MAJOR}/osd/wazuh.yml:/home/node/kbn/data/wazuh/config/wazuh.yml diff --git a/plugins/wazuh-analysis/.i18nrc.json b/plugins/wazuh-analysis/.i18nrc.json new file mode 100644 index 0000000000..2be1de408f --- /dev/null +++ b/plugins/wazuh-analysis/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "wazuhAnalysis", + "paths": { + "wazuhAnalysis": "." + }, + "translations": ["translations/en-US.json"] +} diff --git a/plugins/wazuh-analysis/README.md b/plugins/wazuh-analysis/README.md new file mode 100644 index 0000000000..a5c7789277 --- /dev/null +++ b/plugins/wazuh-analysis/README.md @@ -0,0 +1,252 @@ +# **General Plugin Structure and Functionality** + +A **plugin** in this context follows a **modular and organized structure**, dividing its functionalities into **thematic groups**. Each group contains specific applications, each with its own logic and components. + +Here’s a step-by-step guide on how to **create a new plugin** with **groups and applications**, using snippets based on the provided structure. + +--- + +## **1️⃣ Creating the Plugin Structure** + +``` +πŸ“‚ **`plugins/my-security-plugin/`** _(Root directory of the plugin)_ +│── πŸ“‚ `public/` _(Frontend-related code)_ +β”‚ │── πŸ“‚ `groups/` _(Contains the different security-related groups)_ +β”‚ β”‚ │── πŸ“‚ `network-security/` _(New group: Network Security)_ +β”‚ β”‚ β”‚ │── πŸ“‚ `apps/` _(Contains the individual applications in this group)_ +β”‚ β”‚ β”‚ β”‚ │── πŸ“‚ `firewall-monitoring/` _(First app: Firewall Monitoring)_ +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `application.tsx` _(App entry point and rendering logic)_ +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `firewall-monitoring-app.tsx` _(Main React component for the app)_ +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `constants.ts` _(App-specific constants, titles, and IDs)_ +β”‚ β”‚ β”‚ β”‚ │── πŸ“‚ `network-anomaly-detection/` _(Second app: Network Anomaly Detection)_ +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `application.tsx` +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `network-anomaly-detection-app.tsx` +β”‚ β”‚ β”‚ β”‚ β”‚ │── πŸ“„ `constants.ts` +β”‚ β”‚ β”‚ │── πŸ“„ `applications.ts` _(Registers all apps in this group)_ +β”‚ β”‚ β”‚ │── πŸ“„ `constants.ts` _(Defines the group ID, title, and description)_ +β”‚ β”‚ β”‚ │── πŸ“„ `index.ts` _(Registers the group and navigation details)_ +β”‚ │── πŸ“„ `category.ts` _(Defines categories for grouping apps in UI)_ +``` + +--- + +## **2️⃣ Creating a Group: `network-security`** + +A **group** represents a **category** of related applications. + +πŸ“Œ **File:** `public/groups/network-security/constants.ts` + +```ts +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../common/constants'; + +export const NETWORK_SECURITY_ID = 'network_security'; +export const NETWORK_SECURITY_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${NETWORK_SECURITY_ID}`, + { defaultMessage: 'Network Security' }, +); +export const NETWORK_SECURITY_DESCRIPTION = i18n.translate( + `${PLUGIN_ID}.category.${NETWORK_SECURITY_ID}.description`, + { defaultMessage: 'Monitor and secure your network infrastructure.' }, +); +``` + +πŸ“Œ **File:** `public/groups/network-security/index.ts` + +```ts +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppUpdater, + ChromeNavGroup, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { CATEGORY } from '../category'; +import { Group } from '../../../../wazuh-core/public/services/application/types'; +import { getNetworkSecurityApps } from './applications'; +import { + NETWORK_SECURITY_DESCRIPTION, + NETWORK_SECURITY_ID, + NETWORK_SECURITY_TITLE, +} from './constants'; + +export const NetworkSecurityNavGroup: Group = { + getId: () => NETWORK_SECURITY_ID, + getTitle: () => NETWORK_SECURITY_TITLE, + getDescription: () => NETWORK_SECURITY_DESCRIPTION, + + getNavGroup(): ChromeNavGroup { + return { + id: NETWORK_SECURITY_ID, + title: NETWORK_SECURITY_TITLE, + description: NETWORK_SECURITY_DESCRIPTION, + }; + }, + + getAppGroup(): App { + return { + id: NETWORK_SECURITY_ID, + title: NETWORK_SECURITY_TITLE, + category: CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: NETWORK_SECURITY_ID, + title: NETWORK_SECURITY_TITLE, + category: CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getNetworkSecurityApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps( + applicationService: ApplicationService, + updater$: Subject, + ): App[] { + return getNetworkSecurityApps(applicationService, updater$); + }, +}; +``` + +--- + +## **3️⃣ Creating an Application: `firewall-monitoring`** + +πŸ“Œ **File:** `public/groups/network-security/apps/firewall-monitoring/constants.ts` + +```ts +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { NETWORK_SECURITY_ID } from '../../constants'; + +export const FIREWALL_MONITORING_ID = buildSubAppId( + NETWORK_SECURITY_ID, + 'firewall-monitoring', +); +export const FIREWALL_MONITORING_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${FIREWALL_MONITORING_ID}`, + { + defaultMessage: 'Firewall Monitoring', + }, +); +``` + +πŸ“Œ **File:** `public/groups/network-security/apps/firewall-monitoring/firewall-monitoring-app.tsx` + +```tsx +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { FIREWALL_MONITORING_TITLE } from './constants'; + +interface FirewallMonitoringProps { + params: AppMountParameters; +} + +export const FirewallMonitoringApp = (_props: FirewallMonitoringProps) => ( + <>{FIREWALL_MONITORING_TITLE} App +); +``` + +πŸ“Œ **File:** `public/groups/network-security/apps/firewall-monitoring/application.tsx` + +```tsx +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { NetworkSecurityNavGroup } from '../..'; +import { Layout } from '../../../layout'; +import { createSideNavItems } from '../../../side-nav'; +import { NETWORK_SECURITY_TITLE } from '../../constants'; +import { FirewallMonitoringApp } from './firewall-monitoring-app'; +import { FIREWALL_MONITORING_ID } from './constants'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; +``` + +--- + +## **4️⃣ Registering Applications in the Group** + +πŸ“Œ **File:** `public/groups/network-security/applications.ts` + +```ts +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppNavLinkStatus, + AppUpdater, +} from '../../../../../src/core/public'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { + FIREWALL_MONITORING_ID, + FIREWALL_MONITORING_TITLE, +} from './apps/firewall-monitoring/constants'; +import { NetworkSecurityNavGroup } from '.'; + +export function getNetworkSecurityApps( + applicationService?: ApplicationService, + updater$?: Subject, +): App[] { + const networkSecurityLayout = applicationService?.createLayout( + NetworkSecurityNavGroup, + ); + + return [ + { + id: FIREWALL_MONITORING_ID, + title: FIREWALL_MONITORING_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/firewall-monitoring/application' + ); + + return await renderApp(params, { + Layout: networkSecurityLayout!(FIREWALL_MONITORING_ID), + }); + }, + }, + ]; +} +``` + +--- + +## **5️⃣ Updating `category.ts` to Include the New Group** + +πŸ“Œ **File:** `public/groups/category.ts` + +```ts +import { AppCategory } from 'opensearch-dashboards/public'; +import { PLUGIN_TITLE, PLUGIN_ID } from '../../common/constants'; + +export const CATEGORY: AppCategory = Object.freeze({ + id: PLUGIN_ID, + label: PLUGIN_TITLE, + order: 5000, +}); +``` diff --git a/plugins/wazuh-analysis/common/constants.ts b/plugins/wazuh-analysis/common/constants.ts new file mode 100644 index 0000000000..37f9e94fb4 --- /dev/null +++ b/plugins/wazuh-analysis/common/constants.ts @@ -0,0 +1,6 @@ +import { i18n } from '@osd/i18n'; + +export const PLUGIN_ID = 'wazuhAnalysis'; +export const ANALYSIS_PLUGIN_TITLE = i18n.translate(`${PLUGIN_ID}.title`, { + defaultMessage: 'Analysis', +}); diff --git a/plugins/wazuh-analysis/opensearch_dashboards.json b/plugins/wazuh-analysis/opensearch_dashboards.json new file mode 100644 index 0000000000..1dc0f563d9 --- /dev/null +++ b/plugins/wazuh-analysis/opensearch_dashboards.json @@ -0,0 +1,8 @@ +{ + "id": "wazuhAnalysis", + "version": "5.0.0-00", + "opensearchDashboardsVersion": "opensearchDashboards", + "server": false, + "ui": true, + "requiredPlugins": ["opensearchDashboardsUtils", "wazuhCore"] +} diff --git a/plugins/wazuh-analysis/package.json b/plugins/wazuh-analysis/package.json new file mode 100644 index 0000000000..a4c3b166e6 --- /dev/null +++ b/plugins/wazuh-analysis/package.json @@ -0,0 +1,25 @@ +{ + "name": "wazuh-analysis", + "version": "5.0.0", + "revision": "00", + "pluginPlatform": { + "version": "2.18.0" + }, + "description": "Wazuh Analysis", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build --opensearch-dashboards-version=$OPENSEARCH_DASHBOARDS_VERSION", + "plugin-helpers": "node ../../scripts/plugin_helpers", + "osd": "node ../../scripts/osd", + "test:ui:runner": "node ../../scripts/functional_test_runner.js", + "test:server": "plugin-helpers test:server", + "test:browser": "plugin-helpers test:browser", + "test:jest": "node scripts/jest --runInBand", + "test:jest:runner": "node scripts/runner test" + }, + "dependencies": {}, + "devDependencies": { + "@testing-library/user-event": "^14.5.2", + "@types/": "testing-library/user-event" + } +} diff --git a/plugins/wazuh-analysis/public/application.tsx b/plugins/wazuh-analysis/public/application.tsx new file mode 100644 index 0000000000..443feec151 --- /dev/null +++ b/plugins/wazuh-analysis/public/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { + AnalysisApp, + AnalysisAppDependencies, +} from './components/analysis-app'; + +export const renderApp = async ( + { history, element }: AppMountParameters, + dependencies: AnalysisAppDependencies, +) => { + ReactDOM.render( + , + element, + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/plugins/wazuh-analysis/public/components/analysis-app.tsx b/plugins/wazuh-analysis/public/components/analysis-app.tsx new file mode 100644 index 0000000000..05923fbf81 --- /dev/null +++ b/plugins/wazuh-analysis/public/components/analysis-app.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { EuiPage, EuiPageBody, EuiPageContentBody } from '@elastic/eui'; +import { AppMountParameters } from '../../../../src/core/public'; + +export interface AnalysisAppDependencies {} + +interface AnalysisAppProps { + history: AppMountParameters['history']; + dependencies: AnalysisAppDependencies; +} + +export const AnalysisApp = (_props: AnalysisAppProps) => ( + + + AnalysisApp + + +); diff --git a/plugins/wazuh-analysis/public/groups/category.ts b/plugins/wazuh-analysis/public/groups/category.ts new file mode 100644 index 0000000000..f09e6e802c --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/category.ts @@ -0,0 +1,8 @@ +import { AppCategory } from 'opensearch-dashboards/public'; +import { ANALYSIS_PLUGIN_TITLE, PLUGIN_ID } from '../../common/constants'; + +export const CATEGORY: AppCategory = Object.freeze({ + id: PLUGIN_ID, + label: ANALYSIS_PLUGIN_TITLE, + order: 5000, +}); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/applications.ts b/plugins/wazuh-analysis/public/groups/cloud-security/applications.ts new file mode 100644 index 0000000000..0da192facc --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/applications.ts @@ -0,0 +1,93 @@ +import { Subject } from 'rxjs'; +import { + AppMountParameters, + AppNavLinkStatus, + AppUpdater, +} from '../../../../../src/core/public'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { DOCKER_ID, DOCKER_TITLE } from './apps/docker/constants'; +import { AWS_ID, AWS_TITLE } from './apps/aws/constants'; +import { + GOOGLE_CLOUD_ID, + GOOGLE_CLOUD_TITLE, +} from './apps/google-cloud/constants'; +import { GITHUB_ID, GITHUB_TITLE } from './apps/github/constants'; +import { OFFICE365_ID, OFFICE365_TITLE } from './apps/office-365/constants'; +import { CloudSecurityNavGroup } from '.'; + +export function getCloudSecurityApps( + applicationService?: ApplicationService, + updater$?: Subject, +) { + const cloudSecurityLayout = applicationService?.createLayout( + CloudSecurityNavGroup, + ); + + return [ + { + id: DOCKER_ID, + title: DOCKER_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/docker/application'); + + return await renderApp(params, { + Layout: cloudSecurityLayout!(DOCKER_ID), + }); + }, + }, + { + id: AWS_ID, + title: AWS_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/aws/application'); + + return await renderApp(params, { + Layout: cloudSecurityLayout!(AWS_ID), + }); + }, + }, + { + id: GOOGLE_CLOUD_ID, + title: GOOGLE_CLOUD_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/google-cloud/application'); + + return await renderApp(params, { + Layout: cloudSecurityLayout!(GOOGLE_CLOUD_ID), + }); + }, + }, + { + id: GITHUB_ID, + title: GITHUB_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/github/application'); + + return await renderApp(params, { + Layout: cloudSecurityLayout!(GITHUB_ID), + }); + }, + }, + { + id: OFFICE365_ID, + title: OFFICE365_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/office-365/application'); + + return await renderApp(params, { + Layout: cloudSecurityLayout!(OFFICE365_ID), + }); + }, + }, + ]; +} diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/application.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/application.tsx new file mode 100644 index 0000000000..9e9a6f5d13 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { AwsApp } from './aws-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/aws-app.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/aws-app.tsx new file mode 100644 index 0000000000..bad4702ad5 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/aws-app.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { AWS_TITLE } from './constants'; + +interface AwsAppProps { + params: AppMountParameters; +} + +export const AwsApp = (_props: AwsAppProps) => <>{AWS_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/constants.ts new file mode 100644 index 0000000000..e8eebba268 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/aws/constants.ts @@ -0,0 +1,9 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { CLOUD_SECURITY_ID } from '../../constants'; + +export const AWS_ID = buildSubAppId(CLOUD_SECURITY_ID, 'aws'); +export const AWS_TITLE = i18n.translate(`${PLUGIN_ID}.category.${AWS_ID}`, { + defaultMessage: 'AWS', +}); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/application.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/application.tsx new file mode 100644 index 0000000000..9d040dfcea --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { DockerApp } from './docker-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/constants.ts new file mode 100644 index 0000000000..805f73d706 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/constants.ts @@ -0,0 +1,12 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { CLOUD_SECURITY_ID } from '../../constants'; + +export const DOCKER_ID = buildSubAppId(CLOUD_SECURITY_ID, 'docker'); +export const DOCKER_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${DOCKER_ID}`, + { + defaultMessage: 'Docker', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/docker-app.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/docker-app.tsx new file mode 100644 index 0000000000..99eeb41788 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/docker/docker-app.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { DOCKER_TITLE } from './constants'; + +interface DockerAppProps { + params: AppMountParameters; +} + +export const DockerApp = (_props: DockerAppProps) => <>{DOCKER_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/application.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/application.tsx new file mode 100644 index 0000000000..8d5064bccf --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { GithubApp } from './github-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/constants.ts new file mode 100644 index 0000000000..c699dfebf5 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/constants.ts @@ -0,0 +1,12 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { CLOUD_SECURITY_ID } from '../../constants'; + +export const GITHUB_ID = buildSubAppId(CLOUD_SECURITY_ID, 'github'); +export const GITHUB_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${GITHUB_ID}`, + { + defaultMessage: 'Github', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/github-app.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/github-app.tsx new file mode 100644 index 0000000000..7a444dfecf --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/github/github-app.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { GITHUB_TITLE } from './constants'; + +interface GithubAppProps { + params: AppMountParameters; +} + +export const GithubApp = (_props: GithubAppProps) => <>{GITHUB_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/application.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/application.tsx new file mode 100644 index 0000000000..37352d5c01 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { GoogleCloudApp } from './google-cloud-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/constants.ts new file mode 100644 index 0000000000..3d70808035 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/constants.ts @@ -0,0 +1,12 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { CLOUD_SECURITY_ID } from '../../constants'; + +export const GOOGLE_CLOUD_ID = buildSubAppId(CLOUD_SECURITY_ID, 'google_cloud'); +export const GOOGLE_CLOUD_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${GOOGLE_CLOUD_ID}`, + { + defaultMessage: 'Google Cloud', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/google-cloud-app.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/google-cloud-app.tsx new file mode 100644 index 0000000000..63d32f395b --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/google-cloud/google-cloud-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { GOOGLE_CLOUD_TITLE } from './constants'; + +interface GoogleCloudAppProps { + params: AppMountParameters; +} + +export const GoogleCloudApp = (_props: GoogleCloudAppProps) => ( + <>{GOOGLE_CLOUD_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/application.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/application.tsx new file mode 100644 index 0000000000..05cf66efa4 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { Office365App } from './office-365-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/constants.ts new file mode 100644 index 0000000000..6db1ea3954 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/constants.ts @@ -0,0 +1,12 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { CLOUD_SECURITY_ID } from '../../constants'; + +export const OFFICE365_ID = buildSubAppId(CLOUD_SECURITY_ID, 'office365'); +export const OFFICE365_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${OFFICE365_ID}`, + { + defaultMessage: 'Office 365', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/office-365-app.tsx b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/office-365-app.tsx new file mode 100644 index 0000000000..c008cc855f --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/apps/office-365/office-365-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { OFFICE365_TITLE } from './constants'; + +interface Office365AppProps { + params: AppMountParameters; +} + +export const Office365App = (_props: Office365AppProps) => ( + <>{OFFICE365_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/constants.ts b/plugins/wazuh-analysis/public/groups/cloud-security/constants.ts new file mode 100644 index 0000000000..10f6a46948 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/constants.ts @@ -0,0 +1,17 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../common/constants'; + +export const CLOUD_SECURITY_ID = 'cloud_security'; +export const CLOUD_SECURITY_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${CLOUD_SECURITY_ID}`, + { + defaultMessage: 'Cloud Security', + }, +); +export const CLOUD_SECURITY_DESCRIPTION = i18n.translate( + `${PLUGIN_ID}.category.${CLOUD_SECURITY_ID}.description`, + { + defaultMessage: + 'Monitoring and protection for cloud environments against security threats.', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/cloud-security/index.ts b/plugins/wazuh-analysis/public/groups/cloud-security/index.ts new file mode 100644 index 0000000000..3361c8eea7 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/cloud-security/index.ts @@ -0,0 +1,61 @@ +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppUpdater, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { CATEGORY } from '../category'; +import { Group } from '../../../../wazuh-core/public/services/application/types'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { getCloudSecurityApps } from './applications'; +import { + CLOUD_SECURITY_DESCRIPTION, + CLOUD_SECURITY_ID, + CLOUD_SECURITY_TITLE, +} from './constants'; + +export const CloudSecurityNavGroup: Group = { + getId: () => CLOUD_SECURITY_ID, + getTitle: () => CLOUD_SECURITY_TITLE, + getDescription: () => CLOUD_SECURITY_DESCRIPTION, + + getNavGroup() { + return { + id: CLOUD_SECURITY_ID, + title: CLOUD_SECURITY_TITLE, + description: CLOUD_SECURITY_DESCRIPTION, + }; + }, + + getAppGroup() { + return { + id: CLOUD_SECURITY_ID, + title: CLOUD_SECURITY_TITLE, + category: CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: CLOUD_SECURITY_ID, + title: CLOUD_SECURITY_TITLE, + category: CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getCloudSecurityApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps( + applicationService: ApplicationService, + updater$: Subject, + ): App[] { + return getCloudSecurityApps(applicationService, updater$); + }, +}; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/applications.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/applications.ts new file mode 100644 index 0000000000..b84979f48b --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/applications.ts @@ -0,0 +1,73 @@ +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppNavLinkStatus, + AppUpdater, +} from '../../../../../src/core/public'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { + CONFIGURATION_ASSESSMENT_ID, + CONFIGURATION_ASSESSMENT_TITLE, +} from './apps/configuration-assesment/constants'; +import { FIM_ID, FIM_TITLE } from './apps/fim/constants'; +import { + MALWARE_DETECTION_ID, + MALWARE_DETECTION_TITLE, +} from './apps/malware-detection/constants'; +import { EndpointSecurityNavGroup } from '.'; + +export function getEndpointSecurityApps( + applicationService?: ApplicationService, + updater$?: Subject, +): App[] { + const endpointSecurityLayout = applicationService?.createLayout( + EndpointSecurityNavGroup, + ); + + return [ + { + id: CONFIGURATION_ASSESSMENT_ID, + title: CONFIGURATION_ASSESSMENT_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/configuration-assesment/application' + ); + + return await renderApp(params, { + Layout: endpointSecurityLayout!(CONFIGURATION_ASSESSMENT_ID), + }); + }, + }, + { + id: MALWARE_DETECTION_ID, + title: MALWARE_DETECTION_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/malware-detection/application' + ); + + return await renderApp(params, { + Layout: endpointSecurityLayout!(MALWARE_DETECTION_ID), + }); + }, + }, + { + id: FIM_ID, + title: FIM_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/fim/application'); + + return await renderApp(params, { + Layout: endpointSecurityLayout!(FIM_ID), + }); + }, + }, + ]; +} diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/application.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/application.tsx new file mode 100644 index 0000000000..67fdcdf5e9 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { ConfigurationAssessmentApp } from './configuration-assesment-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/configuration-assesment-app.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/configuration-assesment-app.tsx new file mode 100644 index 0000000000..909361d385 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/configuration-assesment-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { CONFIGURATION_ASSESSMENT_TITLE } from './constants'; + +interface ConfigurationAssessmentAppProps { + params: AppMountParameters; +} + +export const ConfigurationAssessmentApp = ( + _props: ConfigurationAssessmentAppProps, +) => <>{CONFIGURATION_ASSESSMENT_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/constants.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/constants.ts new file mode 100644 index 0000000000..bec8b264f9 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/configuration-assesment/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { ENDPOINT_SECURITY_ID } from '../../constants'; + +export const CONFIGURATION_ASSESSMENT_ID = buildSubAppId( + ENDPOINT_SECURITY_ID, + 'configuration_assessment', +); +export const CONFIGURATION_ASSESSMENT_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${CONFIGURATION_ASSESSMENT_ID}`, + { + defaultMessage: 'Configuration Assessment', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/application.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/application.tsx new file mode 100644 index 0000000000..0706986848 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { FimApp } from './fim-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/constants.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/constants.ts new file mode 100644 index 0000000000..83d3df4c0d --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/constants.ts @@ -0,0 +1,9 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { ENDPOINT_SECURITY_ID } from '../../constants'; + +export const FIM_ID = buildSubAppId(ENDPOINT_SECURITY_ID, 'fim'); +export const FIM_TITLE = i18n.translate(`${PLUGIN_ID}.category.${FIM_ID}`, { + defaultMessage: 'File Integrity Monitoring', +}); diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/fim-app.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/fim-app.tsx new file mode 100644 index 0000000000..5c98693c6e --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/fim/fim-app.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { FIM_TITLE } from './constants'; + +interface FimAppProps { + params: AppMountParameters; +} + +export const FimApp = (_props: FimAppProps) => <>{FIM_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/application.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/application.tsx new file mode 100644 index 0000000000..a47550f1a3 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { MalwareDetectionApp } from './malware-detection-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/constants.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/constants.ts new file mode 100644 index 0000000000..bb0ef22c50 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { ENDPOINT_SECURITY_ID } from '../../constants'; + +export const MALWARE_DETECTION_ID = buildSubAppId( + ENDPOINT_SECURITY_ID, + 'malware_detection', +); +export const MALWARE_DETECTION_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${MALWARE_DETECTION_ID}`, + { + defaultMessage: 'Malware Detection', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/malware-detection-app.tsx b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/malware-detection-app.tsx new file mode 100644 index 0000000000..3100c2693f --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/apps/malware-detection/malware-detection-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { MALWARE_DETECTION_TITLE } from './constants'; + +interface MalwareDetectionAppProps { + params: AppMountParameters; +} + +export const MalwareDetectionApp = (_props: MalwareDetectionAppProps) => ( + <>{MALWARE_DETECTION_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/constants.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/constants.ts new file mode 100644 index 0000000000..0e73dfa3e6 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/constants.ts @@ -0,0 +1,17 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../common/constants'; + +export const ENDPOINT_SECURITY_ID = 'endpoint_security'; +export const ENDPOINT_SECURITY_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${ENDPOINT_SECURITY_ID}`, + { + defaultMessage: 'Endpoint Security', + }, +); +export const ENDPOINT_SECURITY_DESCRIPTION = i18n.translate( + `${PLUGIN_ID}.category.${ENDPOINT_SECURITY_ID}.description`, + { + defaultMessage: + 'Advanced monitoring and protection for devices against security threats.', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/endpoint-security/index.ts b/plugins/wazuh-analysis/public/groups/endpoint-security/index.ts new file mode 100644 index 0000000000..5c0abd0dc1 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/endpoint-security/index.ts @@ -0,0 +1,62 @@ +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppUpdater, + ChromeNavGroup, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { CATEGORY } from '../category'; +import { Group } from '../../../../wazuh-core/public/services/application/types'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { getEndpointSecurityApps } from './applications'; +import { + ENDPOINT_SECURITY_DESCRIPTION, + ENDPOINT_SECURITY_ID, + ENDPOINT_SECURITY_TITLE, +} from './constants'; + +export const EndpointSecurityNavGroup: Group = { + getId: () => ENDPOINT_SECURITY_ID, + getTitle: () => ENDPOINT_SECURITY_TITLE, + getDescription: () => ENDPOINT_SECURITY_DESCRIPTION, + + getNavGroup(): ChromeNavGroup { + return { + id: ENDPOINT_SECURITY_ID, + title: ENDPOINT_SECURITY_TITLE, + description: ENDPOINT_SECURITY_DESCRIPTION, + }; + }, + + getAppGroup(): App { + return { + id: ENDPOINT_SECURITY_ID, + title: ENDPOINT_SECURITY_TITLE, + category: CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: ENDPOINT_SECURITY_ID, + title: ENDPOINT_SECURITY_TITLE, + category: CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getEndpointSecurityApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps( + applicationService: ApplicationService, + updater$: Subject, + ): App[] { + return getEndpointSecurityApps(applicationService, updater$); + }, +}; diff --git a/plugins/wazuh-analysis/public/groups/security-operations/applications.ts b/plugins/wazuh-analysis/public/groups/security-operations/applications.ts new file mode 100644 index 0000000000..3ca72b00fb --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/applications.ts @@ -0,0 +1,73 @@ +import { Subject } from 'rxjs'; +import { + AppMountParameters, + AppNavLinkStatus, + AppUpdater, + App, +} from '../../../../../src/core/public'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { + INCIDENT_RESPONSE_ID, + INCIDENT_RESPONSE_TITLE, +} from './apps/incident-response/constants'; +import { IT_HYGIENE_ID, IT_HYGIENE_TITLE } from './apps/it-hygiene/constants'; +import { + REGULATORY_COMPLIANCE_ID, + REGULATORY_COMPLIANCE_TITLE, +} from './apps/regulatory-compliance/constants'; +import { SecurityOperationsNavGroup } from '.'; + +export function getSecurityOperationsApps( + applicationService?: ApplicationService, + updater$?: Subject, +): App[] { + const securityOperationsLayout = applicationService?.createLayout( + SecurityOperationsNavGroup, + ); + + return [ + { + id: REGULATORY_COMPLIANCE_ID, + title: REGULATORY_COMPLIANCE_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/regulatory-compliance/application' + ); + + return await renderApp(params, { + Layout: securityOperationsLayout!(REGULATORY_COMPLIANCE_ID), + }); + }, + }, + { + id: IT_HYGIENE_ID, + title: IT_HYGIENE_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/it-hygiene/application'); + + return await renderApp(params, { + Layout: securityOperationsLayout!(IT_HYGIENE_ID), + }); + }, + }, + { + id: INCIDENT_RESPONSE_ID, + title: INCIDENT_RESPONSE_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/incident-response/application' + ); + + return await renderApp(params, { + Layout: securityOperationsLayout!(INCIDENT_RESPONSE_ID), + }); + }, + }, + ]; +} diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/application.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/application.tsx new file mode 100644 index 0000000000..4240fc7f78 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { IncidentResponseApp } from './incident-response-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/constants.ts b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/constants.ts new file mode 100644 index 0000000000..2a95bcfb26 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { SECURITY_OPERATIONS_ID } from '../../constants'; + +export const INCIDENT_RESPONSE_ID = buildSubAppId( + SECURITY_OPERATIONS_ID, + 'incident_response', +); +export const INCIDENT_RESPONSE_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${INCIDENT_RESPONSE_ID}`, + { + defaultMessage: 'Incident Response', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/incident-response-app.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/incident-response-app.tsx new file mode 100644 index 0000000000..01081d5798 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/incident-response/incident-response-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { INCIDENT_RESPONSE_TITLE } from './constants'; + +interface IncidentResponseAppProps { + params: AppMountParameters; +} + +export const IncidentResponseApp = (_props: IncidentResponseAppProps) => ( + <>{INCIDENT_RESPONSE_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/application.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/application.tsx new file mode 100644 index 0000000000..83b7701979 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { ItHygieneApp } from './it-hygiene-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/constants.ts b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/constants.ts new file mode 100644 index 0000000000..4db35e0925 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { SECURITY_OPERATIONS_ID } from '../../constants'; + +export const IT_HYGIENE_ID = buildSubAppId( + SECURITY_OPERATIONS_ID, + 'it_hygiene', +); +export const IT_HYGIENE_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${IT_HYGIENE_ID}`, + { + defaultMessage: 'IT Hygiene', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/it-hygiene-app.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/it-hygiene-app.tsx new file mode 100644 index 0000000000..2732594c09 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/it-hygiene/it-hygiene-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { IT_HYGIENE_TITLE } from './constants'; + +interface ItHygieneAppProps { + params: AppMountParameters; +} + +export const ItHygieneApp = (_props: ItHygieneAppProps) => ( + <>{IT_HYGIENE_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/application.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/application.tsx new file mode 100644 index 0000000000..7a30b9d608 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { RegulatoryComplianceApp } from './regulatory-compliance-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/constants.ts b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/constants.ts new file mode 100644 index 0000000000..028b6e2052 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { SECURITY_OPERATIONS_ID } from '../../constants'; + +export const REGULATORY_COMPLIANCE_ID = buildSubAppId( + SECURITY_OPERATIONS_ID, + 'regulatory_compliance', +); +export const REGULATORY_COMPLIANCE_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${REGULATORY_COMPLIANCE_ID}`, + { + defaultMessage: 'Regulatory Compliance', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/regulatory-compliance-app.tsx b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/regulatory-compliance-app.tsx new file mode 100644 index 0000000000..4b2c582d7c --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/apps/regulatory-compliance/regulatory-compliance-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { REGULATORY_COMPLIANCE_TITLE } from './constants'; + +interface RegulatoryComplianceAppProps { + params: AppMountParameters; +} + +export const RegulatoryComplianceApp = ( + _props: RegulatoryComplianceAppProps, +) => <>{REGULATORY_COMPLIANCE_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/security-operations/constants.ts b/plugins/wazuh-analysis/public/groups/security-operations/constants.ts new file mode 100644 index 0000000000..0455237174 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/constants.ts @@ -0,0 +1,17 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../common/constants'; + +export const SECURITY_OPERATIONS_ID = 'security_operations'; +export const SECURITY_OPERATIONS_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${SECURITY_OPERATIONS_ID}`, + { + defaultMessage: 'Security Operations', + }, +); +export const SECURITY_OPERATIONS_DESCRIPTION = i18n.translate( + `${PLUGIN_ID}.category.${SECURITY_OPERATIONS_ID}.description`, + { + defaultMessage: + 'Advanced monitoring and protection for devices against security threats.', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/security-operations/index.ts b/plugins/wazuh-analysis/public/groups/security-operations/index.ts new file mode 100644 index 0000000000..f37e8c9881 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/security-operations/index.ts @@ -0,0 +1,62 @@ +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppUpdater, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { CATEGORY } from '../category'; +import { Group } from '../../../../wazuh-core/public/services/application/types'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { getSecurityOperationsApps } from './applications'; +import { + SECURITY_OPERATIONS_DESCRIPTION, + SECURITY_OPERATIONS_ID, + SECURITY_OPERATIONS_TITLE, +} from './constants'; + +export const SecurityOperationsNavGroup: Group = + { + getId: () => SECURITY_OPERATIONS_ID, + getTitle: () => SECURITY_OPERATIONS_TITLE, + getDescription: () => SECURITY_OPERATIONS_DESCRIPTION, + + getNavGroup() { + return { + id: SECURITY_OPERATIONS_ID, + title: SECURITY_OPERATIONS_TITLE, + description: SECURITY_OPERATIONS_DESCRIPTION, + }; + }, + + getAppGroup() { + return { + id: SECURITY_OPERATIONS_ID, + title: SECURITY_OPERATIONS_TITLE, + category: CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: SECURITY_OPERATIONS_ID, + title: SECURITY_OPERATIONS_TITLE, + category: CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getSecurityOperationsApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps( + applicationService: ApplicationService, + updater$: Subject, + ): App[] { + return getSecurityOperationsApps(applicationService, updater$); + }, + }; diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/applications.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/applications.ts new file mode 100644 index 0000000000..583b5cb4fe --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/applications.ts @@ -0,0 +1,73 @@ +import { Subject } from 'rxjs'; +import { + AppMountParameters, + AppNavLinkStatus, + AppUpdater, +} from '../../../../../src/core/public'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { + MITRE_ATTACK_ID, + MITRE_ATTACK_TITLE, +} from './apps/mitre-att&ck/constants'; +import { + THREAT_HUNTING_ID, + THREAT_HUNTING_TITLE, +} from './apps/threat-hunting/constants'; +import { + VULNERABILITY_DETECTION_ID, + VULNERABILITY_DETECTION_TITLE, +} from './apps/vulnerability-detection/constants'; +import { ThreatIntelligenceNavGroup } from '.'; + +export function getThreatIntelligenceApps( + applicationService?: ApplicationService, + updater$?: Subject, +) { + const threatIntelligenceLayout = applicationService?.createLayout( + ThreatIntelligenceNavGroup, + ); + + return [ + { + id: THREAT_HUNTING_ID, + title: THREAT_HUNTING_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/threat-hunting/application'); + + return await renderApp(params, { + Layout: threatIntelligenceLayout!(THREAT_HUNTING_ID), + }); + }, + }, + { + id: VULNERABILITY_DETECTION_ID, + title: VULNERABILITY_DETECTION_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import( + './apps/vulnerability-detection/application' + ); + + return await renderApp(params, { + Layout: threatIntelligenceLayout!(VULNERABILITY_DETECTION_ID), + }); + }, + }, + { + id: MITRE_ATTACK_ID, + title: MITRE_ATTACK_TITLE, + navLinkStatus: AppNavLinkStatus.hidden, + updater$, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/mitre-att&ck/application'); + + return await renderApp(params, { + Layout: threatIntelligenceLayout!(MITRE_ATTACK_ID), + }); + }, + }, + ]; +} diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/application.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/application.tsx new file mode 100644 index 0000000000..bab1547a29 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { MitreAttackApp } from './mitre-att&ck-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/constants.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/constants.ts new file mode 100644 index 0000000000..f33bc0ff69 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { THREAT_INTELLIGENCE_ID } from '../../constants'; + +export const MITRE_ATTACK_ID = buildSubAppId( + THREAT_INTELLIGENCE_ID, + 'mitre_attack', +); +export const MITRE_ATTACK_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${MITRE_ATTACK_ID}`, + { + defaultMessage: 'MITRE ATT&CK', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/mitre-att&ck-app.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/mitre-att&ck-app.tsx new file mode 100644 index 0000000000..d5d4b02b9d --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/mitre-att&ck/mitre-att&ck-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { MITRE_ATTACK_TITLE } from './constants'; + +interface MitreAttackAppProps { + params: AppMountParameters; +} + +export const MitreAttackApp = (_props: MitreAttackAppProps) => ( + <>{MITRE_ATTACK_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/application.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/application.tsx new file mode 100644 index 0000000000..43acd78ef0 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { ThreatHuntingApp } from './threat-hunting-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/constants.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/constants.ts new file mode 100644 index 0000000000..d4e8743adf --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { THREAT_INTELLIGENCE_ID } from '../../constants'; + +export const THREAT_HUNTING_ID = buildSubAppId( + THREAT_INTELLIGENCE_ID, + 'threat_hunting', +); +export const THREAT_HUNTING_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${THREAT_HUNTING_ID}`, + { + defaultMessage: 'Threat Hunting', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/threat-hunting-app.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/threat-hunting-app.tsx new file mode 100644 index 0000000000..d37cfcf232 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/threat-hunting/threat-hunting-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { THREAT_HUNTING_TITLE } from './constants'; + +interface ThreatHuntingAppProps { + params: AppMountParameters; +} + +export const ThreatHuntingApp = (_props: ThreatHuntingAppProps) => ( + <>{THREAT_HUNTING_TITLE} App +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/application.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/application.tsx new file mode 100644 index 0000000000..3a09fc3124 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/application.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import ReactDOM from 'react-dom'; +import { AppProps } from '../../../../../../wazuh-core/public/services/application/types'; +import { VulnerabilityDetectionApp } from './vulnerability-detection-app'; + +export const renderApp = async ( + params: AppMountParameters, + { Layout }: AppProps, +) => { + ReactDOM.render( + + + , + params.element, + ); + + return () => ReactDOM.unmountComponentAtNode(params.element); +}; diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/constants.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/constants.ts new file mode 100644 index 0000000000..bbd675d3a9 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/constants.ts @@ -0,0 +1,15 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../../../common/constants'; +import { buildSubAppId } from '../../../../utils'; +import { THREAT_INTELLIGENCE_ID } from '../../constants'; + +export const VULNERABILITY_DETECTION_ID = buildSubAppId( + THREAT_INTELLIGENCE_ID, + 'vulnerability_detection', +); +export const VULNERABILITY_DETECTION_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${VULNERABILITY_DETECTION_ID}`, + { + defaultMessage: 'Vulnerability Detection', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/vulnerability-detection-app.tsx b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/vulnerability-detection-app.tsx new file mode 100644 index 0000000000..7a439f870b --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/apps/vulnerability-detection/vulnerability-detection-app.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { AppMountParameters } from 'opensearch-dashboards/public'; +import { VULNERABILITY_DETECTION_TITLE } from './constants'; + +interface VulnerabilityDetectionAppProps { + params: AppMountParameters; +} + +export const VulnerabilityDetectionApp = ( + _props: VulnerabilityDetectionAppProps, +) => <>{VULNERABILITY_DETECTION_TITLE} App; diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/constants.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/constants.ts new file mode 100644 index 0000000000..43ed7af00c --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/constants.ts @@ -0,0 +1,17 @@ +import { i18n } from '@osd/i18n'; +import { PLUGIN_ID } from '../../../common/constants'; + +export const THREAT_INTELLIGENCE_ID = 'threat_intelligence'; +export const THREAT_INTELLIGENCE_TITLE = i18n.translate( + `${PLUGIN_ID}.category.${THREAT_INTELLIGENCE_ID}.`, + { + defaultMessage: 'Threat Intelligence', + }, +); +export const THREAT_INTELLIGENCE_DESCRIPTION = i18n.translate( + `${PLUGIN_ID}.category.${THREAT_INTELLIGENCE_ID}.description`, + { + defaultMessage: + 'Collect and analyze information about potential threats to inform security decisions.', + }, +); diff --git a/plugins/wazuh-analysis/public/groups/threat-intelligence/index.ts b/plugins/wazuh-analysis/public/groups/threat-intelligence/index.ts new file mode 100644 index 0000000000..2517b2fb52 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/threat-intelligence/index.ts @@ -0,0 +1,62 @@ +import { Subject } from 'rxjs'; +import { + App, + AppMountParameters, + AppUpdater, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { CATEGORY } from '../category'; +import { Group } from '../../../../wazuh-core/public/services/application/types'; +import { ApplicationService } from '../../../../wazuh-core/public/services/application/application'; +import { getThreatIntelligenceApps } from './applications'; +import { + THREAT_INTELLIGENCE_DESCRIPTION, + THREAT_INTELLIGENCE_ID, + THREAT_INTELLIGENCE_TITLE, +} from './constants'; + +export const ThreatIntelligenceNavGroup: Group = + { + getId: () => THREAT_INTELLIGENCE_ID, + getTitle: () => THREAT_INTELLIGENCE_TITLE, + getDescription: () => THREAT_INTELLIGENCE_DESCRIPTION, + + getNavGroup() { + return { + id: THREAT_INTELLIGENCE_ID, + title: THREAT_INTELLIGENCE_TITLE, + description: THREAT_INTELLIGENCE_DESCRIPTION, + }; + }, + + getAppGroup() { + return { + id: THREAT_INTELLIGENCE_ID, + title: THREAT_INTELLIGENCE_TITLE, + category: CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: THREAT_INTELLIGENCE_ID, + title: THREAT_INTELLIGENCE_TITLE, + category: CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getThreatIntelligenceApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps( + applicationService: ApplicationService, + updater$: Subject, + ): App[] { + return getThreatIntelligenceApps(applicationService, updater$); + }, + }; diff --git a/plugins/wazuh-analysis/public/groups/types.ts b/plugins/wazuh-analysis/public/groups/types.ts new file mode 100644 index 0000000000..e8448b2686 --- /dev/null +++ b/plugins/wazuh-analysis/public/groups/types.ts @@ -0,0 +1,10 @@ +import { CLOUD_SECURITY_ID } from './cloud-security/constants'; +import { ENDPOINT_SECURITY_ID } from './endpoint-security/constants'; +import { SECURITY_OPERATIONS_ID } from './security-operations/constants'; +import { THREAT_INTELLIGENCE_ID } from './threat-intelligence/constants'; + +export type GroupsId = + | typeof ENDPOINT_SECURITY_ID + | typeof THREAT_INTELLIGENCE_ID + | typeof SECURITY_OPERATIONS_ID + | typeof CLOUD_SECURITY_ID; diff --git a/plugins/wazuh-analysis/public/index.ts b/plugins/wazuh-analysis/public/index.ts new file mode 100644 index 0000000000..312e392c29 --- /dev/null +++ b/plugins/wazuh-analysis/public/index.ts @@ -0,0 +1,9 @@ +import { AnalysisPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as the OpenSearch Dashboards Platform `plugin()` initializer. +export function plugin() { + return new AnalysisPlugin(); +} + +export type { AnalysisSetup, AnalysisStart } from './types'; diff --git a/plugins/wazuh-analysis/public/plugin-services.ts b/plugins/wazuh-analysis/public/plugin-services.ts new file mode 100644 index 0000000000..74c69915da --- /dev/null +++ b/plugins/wazuh-analysis/public/plugin-services.ts @@ -0,0 +1,4 @@ +import { CoreStart } from 'opensearch-dashboards/public'; +import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/common'; + +export const [getCore, setCore] = createGetterSetter('Core'); diff --git a/plugins/wazuh-analysis/public/plugin.ts b/plugins/wazuh-analysis/public/plugin.ts new file mode 100644 index 0000000000..31cd28670d --- /dev/null +++ b/plugins/wazuh-analysis/public/plugin.ts @@ -0,0 +1,53 @@ +import { CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; +import { Group } from '../../wazuh-core/public/services/application/types'; +import { CloudSecurityNavGroup } from './groups/cloud-security'; +import { EndpointSecurityNavGroup } from './groups/endpoint-security'; +import { SecurityOperationsNavGroup } from './groups/security-operations'; +import { ThreatIntelligenceNavGroup } from './groups/threat-intelligence'; +import { GroupsId } from './groups/types'; +import { setCore } from './plugin-services'; +import { + AnalysisSetup, + AnalysisSetupDependencies, + AnalysisStart, + AnalysisStartDependencies, +} from './types'; + +export class AnalysisPlugin + implements + Plugin +{ + private readonly navGroups: Group[] = [ + EndpointSecurityNavGroup, + ThreatIntelligenceNavGroup, + SecurityOperationsNavGroup, + CloudSecurityNavGroup, + ]; + + public setup( + core: CoreSetup, + plugins: AnalysisSetupDependencies, + ): AnalysisSetup | Promise { + console.debug(`${AnalysisPlugin.name} setup`); + plugins.wazuhCore.applicationService.register({ + id: 'wz-analysis', + navGroups: this.navGroups, + }); + + return {}; + } + + start( + core: CoreStart, + _plugins: AnalysisStartDependencies, + ): AnalysisStart | Promise { + console.debug(`${AnalysisPlugin.name} start`); + setCore(core); + + return {}; + } + + stop?(): void { + console.debug(`${AnalysisPlugin.name} stop`); + } +} diff --git a/plugins/wazuh-analysis/public/types.ts b/plugins/wazuh-analysis/public/types.ts new file mode 100644 index 0000000000..4fe145cda9 --- /dev/null +++ b/plugins/wazuh-analysis/public/types.ts @@ -0,0 +1,15 @@ +import { + WazuhCorePluginSetup, + WazuhCorePluginStart, +} from '../../wazuh-core/public'; + +export interface AnalysisSetup {} + +export interface AnalysisStart {} +export interface AnalysisSetupDependencies { + wazuhCore: WazuhCorePluginSetup; +} + +export interface AnalysisStartDependencies { + wazuhCore: WazuhCorePluginStart; +} diff --git a/plugins/wazuh-analysis/public/utils/index.ts b/plugins/wazuh-analysis/public/utils/index.ts new file mode 100644 index 0000000000..d393c82b65 --- /dev/null +++ b/plugins/wazuh-analysis/public/utils/index.ts @@ -0,0 +1,11 @@ +/** + * The function `buildSubAppId` takes a parent app ID and a sub app ID, and + * returns a combined ID with the sub app ID URL-encoded. + * @param {string} parentAppId - The `parentAppId` parameter is a string + * representing the ID of the parent application. + * @param {string} subAppId - The `subAppId` parameter is a string representing the + * ID of a sub-application within a parent application. + */ +export function buildSubAppId(parentAppId: string, subAppId: string) { + return `${parentAppId}_${encodeURIComponent(`/${subAppId}`)}`; +} diff --git a/plugins/wazuh-analysis/tsconfig.json b/plugins/wazuh-analysis/tsconfig.json new file mode 100644 index 0000000000..cc7e3e157f --- /dev/null +++ b/plugins/wazuh-analysis/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*", + "public/hooks" + ], + "exclude": [] +} diff --git a/plugins/wazuh-analysis/yarn.lock b/plugins/wazuh-analysis/yarn.lock new file mode 100644 index 0000000000..bda17e6374 --- /dev/null +++ b/plugins/wazuh-analysis/yarn.lock @@ -0,0 +1,12 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@testing-library/user-event@^14.5.2": + version "14.6.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.0.tgz#6748ec1ac6df9291e92b6abc0f3530b3842bf34d" + integrity sha512-+jsfK7kVJbqnCYtLTln8Ja/NmVrZRwBJHmHR9IxIVccMWSOZ6Oy0FkDJNeyVu4QSpMNmRfy10Xb76ObRDlWWBQ== + +"@types/@testing-library/user-event": + version "0.0.0-semantically-released" + resolved "https://codeload.github.com/testing-library/user-event/tar.gz/a7f8c09f4063423241472c2af3514e198a255dc8" diff --git a/plugins/wazuh-core/common/logger/noop-logger.ts b/plugins/wazuh-core/common/logger/noop-logger.ts new file mode 100644 index 0000000000..3165be918a --- /dev/null +++ b/plugins/wazuh-core/common/logger/noop-logger.ts @@ -0,0 +1,37 @@ +import { Logger, LogMeta, LogRecord } from '@osd/logging'; + +const noop = () => {}; + +export class NoopLogger implements Logger { + info(_message: string): void { + return noop(); + } + + error(_message: string): void { + return noop(); + } + + debug(_message: string): void { + return noop(); + } + + warn(_message: string): void { + return noop(); + } + + trace(_message: string, _meta?: LogMeta): void { + return noop(); + } + + fatal(_errorOrMessage: string | Error, _meta?: LogMeta): void { + return noop(); + } + + log(_record: LogRecord): void { + return noop(); + } + + get(..._childContextPaths: string[]): Logger { + return this; + } +} diff --git a/plugins/wazuh-core/public/components/global_search/global-search-page-item.tsx b/plugins/wazuh-core/public/components/global_search/global-search-page-item.tsx new file mode 100644 index 0000000000..43052f3fea --- /dev/null +++ b/plugins/wazuh-core/public/components/global_search/global-search-page-item.tsx @@ -0,0 +1,80 @@ +import { + EuiBreadcrumb, + EuiFlexGroup, + EuiFlexItem, + EuiHighlight, + EuiIcon, + EuiSimplifiedBreadcrumbs, +} from '@elastic/eui'; +import { + ChromeNavLink, + ChromeRegistrationNavLink, + CoreStart, + NavGroupItemInMap, +} from 'opensearch-dashboards/public'; +import React from 'react'; + +interface Props { + key: React.Key; + link: ChromeRegistrationNavLink & + ChromeNavLink & { navGroup: NavGroupItemInMap }; + coreStart: CoreStart; + search: string; + callback?: () => void; +} + +export const GlobalSearchPageItem = ({ + key, + link, + coreStart, + search, + callback, +}: Props) => { + const breadcrumbs: EuiBreadcrumb[] = []; + const navGroupElement = (navGroup: NavGroupItemInMap) => ( + + {navGroup.icon && ( + + + + )} + + + {navGroup.title} + + + + ); + + breadcrumbs.push({ text: navGroupElement(link.navGroup) }); + + const onNavItemClick = () => { + callback?.(); + coreStart.chrome.navGroup.setCurrentNavGroup(link.navGroup.id); + coreStart.application.navigateToApp(link.id); + }; + + breadcrumbs.push({ + text: ( + + {link.title} + + ), + onClick: () => {}, + }); + + return ( + + ); +}; diff --git a/plugins/wazuh-core/public/components/global_search/search-pages-command.tsx b/plugins/wazuh-core/public/components/global_search/search-pages-command.tsx new file mode 100644 index 0000000000..53e50623c6 --- /dev/null +++ b/plugins/wazuh-core/public/components/global_search/search-pages-command.tsx @@ -0,0 +1,83 @@ +import { + ChromeNavLink, + ChromeRegistrationNavLink, + CoreStart, +} from 'opensearch-dashboards/public'; +import { first } from 'rxjs/operators'; +import React, { ReactNode } from 'react'; +import { GlobalSearchPageItem } from './global-search-page-item'; + +function hasMatch(title: string | undefined, query: string) { + return title && title.toLowerCase().includes(query.toLowerCase()); +} + +export const searchPages = async ( + query: string, + applicationIds: string[], + coreStart?: CoreStart, + callback?: () => void, +): Promise => { + if (!coreStart) { + return []; + } + + const navGroupMap = await coreStart.chrome.navGroup + .getNavGroupsMap$() + .pipe(first()) + .toPromise(); + const searchResult = applicationIds.flatMap(useCaseId => { + const navGroup = navGroupMap[useCaseId]; + + if (!navGroup) { + return []; + } + + const links = navGroup.navLinks as (ChromeRegistrationNavLink & + ChromeNavLink)[]; + // parent nav links are not clickable + const parentNavLinkIds = new Set( + links.map(link => link.parentNavLinkId).filter(link => !!link), + ); + + return links + .filter(link => { + const title = link.title; + let parentNavLinkTitle = ''; + + // parent title also taken into consideration for search its sub items + if (link.parentNavLinkId) { + parentNavLinkTitle = + navGroup.navLinks.find( + navLink => navLink.id === link.parentNavLinkId, + )?.title ?? ''; + } + + const navGroupTitleMatch = hasMatch(navGroup.title, query); + const titleMatch = hasMatch(title, query); + const parentTitleMatch = hasMatch(parentNavLinkTitle, query); + + return ( + !link.disabled && + (navGroupTitleMatch || titleMatch || parentTitleMatch) && + !parentNavLinkIds.has(link.id) + ); + }) + .map(link => ({ + ...link, + navGroup, + })); + }); + const pages = searchResult + .slice(0, 10) + .map(link => ( + + )); + + return pages; +}; diff --git a/plugins/wazuh-core/public/plugin.ts b/plugins/wazuh-core/public/plugin.ts index cd50d1a18c..a5f3a6a2e4 100644 --- a/plugins/wazuh-core/public/plugin.ts +++ b/plugins/wazuh-core/public/plugin.ts @@ -5,42 +5,64 @@ import { PluginInitializerContext, } from 'opensearch-dashboards/public'; import { Cookies } from 'react-cookie'; +import { Logger } from '@osd/logging'; import { ConfigurationStore } from '../common/services/configuration/configuration-store'; import { EConfigurationProviders } from '../common/constants'; import { API_USER_STATUS_RUN_AS } from '../common/api-user-status-run-as'; import { Configuration } from '../common/services/configuration'; +import { NoopLogger } from '../common/logger/noop-logger'; import { WazuhCorePluginSetup, WazuhCorePluginStart } from './types'; import { setChrome, setCore, setUiSettings } from './plugin-services'; import { UISettingsConfigProvider } from './services/configuration/ui-settings-provider'; import { InitializerConfigProvider } from './services/configuration/initializer-context-provider'; import * as utils from './utils'; import * as uiComponents from './components'; -import { DashboardSecurity } from './services/dashboard-security'; +import { + DashboardSecurity, + DashboardSecurityServiceSetupReturn, +} from './services/dashboard-security'; import * as hooks from './hooks'; -import { CoreState, State } from './services/state'; +import { CoreState, State, StateSetupReturn } from './services/state'; import { ServerHostClusterInfoStateContainer } from './services/state/containers/server-host-cluster-info'; import { ServerHostStateContainer } from './services/state/containers/server-host'; import { DataSourceAlertsStateContainer } from './services/state/containers/data-source-alerts'; -import { CoreServerSecurity } from './services'; +import { CoreServerSecurity, ServerSecurity } from './services'; import { CoreHTTPClient } from './services/http/http-client'; +import { ApplicationService } from './services/application/application'; -const noop = () => {}; +interface RuntimeSetup { + dashboardSecurity: DashboardSecurityServiceSetupReturn; + http: Awaited>; + serverSecurity: Awaited>; + state: StateSetupReturn; +} + +interface Runtime { + setup: RuntimeSetup; + start: Record; +} export class WazuhCorePlugin implements Plugin { - runtime: Record = { - setup: {}, - start: {}, - }; - internal: Record = {}; + runtime: Runtime; + internal: Record; services: { - [key: string]: any; - dashboardSecurity?: DashboardSecurity; - state?: State; - } = {}; + applicationService: ApplicationService; + configuration: Configuration; + dashboardSecurity: DashboardSecurity; + http: CoreHTTPClient; + serverSecurity: ServerSecurity; + state: State; + }; constructor(private readonly initializerContext: PluginInitializerContext) { + this.runtime = { + // @ts-expect-error Type '{}' is missing some properties + setup: {}, + start: {}, + }; + // @ts-expect-error Type '{}' is missing some properties this.services = {}; this.internal = {}; } @@ -48,16 +70,7 @@ export class WazuhCorePlugin public async setup(core: CoreSetup): Promise { // No operation logger - const logger = { - info: noop, - error: noop, - debug: noop, - warn: noop, - trace: noop, - fatal: noop, - log: noop, - get: () => logger, - }; + const logger: Logger = new NoopLogger(); this.internal.configurationStore = new ConfigurationStore(logger); @@ -72,6 +85,8 @@ export class WazuhCorePlugin new UISettingsConfigProvider(core.uiSettings), ); + this.services.applicationService = new ApplicationService(logger, core); + this.services.configuration = new Configuration( logger, this.internal.configurationStore, @@ -81,7 +96,7 @@ export class WazuhCorePlugin this.services.dashboardSecurity = new DashboardSecurity(logger, core.http); // Create state - this.services.state = new CoreState(logger); + this.services.state = new CoreState(logger) as unknown as State; const cookiesStore = new Cookies(); @@ -159,6 +174,7 @@ export class WazuhCorePlugin this.services.state.start(); await this.services.dashboardSecurity.start(); await this.services.http.start(); + this.services.applicationService.start(core); this.runtime.start.serverSecurityDeps = { chrome: core.chrome, diff --git a/plugins/wazuh-core/public/services/application/README.md b/plugins/wazuh-core/public/services/application/README.md new file mode 100644 index 0000000000..5699c145a5 --- /dev/null +++ b/plugins/wazuh-core/public/services/application/README.md @@ -0,0 +1,140 @@ +# ApplicationService + +Below is a simple explanation of each relevant method in the `ApplicationService` class, and how they’re used in the sample plugin. + +--- + +## Methods + +### `modifyAppGroupMount(app: App, subApps: ChromeRegistrationNavLink[])` + +It prepares and registers the mounting process for an application group, ensuring that navigation links become visible and a startup event is triggered when the group mounts. + +--- + +### `modifySubAppMount(app: App)` + +It adjusts the lifecycle of sub-applications so that their navigation links are properly shown when the app is mounted and hidden when unmounted. + +--- + +### `start(core: CoreStart)` + +This method is typically called during the plugin’s `start` phase to ensure that, when an application or group starts, the UI reflects the correct navigation state and the user is redirected accordingly. + +--- + +### `registerSearchCommand({ id, navGroups }: RegisterParams)` + +This command integrates global search functionality so that users can search within the pages provided by the registered app groups. + +--- + +### `register({ id, navGroups }: RegisterParams)` + +This method is invoked by the plugin’s setup routine (as seen in the sample `AnalysisPlugin`), initializing the navigation groups, their associated applications, and the search command. + +--- + +## How It’s Used in a Plugin + +The sample `MyCustomPlugin` demonstrates how `ApplicationService` integrates into the plugin lifecycle: + +- **In the `setup` method:** + + - The plugin defines an array of navigation groups (e.g., `CustomGroupNavGroup`, etc.). + - Each navigation group must be implemented as an object that implements the `Group` interface. [See the `Group` interface for more details.](types.ts) + - It calls `plugins.wazuhCore.applicationService.register({ ... })` to register all navigation groups and their sub-applications with the core system. + +- **In the `start` method:** + - The wazuh-core plugin calls `this.services.applicationService.start(core)` to subscribe to startup events. This ensures that when an app group is started, the UI will update the active navigation group and automatically navigate to the first available app in that group. + +### Defining Navigation Groups and Submenus + +To define a navigation group, each group should implement the `Group` interface and define its submenus: + +#### Example of a Navigation Group Definition + +```ts +export const CustomGroupNavGroup: Group = { + getId: (): string => CUSTOM_GROUP_ID, + getTitle: (): string => CUSTOM_GROUP_TITLE, + getDescription: (): string => CUSTOM_GROUP_DESCRIPTION, + + getNavGroup(): ChromeNavGroup { + return { + id: CUSTOM_GROUP_ID, + title: CUSTOM_GROUP_TITLE, + description: CUSTOM_GROUP_DESCRIPTION, + }; + }, + + getAppGroup(): App { + return { + id: CUSTOM_GROUP_ID, + title: CUSTOM_GROUP_TITLE, + category: CUSTOM_CATEGORY, + mount: async (_params: AppMountParameters) => () => {}, + }; + }, + + getGroupNavLink(): ChromeRegistrationNavLink { + return { + id: CUSTOM_GROUP_ID, + title: CUSTOM_GROUP_TITLE, + category: CUSTOM_CATEGORY, + }; + }, + + getAppsNavLinks(): ChromeRegistrationNavLink[] { + return getCustomGroupApps().map(app => ({ + id: app.id, + title: app.title, + })); + }, + + getApps(updater$?: Subject): App[] { + return getCustomGroupApps(updater$); + }, +}; +``` + +#### Registering Navigation Groups in the Plugin + +```diff +export class MyCustomPlugin + implements Plugin +{ ++ private readonly navGroups: Group[] = [ ++ UserManagementNavGroup, ++ ]; + + public setup( + core: CoreSetup, + plugins: CustomSetupDependencies, + ): CustomSetup | Promise { ++ plugins.wazuhCore.applicationService.register({ ++ id: 'custom-app', ++ navGroups: this.navGroups, ++ }); + + return {}; + } +} +``` + +--- + +The `ApplicationService` class centralizes the registration and lifecycle management of navigation groups and their associated applications. It provides mechanisms to: + +- **Modify Mount/Unmount Behavior:** + By injecting `beforeMount` and `cleanup` operations into the app’s lifecycle, it ensures the UI is updated correctly when apps are mounted or unmounted. + +- **Register Navigation Groups and Apps:** + It organizes both app groups and sub-applications, ensuring they are properly integrated into the Chrome navigation and application registry. + +- **Handle Navigation Startup:** + It listens for startup events and automatically navigates the user to the first available application in the active navigation group. + +- **Integrate Global Search:** + It registers a search command that allows users to search within the pages of the registered app groups. diff --git a/plugins/wazuh-core/public/services/application/application.ts b/plugins/wazuh-core/public/services/application/application.ts new file mode 100644 index 0000000000..525c50d435 --- /dev/null +++ b/plugins/wazuh-core/public/services/application/application.ts @@ -0,0 +1,373 @@ +import { Logger } from '@osd/logging'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { EuiSideNavItemType, htmlIdGenerator } from '@elastic/eui'; +import { + App, + AppMount, + AppMountParameters, + AppNavLinkStatus, + AppUpdater, + CoreSetup, + CoreStart, + DEFAULT_NAV_GROUPS, + NavGroupItemInMap, + NavGroupType, + ChromeRegistrationNavLink, +} from '../../../../../src/core/public'; +import { searchPages } from '../../components/global_search/search-pages-command'; +import { getCore } from '../../plugin-services'; +import { AppUpdaterNotFoundError } from './errors/app-updater-not-found-error'; +import { AppOperations, Group } from './types'; +import { createLayout } from './create-layout'; + +interface RegisterParams { + id: string; + navGroups: Group[]; +} + +export class ApplicationService { + /** + * Stores app updaters for different applications, used to emit updates (e.g., navigation link visibility changes). + */ + private readonly appUpdater$: Partial>> = + {}; + /** + * Emits startup events for applications, allowing navigation updates (e.g., setting the current group and navigating). + */ + private readonly appStartup$ = new Subject(); + + constructor( + private readonly logger: Logger, + private readonly coreSetup: CoreSetup, + ) { + this.logger = logger.get('ApplicationService'); + } + + /** + * Retrieves the current navigation group asynchronously. + */ + private async getCurrentNavGroup(core: CoreStart) { + return core.chrome.navGroup.getCurrentNavGroup$().pipe(first()).toPromise(); + } + + /** + * Registers an app updater for a given application ID, typically used for navigation group updates. + * @param appId - Unique identifier of the application to register an updater for. + */ + private registerAppUpdater(appId: string) { + this.logger?.debug(`${this.registerAppUpdater.name} [AppId: ${appId}]`); + this.appUpdater$[appId] = new Subject(); + } + + /** + * Retrieves the app updater for a specific app ID. Throws an error if not found. + * @param appId - Unique identifier of the application. + * @returns The app updater associated with the given `appId`. + * @throws {AppUpdaterNotFoundError} If no updater exists for the provided `appId`. + */ + private getAppUpdater(appId: string) { + this.logger?.debug(`${this.getAppUpdater.name} [AppId: ${appId}]`); + + if (!this.appUpdater$[appId]) { + this.logger?.error(`getAppUpdater ${appId}`); + throw new AppUpdaterNotFoundError(appId); + } + + return this.appUpdater$[appId]; + } + + /** + * Returns an object setting `navLinkStatus` to visible. + */ + private setNavLinkVisible(): Partial { + return { navLinkStatus: AppNavLinkStatus.visible }; + } + + /** + * Returns an object setting `navLinkStatus` to hidden. + */ + private setNavLinkHidden(): Partial { + return { navLinkStatus: AppNavLinkStatus.hidden }; + } + + /** + * Extracts the navigation group ID from an application ID. + */ + private getNavGroupId(appId: string): string { + return appId.split('_%2F')[0]; + } + + /** + * Checks if navigation groups are enabled. + */ + private isNavGroupEnabled() { + return this.coreSetup.chrome.navGroup.getNavGroupEnabled(); + } + + /** + * Modifies an application's mount behavior to handle lifecycle operations. + * @param app - The application to modify. + * @param appOperations - Optional lifecycle operations. + */ + private modifyMount(app: App, appOperations?: AppOperations) { + const mount = app.mount.bind(app) as AppMount; + + app.mount = async (params: AppMountParameters) => { + appOperations?.beforeMount?.(); + + const unmount = await mount(params); + + return () => { + this.logger?.debug(`Unmount [AppId: ${app.id}]`); + + appOperations?.cleanup?.(); + + return unmount(); + }; + }; + } + + /** + * The method initializes and registers the mounting of a set of + * applications that belong to navigation groups. + * @param {App[]} apps - This parameter is an array of objects representing + * different applications. Each object contains information about a specific + * app, such as its ID, name, and mount function. + * @param {CoreSetup} core - This parameter is used to access core + * functionalities and services provided by the application framework. This + * parameter is typically used to register applications, access the Chrome + * service for navigation group settings, and perform other core setup + * @param {AppOperations} [appOperations] - This parameter is an optional + * object that contains two properties: `beforeMount` and `cleanup`. These + * properties are functions that are executed before and after mounting each + * application, respectively. The `beforeMount` function is used to prepare + * the application for mounting, while the `cleanup` function is used to clean + * up the application after it has been unmounted. + */ + private modifyAppGroupMount(app: App, subApps: ChromeRegistrationNavLink[]) { + this.logger?.debug(`${this.modifyAppGroupMount.name} [AppId: ${app.id}]`); + + const navGroupId = this.getNavGroupId(app.id); + + const beforeMount = () => { + if (getCore().chrome.navGroup.getNavGroupEnabled()) { + this.getAppUpdater(navGroupId).next(this.setNavLinkVisible); + this.appStartup$.next(navGroupId); + } + + if (!getCore().chrome.navGroup.getNavGroupEnabled()) { + getCore().application.navigateToApp(subApps[0].id); + } + }; + + this.modifyMount(app, { beforeMount }); + } + + /** + * The method initializes and registers the mounting of sub-applications, + * adding logic for both mounting and cleanup (unmounting). + * @param {App[]} apps - This parameter is an array of objects representing + * different applications. Each object contains information about a specific + * app, such as its ID, name, and mount function. + * @param {CoreSetup} core - This parameter in the is used to access core + * services and functionalities provided by the application framework. This + * parameter is typically used to register applications, access the Chrome + * service for UI components, and perform other core setup tasks + * @param {AppOperations} [appOperations] - This parameter is an optional + * object that contains two properties: `beforeMount` and `cleanup`. These + * properties are functions that are executed before and after mounting each + * application, respectively. The `beforeMount` function is used to prepare + * the application for mounting, while the `cleanup` function is used to clean + * up the application after it has been unmounted. + */ + private modifySubAppMount(app: App) { + this.logger?.debug(`${this.modifySubAppMount.name} [AppId: ${app.id}]`); + + const navGroupId = this.getNavGroupId(app.id); + + const beforeMount = () => { + if (getCore().chrome.navGroup.getNavGroupEnabled()) { + this.getAppUpdater(navGroupId).next(this.setNavLinkVisible); + } + }; + + const cleanup = () => { + if (getCore().chrome.navGroup.getNavGroupEnabled()) { + this.getAppUpdater(navGroupId).next(this.setNavLinkHidden); + } + }; + + this.modifyMount(app, { beforeMount, cleanup }); + } + + /** + * The method ensures that, after an application starts, the interface updates + * to reflect the active group and automatically redirects the user to the + * first available application in that group. + * @param {CoreStart} core - An object that provides access to various + * services and functionalities within the application. It allows interaction + * with core services such as navigation, HTTP requests, and more. + */ + start(core: CoreStart) { + this.appStartup$.subscribe({ + next: async (navGroupId: string) => { + if (core.chrome.navGroup.getNavGroupEnabled()) { + core.chrome.navGroup.setCurrentNavGroup(navGroupId); + + const currentNavGroup = await this.getCurrentNavGroup(core); + + this.navigateToFirstAppInNavGroup(core, currentNavGroup); + } + }, + }); + } + + /** + * Navigates to the first available application in in the specified navigation + * group + * @param {CoreStart} core - An object that provides access to various + * services and functionalities within the application. It allows interaction + * with core services such as navigation, HTTP requests, and more. + * @param navGroup - Navigation group containing app links. + */ + private async navigateToFirstAppInNavGroup( + core: CoreStart, + navGroup: NavGroupItemInMap | undefined, + ) { + this.logger?.debug( + `${this.navigateToFirstAppInNavGroup.name} [NavGroupId: ${navGroup?.id}]`, + ); + + // Get the first nav item, if it exists navigate to the app + const firstNavItem = navGroup?.navLinks[0]; + + if (firstNavItem?.id) { + core.application.navigateToApp(firstNavItem.id); + } + } + + private registerAppGroup( + appGroup: App, + subApps: ChromeRegistrationNavLink[], + ) { + this.logger?.debug(`${this.registerAppGroup.name} [AppId: ${appGroup.id}]`); + this.registerAppUpdater(appGroup.id); + this.modifyAppGroupMount(appGroup, subApps); + this.coreSetup.application.register(appGroup); + } + + private assignNavLinksToChromeGroups(navGroup: Group) { + this.logger?.debug( + `${this.assignNavLinksToChromeGroups.name} [NavGroupId: ${navGroup.getId()}]`, + ); + this.coreSetup.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.all, [ + navGroup.getGroupNavLink(), + ]); + + const navGroupChromeGroup = navGroup.getNavGroup(); + + navGroupChromeGroup.type = NavGroupType.SYSTEM; + this.coreSetup.chrome.navGroup.addNavLinksToGroup( + navGroupChromeGroup, + navGroup.getAppsNavLinks(), + ); + } + + /** + * Registers a navigation group and its associated applications. + */ + private registerNavGroup(navGroup: Group) { + this.logger?.debug( + `${this.registerNavGroup.name} [NavGroupId: ${navGroup.getId()}]`, + ); + this.assignNavLinksToChromeGroups(navGroup); + this.registerAppGroup(navGroup.getAppGroup(), navGroup.getAppsNavLinks()); + } + + /** + * Registers sub-applications within a navigation group. + */ + private registerSubAppsGroups(navGroup: Group) { + this.logger?.debug( + `${this.registerSubAppsGroups.name} [NavGroupId: ${navGroup.getId()}]`, + ); + + const subApps: App[] = navGroup.getApps( + this, + this.getAppUpdater(navGroup.getId()), + ); + + for (const app of subApps) { + this.modifySubAppMount(app); + this.coreSetup.application.register(app); + } + } + + /** + * Registers a global search command for searching pages within app groups. + */ + private registerSearchCommand({ id, navGroups }: RegisterParams) { + this.logger?.debug(`${this.registerSearchCommand.name} [Id: ${id}]`); + + const applications: App[] = navGroups.map(navGroup => + navGroup.getAppGroup(), + ); + const applicationIds = applications.map(app => app.id); + + if (this.coreSetup.chrome.navGroup.getNavGroupEnabled()) { + this.coreSetup.chrome.globalSearch.registerSearchCommand({ + id, + type: 'PAGES', + run: async (query: string, done?: () => void) => + searchPages(query, applicationIds, getCore(), done), + }); + } + } + + /** + * Initializes the service by registering navigation groups and applications. + */ + register({ id, navGroups }: RegisterParams) { + this.logger?.debug(`${this.register.name} [Id: ${id}]`); + + for (const navGroup of navGroups) { + this.registerNavGroup(navGroup); + this.registerSubAppsGroups(navGroup); + } + + this.registerSearchCommand({ id, navGroups }); + } + + private createSideNavItems(group: Group) { + return (selectedAppId?: App['id']) => { + this.logger.debug(`createSideNavItems [GroupId: ${group.getId()}]`); + + const items: EuiSideNavItemType[] = [ + { + id: htmlIdGenerator(group.getId())(), + name: group.getTitle(), + items: group.getAppsNavLinks().map(app => ({ + id: app.id, + name: app.title, + onClick: () => getCore().application.navigateToApp(app.id), + isSelected: app.id === selectedAppId, + })), + }, + ]; + + return items; + }; + } + + createLayout(group: Group) { + return (selectedAppId: string) => { + this.logger.debug(`createLayout [GroupId: ${group.getId()}]`); + + return createLayout({ + label: group.getTitle(), + items: this.createSideNavItems(group)(selectedAppId), + }); + }; + } +} diff --git a/plugins/wazuh-core/public/services/application/create-layout.tsx b/plugins/wazuh-core/public/services/application/create-layout.tsx new file mode 100644 index 0000000000..56b4acc62b --- /dev/null +++ b/plugins/wazuh-core/public/services/application/create-layout.tsx @@ -0,0 +1,18 @@ +import { EuiSideNavItemType } from '@elastic/eui'; +import React from 'react'; +import { Layout } from './layout'; + +interface LayoutProps { + label: string; + items: EuiSideNavItemType[]; +} + +export const createLayout = (props: LayoutProps) => { + const NewLayout = ({ children }: { children: React.ReactNode[] }) => ( + + {children} + + ); + + return NewLayout; +}; diff --git a/plugins/wazuh-core/public/services/application/errors/app-updater-not-found-error.ts b/plugins/wazuh-core/public/services/application/errors/app-updater-not-found-error.ts new file mode 100644 index 0000000000..63239cae90 --- /dev/null +++ b/plugins/wazuh-core/public/services/application/errors/app-updater-not-found-error.ts @@ -0,0 +1,10 @@ +export class AppUpdaterNotFoundError extends Error { + constructor(appId: string) { + super( + i18n.translate('errors.appUpdater.NotFound', { + defaultMessage: `AppUpdater for ${appId} not found`, + }), + ); + this.name = 'AppUpdaterNotFoundError'; + } +} diff --git a/plugins/wazuh-core/public/services/application/layout.tsx b/plugins/wazuh-core/public/services/application/layout.tsx new file mode 100644 index 0000000000..390732ea78 --- /dev/null +++ b/plugins/wazuh-core/public/services/application/layout.tsx @@ -0,0 +1,29 @@ +import { + EuiPage, + EuiPageBody, + EuiPageContentBody, + EuiPageSideBar, + EuiSideNav, + EuiSideNavItemType, +} from '@elastic/eui'; +import React from 'react'; +import { getCore } from '../../plugin-services'; + +interface LayoutProps { + label: string; + items: EuiSideNavItemType[]; + children: React.ReactNode[]; +} + +export const Layout = (props: LayoutProps) => ( + + {!getCore().chrome.navGroup.getNavGroupEnabled() && ( + + + + )} + + {props.children} + + +); diff --git a/plugins/wazuh-core/public/services/application/types.ts b/plugins/wazuh-core/public/services/application/types.ts new file mode 100644 index 0000000000..b809a2e40e --- /dev/null +++ b/plugins/wazuh-core/public/services/application/types.ts @@ -0,0 +1,76 @@ +import { + App, + AppUpdater, + ChromeNavGroup, + ChromeRegistrationNavLink, +} from 'opensearch-dashboards/public'; +import { Subject } from 'rxjs'; +import { ApplicationService } from './application'; + +export interface AppOperations { + beforeMount?: () => Partial; + cleanup?: () => Partial; +} + +export interface AppProps { + Layout: React.ElementType; +} + +export interface Group { + getId: () => GroupId; + getTitle: () => string; + getDescription: () => string; + + /** + * This method is used to retrieve the navigation group to which the group + * belongs. The `ChromeNavGroup` object represents a group of navigation links + * within the OpenSearch Dashboards application. By calling `getNavGroup`, you + * can get the specific navigation group associated with the group, which can + * be used for organizing and displaying navigation links related to that + * group within the application's user interface. + */ + getNavGroup: () => ChromeNavGroup; + + /** + * This method is used to retrieve the specific OpenSearch Dashboards + * application associated with the group. The `App` object represents an + * application within the OpenSearch Dashboards framework and contains + * information about the application, such as its title, description, and + * configuration. + */ + getAppGroup: () => App; + + /** + * This method is used to retrieve a specific navigation link associated with + * the group. The `ChromeRegistrationNavLink` object represents a single + * navigation link within the OpenSearch Dashboards application. By calling + * this method, you can get the specific navigation link that is related to + * the group, which can be used for navigating to a specific section or + * feature within the application's user interface that is associated with + * that group. + */ + getGroupNavLink: () => ChromeRegistrationNavLink; + + /** + * Returns an array of `ChromeRegistrationNavLink` objects. These objects + * represent navigation links for sub-applications within the OpenSearch + * Dashboards application that are associated with the specific group. + */ + getAppsNavLinks: () => ChromeRegistrationNavLink[]; + + /** + * This method is used to retrieve the list of applications associated with + * the specific group. + * @param {Subject} updater$ This parameter is a subject that can + * be used to update or notify subscribers about changes to the list of + * applications. + * @returns {App[]} By calling this method, you can get the array of `App` + * objects that belong to the group, allowing you to access information about + * each application, such as its title, description, and configuration within + * the OpenSearch Dashboards framework. + */ + getApps: ( + applicationService: ApplicationService, + updater$: Subject, + ) => App[]; +} diff --git a/plugins/wazuh-core/public/services/dashboard-security/dashboard-security.ts b/plugins/wazuh-core/public/services/dashboard-security/dashboard-security.ts index 3ac207a9c4..c0cba3afaf 100644 --- a/plugins/wazuh-core/public/services/dashboard-security/dashboard-security.ts +++ b/plugins/wazuh-core/public/services/dashboard-security/dashboard-security.ts @@ -1,6 +1,7 @@ import { BehaviorSubject } from 'rxjs'; import jwtDecode from 'jwt-decode'; -import { Logger } from '../../../common/services/configuration'; +import { Logger } from '@osd/logging'; +import { i18n } from '@osd/i18n'; import { WAZUH_ROLE_ADMINISTRATOR_ID } from '../../../common/constants'; import { createDashboardSecurityHooks } from './ui/hooks/creator'; import { createDashboardSecurityHOCs } from './ui/hocs/creator'; @@ -12,20 +13,20 @@ import { } from './types'; export class DashboardSecurity implements DashboardSecurityService { - private _securityPlatform = ''; + private _securityPlatform: DashboardSecurityService['securityPlatform'] = ''; public account$: BehaviorSubject; constructor( private readonly logger: Logger, private readonly http: { get: (path: string) => any }, ) { - this.account$ = new BehaviorSubject({ + this.account$ = new BehaviorSubject({ administrator: false, administrator_requirements: null, }); } - get securityPlatform() { + get securityPlatform(): DashboardSecurityService['securityPlatform'] { return this._securityPlatform; } @@ -41,7 +42,7 @@ export class DashboardSecurity implements DashboardSecurityService { this.logger.debug(`Security platform: ${this._securityPlatform}`); return this.securityPlatform; - } catch (error) { + } catch (error: any) { this.logger.error(error.message); throw error; } @@ -71,7 +72,7 @@ export class DashboardSecurity implements DashboardSecurityService { hocs = createDashboardSecurityHOCs(hooks); this.logger.debug('Created HOCs'); this.logger.debug('Created the UI utilities'); - } catch (error) { + } catch (error: any) { this.logger.error(`Error creating the UI utilities: ${error.message}`); throw error; } @@ -79,14 +80,14 @@ export class DashboardSecurity implements DashboardSecurityService { try { this.logger.debug('Getting security platform'); await this.fetchCurrentPlatform(); - } catch (error) { + } catch (error: any) { this.logger.error( `Error fetching the current platform: ${error.message}`, ); } // Update the dashboard security account information based on server API token - updateData$.subscribe(({ token }: { token: string }) => { + updateData$.subscribe(({ token }) => { const jwtPayload: { rbac_roles?: number[]; } | null = token ? jwtDecode(token) : null; @@ -104,18 +105,24 @@ export class DashboardSecurity implements DashboardSecurityService { async stop() {} - private getAccountFromJWTAPIDecodedToken(decodedToken: { - rbac_roles?: number[]; - }) { - const isAdministrator = decodedToken?.rbac_roles?.some?.( - role => role === WAZUH_ROLE_ADMINISTRATOR_ID, - ); + private getAccountFromJWTAPIDecodedToken( + decodedToken: { + rbac_roles?: number[]; + } | null, + ) { + const isAdministrator = + decodedToken?.rbac_roles?.some?.( + role => role === WAZUH_ROLE_ADMINISTRATOR_ID, + ) ?? false; return { administrator: isAdministrator, administrator_requirements: isAdministrator ? null - : 'User has no administrator role in the selected API connection.', + : i18n.translate('wazuh.security.no_admin_role', { + defaultMessage: + 'User has no administrator role in the selected API connection.', + }), }; } } diff --git a/plugins/wazuh-core/public/services/dashboard-security/types.ts b/plugins/wazuh-core/public/services/dashboard-security/types.ts index 0239b78b09..da6e8996e8 100644 --- a/plugins/wazuh-core/public/services/dashboard-security/types.ts +++ b/plugins/wazuh-core/public/services/dashboard-security/types.ts @@ -5,6 +5,7 @@ import { WAZUH_SECURITY_PLUGIN_OPENSEARCH_DASHBOARDS_SECURITY } from '../../../c export interface DashboardSecurityServiceAccount { administrator: boolean; administrator_requirements: string | null; + token?: string; } export interface DashboardSecurityServiceSetupReturn { @@ -16,13 +17,12 @@ export interface DashboardSecurityServiceSetupReturn { useDashboardSecurityIsAdmin: () => boolean; }; hocs: { - // FIXME: enhance typing withDashboardSecurityAccount: ( WrappedComponent: React.ElementType, - ) => (props: any) => React.ElementRef; + ) => (props: any) => React.ElementRef; withDashboardSecurityAccountAdmin: ( WrappedComponent: React.ElementType, - ) => (props: any) => React.ElementRef; + ) => (props: any) => React.ElementRef; }; } diff --git a/plugins/wazuh-core/public/services/types.ts b/plugins/wazuh-core/public/services/types.ts index 75147d110f..1cb50b0598 100644 --- a/plugins/wazuh-core/public/services/types.ts +++ b/plugins/wazuh-core/public/services/types.ts @@ -6,7 +6,7 @@ export interface LifecycleService< StopDeps = any, StopReturn = any, > { - setup: (deps: SetupDeps) => SetupReturn; - start: (deps: StartDeps) => StartReturn; - stop: (deps: StopDeps) => StopReturn; + setup: (deps?: SetupDeps) => SetupReturn; + start: (deps?: StartDeps) => StartReturn; + stop: (deps?: StopDeps) => StopReturn; } diff --git a/plugins/wazuh-core/public/types.ts b/plugins/wazuh-core/public/types.ts index 6339a8cb7f..49cc6ef452 100644 --- a/plugins/wazuh-core/public/types.ts +++ b/plugins/wazuh-core/public/types.ts @@ -12,10 +12,12 @@ import { DashboardSecurityService, DashboardSecurityServiceSetupReturn, } from './services/dashboard-security'; +import { ApplicationService } from './services/application/application'; export interface WazuhCorePluginSetup { _internal: any; utils: { formatUIDate: (date: Date) => string }; + applicationService: ApplicationService; API_USER_STATUS_RUN_AS: typeof API_USER_STATUS_RUN_AS; configuration: Configuration; dashboardSecurity: DashboardSecurityService; @@ -43,6 +45,7 @@ export interface WazuhCorePluginSetup { export interface WazuhCorePluginStart { utils: { formatUIDate: (date: Date) => string }; + applicationService: ApplicationService; API_USER_STATUS_RUN_AS: typeof API_USER_STATUS_RUN_AS; configuration: Configuration; dashboardSecurity: DashboardSecurityService;