diff --git a/plugins/main/public/components/common/hocs/validate-states-index-pattern-and-dashboards.tsx b/plugins/main/public/components/common/hocs/validate-states-index-pattern-and-dashboards.tsx new file mode 100644 index 0000000000..e54786ffa1 --- /dev/null +++ b/plugins/main/public/components/common/hocs/validate-states-index-pattern-and-dashboards.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { compose } from 'redux'; +import { connect } from 'react-redux'; +import { withGuardAsync } from '.'; +import { getSavedObjects } from '../../../kibana-services'; +import { SavedObject } from '../../../react-services'; +import { NOT_TIME_FIELD_NAME_INDEX_PATTERN } from '../../../../common/constants'; +import { EuiButton, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; +import { webDocumentationLink } from '../../../../common/services/web_documentation'; +import { vulnerabilityDetection } from '../../../utils/applications'; +import { LoadingSpinnerDataSource } from '../loading/loading-spinner-data-source'; +import NavigationService from '../../../react-services/navigation-service'; + +const INDEX_PATTERN_CREATION_NO_INDEX = 'INDEX_PATTERN_CREATION_NO_INDEX'; + +async function checkExistenceIndexPattern(indexPatternID) { + return await getSavedObjects().client.get('index-pattern', indexPatternID); +} + +async function checkExistenceIndices(indexPatternId) { + try { + const fields = await SavedObject.getIndicesFields(indexPatternId); + return { exist: true, fields }; + } catch (error) { + return { exist: false }; + } +} + +async function createIndexPattern(indexPattern, fields) { + try { + await SavedObject.createSavedObject( + 'index-pattern', + indexPattern, + { + attributes: { + title: indexPattern, + timeFieldName: NOT_TIME_FIELD_NAME_INDEX_PATTERN, + }, + }, + fields, + ); + await SavedObject.validateIndexPatternSavedObjectCanBeFound([indexPattern]); + } catch (error) { + return { error: error.message }; + } +} + +export async function createDashboard() { + try { + // Create the dashboard + const result = await SavedObject.createSavedObjectDashboard(); + + let targetDashboard = result?.data?.successResults?.find( + dashboard => dashboard.id === '94febc80-55a2-11ef-a580-5b5ba88681be', + ); + + if (result) { + return targetDashboard; + } else { + console.error('Failed to create dashboard.'); + return null; + } + } catch (error) { + console.error('Error creating dashboard:', error); + return null; + } +} + +export async function validateVulnerabilitiesStateDataSources({ + vulnerabilitiesStatesindexPatternID: indexPatternID, +}) { + try { + // Check the existence of related index pattern + const existIndexPattern = await checkExistenceIndexPattern(indexPatternID); + let indexPattern = existIndexPattern; + + // If the index pattern does not exist, then check the existence of index + if (existIndexPattern?.error?.statusCode === 404) { + // Check the existence of indices + const { exist, fields } = await checkExistenceIndices(indexPatternID); + + if (!exist) { + return { + ok: true, + data: { + error: { + title: + 'Vulnerability detection seems to be disabled or has a problem', + type: INDEX_PATTERN_CREATION_NO_INDEX, + }, + }, + }; + } + // If some index matches the index pattern, then create the index pattern + const resultCreateIndexPattern = await createIndexPattern( + indexPatternID, + fields, + ); + if (resultCreateIndexPattern?.error) { + return { + ok: true, + data: { + error: { + title: 'There was a problem creating the index pattern', + message: resultCreateIndexPattern?.error, + }, + }, + }; + } + /* WORKAROUND: Redirect to the root of Vulnerabilities Detection application that should + redirects to the Dashboard tab. We want to redirect to this view, because we need the + component is visible (visualizations) to ensure the process that defines the filters for the + Events tab is run when the Dashboard component is unmounted. This workaround solves a + problem in the Events tabs related there are no implicit filters when accessing if the HOC + that protect the view is passed. + */ + NavigationService.getInstance().navigateToApp(vulnerabilityDetection.id); + } + return { + ok: false, + data: { indexPattern }, + }; + } catch (error) { + return { + ok: true, + data: { + error: { title: 'There was a problem', message: error.message }, + }, + }; + } +} + +const errorPromptBody = { + INDEX_PATTERN_CREATION_NO_INDEX: ( +

+ Please check the cluster status. Also, you can check the{' '} + + vulnerability detection documentation. + +

+ ), +}; + +export const PromptCheckIndex = props => { + const { refresh } = props; + const { title, message } = props?.error; + const body = errorPromptBody?.[props?.error?.type] ||

{message}

; + + return ( + {title}} + body={body} + actions={ + + Refresh + + } + /> + ); +}; + +const mapStateToProps = state => ({ + vulnerabilitiesStatesindexPatternID: + state.appConfig.data['vulnerabilities.pattern'], +}); + +export const withVulnerabilitiesStateDataSource = compose( + connect(mapStateToProps), + withGuardAsync( + validateVulnerabilitiesStateDataSources, + ({ error, check }) => , + () => , + ), +); diff --git a/plugins/main/public/components/common/modules/modules-defaults.tsx b/plugins/main/public/components/common/modules/modules-defaults.tsx index 926216572a..1dcb9f7735 100644 --- a/plugins/main/public/components/common/modules/modules-defaults.tsx +++ b/plugins/main/public/components/common/modules/modules-defaults.tsx @@ -17,6 +17,7 @@ import { ComplianceTable } from '../../overview/compliance-table'; import { ButtonModuleGenerateReport } from '../modules/buttons'; import { OfficePanel } from '../../overview/office/panel'; import { GitHubPanel } from '../../overview/github/panel'; +import { DashboardPOCByReference } from '../../overview/poc/dashboards/overview/dashboard'; import { withModuleNotForAgent } from '../hocs'; import { WazuhDiscover, @@ -89,7 +90,6 @@ const renderDiscoverTab = (props: WazuhDiscoverProps) => { component: () => , }; }; - export const ModulesDefaults = { general: { init: 'events', @@ -248,6 +248,13 @@ export const ModulesDefaults = { vuls: { init: 'dashboard', tabs: [ + { + id: 'dashboardByReference', + name: 'Dashboard by reference', + component: DashboardPOCByReference, + /* For ButtonExploreAgent to insert correctly according to the module's index pattern, the moduleIndexPatternTitle parameter is added. By default it applies the index pattern wazuh-alerts-* */ + buttons: [], + }, { id: 'dashboard', name: 'Dashboard', diff --git a/plugins/main/public/components/overview/poc/dashboards/overview/dashboard.tsx b/plugins/main/public/components/overview/poc/dashboards/overview/dashboard.tsx new file mode 100644 index 0000000000..016eafc76e --- /dev/null +++ b/plugins/main/public/components/overview/poc/dashboards/overview/dashboard.tsx @@ -0,0 +1,161 @@ +import React, { useState, useEffect } from 'react'; +import { getPlugins } from '../../../../../kibana-services'; +import { ViewMode } from '../../../../../../../../src/plugins/embeddable/public'; +import { withErrorBoundary } from '../../../../common/hocs'; +import { compose } from 'redux'; +import { + withVulnerabilitiesStateDataSource, + createDashboard, +} from '../../../../common/hocs/validate-states-index-pattern-and-dashboards'; +import { SavedObject } from '../../../../../react-services'; +import { EuiButton } from '@elastic/eui'; + +const DashboardByRenderer = + getPlugins().dashboard.DashboardContainerByValueRenderer; + +const transformPanelsJSON = ({ panelsJSON, references }) => + Object.fromEntries( + JSON.parse(panelsJSON).map(({ gridData, panelIndex, panelRefName }) => [ + panelIndex, + { + gridData, + type: 'visualization', + explicitInput: { + id: panelIndex, + savedObjectId: references.find(({ name }) => name === panelRefName) + .id, + }, + }, + ]), + ); + +const transform = spec => { + const options = JSON.parse(spec.attributes.optionsJSON); + return { + title: spec.attributes.title, + panels: transformPanelsJSON({ + panelsJSON: spec.attributes.panelsJSON, + references: spec.references, + }), + useMargins: options.useMargins, + hidePanelTitles: options.hidePanelTitles, + description: spec.attributes.description, + id: spec.id, + }; +}; + +export const DashboardSavedObject = ({ savedObjectId }) => { + const [dashboardSpecForComponent, setDashboardSpecForComponent] = + useState(null); + + useEffect(() => { + (async () => { + try { + const { data } = await SavedObject.getDashboardById(savedObjectId); + const dashboardSpecRenderer = transform(data); + setDashboardSpecForComponent(dashboardSpecRenderer); + } catch (error) { + console.error('Error fetching dashboard:', error); + } + })(); + }, [savedObjectId]); + + return dashboardSpecForComponent ? ( + + ) : ( +

Loading dashboard...

+ ); +}; + +const DashboardComponent = () => { + const [idDashboard, setIdDashboard] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [previousDashboardState, setPreviousDashboardState] = useState(null); + + useEffect(() => { + (async () => { + try { + const dashboards = await SavedObject.getAllDashboards(); + let targetDashboard = dashboards.data.saved_objects.find( + dashboard => dashboard.id === '94febc80-55a2-11ef-a580-5b5ba88681be', + ); + + if (!targetDashboard) { + const newDashboardId = await createDashboard(); + if (newDashboardId) { + targetDashboard = { id: newDashboardId.id }; + setIdDashboard(targetDashboard.id); + } + } else { + setIdDashboard(targetDashboard.id); + setPreviousDashboardState(targetDashboard); + } + } catch (error) { + console.error('Error processing dashboards:', error); + } finally { + setIsLoading(false); + } + })(); + }, [idDashboard]); + + const handleRestart = async () => { + try { + const savedObjectId = '94febc80-55a2-11ef-a580-5b5ba88681be'; + const dashboardChanged = await SavedObject.getDashboardById( + savedObjectId, + ); + + const changed = + JSON.stringify(dashboardChanged) !== + JSON.stringify(previousDashboardState); + + if (changed) { + if ( + window.confirm( + 'The dashboard has changed. Do you want to revert to the previous version?', + ) + ) { + await createDashboard(); // Restore the dashboard to its previous state + setIdDashboard(null); // Force re-rendering + setTimeout(() => setIdDashboard(savedObjectId), 0); // Reassign ID to render the restored dashboard + } + } else { + alert('No changes detected in the dashboard.'); + } + } catch (error) { + console.error('Error processing dashboard changes:', error); + } + }; + + return ( + <> + {idDashboard ? ( + <> + Restart + + + ) : isLoading ? ( +

Loading dashboard...

+ ) : ( +

No matching dashboard found.

+ )} + + ); +}; + +export const DashboardPOCByReference = compose( + withErrorBoundary, + withVulnerabilitiesStateDataSource, +)(DashboardComponent); diff --git a/plugins/main/public/components/overview/vulnerabilities/common/hocs/validate-vulnerabilities-states-index-pattern.tsx b/plugins/main/public/components/overview/vulnerabilities/common/hocs/validate-vulnerabilities-states-index-pattern.tsx index c5c819eab2..5e47789b24 100644 --- a/plugins/main/public/components/overview/vulnerabilities/common/hocs/validate-vulnerabilities-states-index-pattern.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/common/hocs/validate-vulnerabilities-states-index-pattern.tsx @@ -28,7 +28,7 @@ async function checkExistenceIndices(indexPatternId: string) { async function createIndexPattern(indexPattern, fields: any) { try { - await SavedObject.createSavedObject( + await SavedObject.createSavedObjectIndexPattern( 'index-pattern', indexPattern, { diff --git a/plugins/main/public/react-services/saved-objects.js b/plugins/main/public/react-services/saved-objects.js index f9818aecdb..809c028204 100644 --- a/plugins/main/public/react-services/saved-objects.js +++ b/plugins/main/public/react-services/saved-objects.js @@ -26,6 +26,7 @@ import { getDataPlugin, getSavedObjects } from '../kibana-services'; import { webDocumentationLink } from '../../common/services/web_documentation'; import { ErrorFactory } from './error-management'; import { WarningError } from './error-management/error-factory/errors/WarningError'; +import { WzRequest } from '../react-services/wz-request'; export class SavedObject { /** @@ -342,4 +343,100 @@ Restart the ${PLUGIN_PLATFORM_NAME} service to initialize the index. More inform )}.`); } } + + /** + * Given a dashboard ID, checks if it exists + */ + static async existsDashboard(dashboardID) { + try { + const dashboardData = await GenericRequest.request( + 'GET', + `/api/saved_objects/dashboard/${dashboardID}`, + null, + true, + ); + + const title = dashboardData?.data?.attributes?.title; + const id = dashboardData?.data?.id; + + if (title) { + return { + data: 'Dashboard found', + status: true, + statusCode: 200, + title, + id, + }; + } + } catch (error) { + if (error && error.response && error.response.status == 404) return false; + return ((error || {}).data || {}).message || false + ? new Error(error.data.message) + : new Error( + error.message || `Error getting the '${dashboardID}' dashboard`, + ); + } + } + + /** + * Create dashboard */ + static async createSavedObjectDashboard() { + try { + const dashboardVuls = await WzRequest.genericReq( + 'GET', + '/api/dashboards/vulnerabilityDashboard.ndjson', + ); + + const ndjsonBlob = new Blob([dashboardVuls.data], { + type: 'application/x-ndjson', + }); + + const formdata = new FormData(); + formdata.append('file', ndjsonBlob, 'ndjsonBlob.ndjson'); + + const postDashboard = await WzRequest.genericReq( + 'POST', + '/api/saved_objects/_import?overwrite=true', + formdata, + { overwriteHeaders: { 'content-type': 'multipart/form-data' } }, + ); + return postDashboard; + } catch (error) { + throw ((error || {}).data || {}).message || false + ? new Error(error.data.message) + : error; + } + } + + /** + * Get all dashboards */ + static async getAllDashboards() { + try { + const allDashboards = await WzRequest.genericReq( + 'GET', + '/api/saved_objects/_find?type=dashboard', + ); + return allDashboards; + } catch (error) { + throw ((error || {}).data || {}).message || false + ? new Error(error.data.message) + : error; + } + } + + /** + * Get dashboard for ID */ + static async getDashboardById(savedObjectId) { + try { + const dashboardForId = await WzRequest.genericReq( + 'GET', + `/api/saved_objects/dashboard/${savedObjectId}`, + ); + return dashboardForId; + } catch (error) { + throw ((error || {}).data || {}).message || false + ? new Error(error.data.message) + : error; + } + } } diff --git a/plugins/main/server/controllers/wazuh-api.ts b/plugins/main/server/controllers/wazuh-api.ts index af9eafc8aa..a5c3772c8f 100644 --- a/plugins/main/server/controllers/wazuh-api.ts +++ b/plugins/main/server/controllers/wazuh-api.ts @@ -29,6 +29,8 @@ import { version as pluginVersion, revision as pluginRevision, } from '../../package.json'; +import fs from 'fs'; +import path from 'path'; export class WazuhApiCtrl { constructor() {} @@ -1116,4 +1118,27 @@ export class WazuhApiCtrl { ); } } + async getAppDashboards( + context: RequestHandlerContext, + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory, + ) { + try { + const dashboardName = request.params.name; + const dashboardFilePath = path.join( + __dirname, + '../integration-files/dashboards', + dashboardName, + ); + const dashboardFile = fs.readFileSync(dashboardFilePath); + + return response.ok({ + headers: { 'Content-Type': 'application/x-ndjson' }, + body: dashboardFile, + }); + } catch (error) { + context.wazuh.logger.error(error.message || error); + return ErrorResponse(error.message || error, 5030, 500, response); + } + } } diff --git a/plugins/main/server/integration-files/dashboards/vulnerabilityDashboard.ndjson b/plugins/main/server/integration-files/dashboards/vulnerabilityDashboard.ndjson new file mode 100644 index 0000000000..761eb0a393 --- /dev/null +++ b/plugins/main/server/integration-files/dashboards/vulnerabilityDashboard.ndjson @@ -0,0 +1,4 @@ +{"attributes":{"fields":"[{\"count\":0,\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"agent.build.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"agent.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"agent.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"ecs.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.full.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.os.full\"}}},{\"count\":0,\"name\":\"host.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.os.name\"}}},{\"count\":0,\"name\":\"host.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"host.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"package.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.build_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.checksum\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.install_scope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.installed\",\"type\":\"date\",\"esTypes\":[\"date\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.license\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"package.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.classification\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.description.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"vulnerability.description\"}}},{\"count\":0,\"name\":\"vulnerability.detected_at\",\"type\":\"date\",\"esTypes\":[\"date\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.enumeration\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.published_at\",\"type\":\"date\",\"esTypes\":[\"date\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.report_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.scanner.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.score.base\",\"type\":\"number\",\"esTypes\":[\"float\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.score.environmental\",\"type\":\"number\",\"esTypes\":[\"float\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.score.temporal\",\"type\":\"number\",\"esTypes\":[\"float\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.score.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"vulnerability.severity\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"wazuh.cluster.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"wazuh.cluster.node\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"wazuh.manager.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"wazuh.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]","title":"wazuh-states-vulnerabilities-*"},"id":"wazuh-states-vulnerabilities-*","migrationVersion":{"index-pattern":"7.6.0"},"references":[],"type":"index-pattern","updated_at":"2024-08-08T16:23:10.532Z","version":"WzI4MCwxXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"vis","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"vis\",\"type\":\"area\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"agent.name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{}}}"},"id":"9caace70-55a1-11ef-a580-5b5ba88681be","migrationVersion":{"visualization":"7.10.0"},"references":[{"id":"wazuh-states-vulnerabilities-*","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2024-08-08T16:23:10.532Z","version":"WzI4MSwxXQ=="} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"hidePanelTitles\":false}","panelsJSON":"[{\"version\":\"2.13.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"dde69adc-98bc-4c6d-bd0b-3756c9d0228e\"},\"panelIndex\":\"dde69adc-98bc-4c6d-bd0b-3756c9d0228e\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"}]","timeRestore":false,"title":"dash","version":1},"id":"94febc80-55a2-11ef-a580-5b5ba88681be","migrationVersion":{"dashboard":"7.9.3"},"references":[{"id":"9caace70-55a1-11ef-a580-5b5ba88681be","name":"panel_0","type":"visualization"}],"type":"dashboard","updated_at":"2024-08-08T16:23:44.456Z","version":"WzI4MiwxXQ=="} +{"exportedCount":3,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/plugins/main/server/routes/wazuh-api.ts b/plugins/main/server/routes/wazuh-api.ts index 9e0e2cc1bd..4130331b01 100644 --- a/plugins/main/server/routes/wazuh-api.ts +++ b/plugins/main/server/routes/wazuh-api.ts @@ -138,4 +138,19 @@ export function WazuhApiRoutes(router: IRouter) { async (context, request, response) => ctrl.getAppLogos(context, request, response), ); + + // Return binary dashboard + router.get( + { + path: '/api/dashboards/{name}', + validate: { + params: schema.object({ + name: schema.string(), + }), + }, + // options: { authRequired: false }, + }, + async (context, request, response) => + ctrl.getAppDashboards(context, request, response), + ); }