-
+
Tactics
-
+
this.onGearButtonClick()}
aria-label={'tactics options'}
>
}
isOpen={this.state.isPopoverOpen}
- panelPaddingSize="none"
+ panelPaddingSize='none'
closePopover={() => this.closePopover()}
>
@@ -292,7 +318,7 @@ export class Tactics extends Component {
{this.props.isLoading ? (
-
+
) : (
{this.getTacticsList()}
diff --git a/plugins/main/public/components/overview/mitre/components/techniques/techniques.tsx b/plugins/main/public/components/overview/mitre/components/techniques/techniques.tsx
index 50c4f10b28..2daf6834cb 100644
--- a/plugins/main/public/components/overview/mitre/components/techniques/techniques.tsx
+++ b/plugins/main/public/components/overview/mitre/components/techniques/techniques.tsx
@@ -85,11 +85,21 @@ export const Techniques = withWindowSize(
}
shouldComponentUpdate(nextProps, nextState) {
- const { filterParams, indexPattern, selectedTactics, isLoading } = this.props;
+ const { filterParams, indexPattern, selectedTactics, isLoading } =
+ this.props;
if (nextProps.isLoading !== isLoading) return true;
- if (JSON.stringify(nextProps.filterParams) !== JSON.stringify(filterParams)) return true;
- if (JSON.stringify(nextProps.indexPattern) !== JSON.stringify(indexPattern)) return true;
- if (JSON.stringify(nextState.selectedTactics) !== JSON.stringify(selectedTactics))
+ if (
+ JSON.stringify(nextProps.filterParams) !== JSON.stringify(filterParams)
+ )
+ return true;
+ if (
+ JSON.stringify(nextProps.indexPattern) !== JSON.stringify(indexPattern)
+ )
+ return true;
+ if (
+ JSON.stringify(nextState.selectedTactics) !==
+ JSON.stringify(selectedTactics)
+ )
return true;
return false;
}
@@ -97,9 +107,11 @@ export const Techniques = withWindowSize(
componentDidUpdate(prevProps) {
const { isLoading, tacticsObject, filters } = this.props;
if (
- JSON.stringify(prevProps.tacticsObject) !== JSON.stringify(tacticsObject) ||
+ JSON.stringify(prevProps.tacticsObject) !==
+ JSON.stringify(tacticsObject) ||
isLoading !== prevProps.isLoading ||
- JSON.stringify(prevProps.filterParams) !== JSON.stringify(this.props.filterParams)
+ JSON.stringify(prevProps.filterParams) !==
+ JSON.stringify(this.props.filterParams)
)
this.getTechniquesCount();
}
@@ -108,7 +120,12 @@ export const Techniques = withWindowSize(
this._isMount = false;
}
- showToast(color: string, title: string = '', text: string = '', time: number = 3000) {
+ showToast(
+ color: string,
+ title: string = '',
+ text: string = '',
+ time: number = 3000,
+ ) {
getToasts().add({
color: color,
title: title,
@@ -134,9 +151,14 @@ export const Techniques = withWindowSize(
this._isMount && this.setState({ loadingAlerts: true });
// TODO: use `status` and `statusText` to show errors
// @ts-ignore
- const { data, status, statusText } = await getElasticAlerts(indexPattern, filters, aggs);
- const { buckets } = data.aggregations.techniques;
- this._isMount && this.setState({ techniquesCount: buckets, loadingAlerts: false });
+ const { data, status, statusText } = await getElasticAlerts(
+ indexPattern,
+ filters,
+ aggs,
+ );
+ const buckets = data?.aggregations?.techniques?.buckets || [];
+ this._isMount &&
+ this.setState({ techniquesCount: buckets, loadingAlerts: false });
} catch (error) {
const options = {
context: `${Techniques.name}.getTechniquesCount`,
@@ -163,23 +185,31 @@ export const Techniques = withWindowSize(
items: [
{
name: 'Filter for value',
- icon:
,
+ icon:
,
onClick: () => {
this.closeActionsMenu();
- this.addFilter({ key: 'rule.mitre.id', value: techniqueID, negate: false });
+ this.addFilter({
+ key: 'rule.mitre.id',
+ value: techniqueID,
+ negate: false,
+ });
},
},
{
name: 'Filter out value',
- icon:
,
+ icon:
,
onClick: () => {
this.closeActionsMenu();
- this.addFilter({ key: 'rule.mitre.id', value: techniqueID, negate: true });
+ this.addFilter({
+ key: 'rule.mitre.id',
+ value: techniqueID,
+ negate: true,
+ });
},
},
{
name: 'View technique details',
- icon:
,
+ icon:
,
onClick: () => {
this.closeActionsMenu();
this.showFlyout(techniqueID);
@@ -192,7 +222,11 @@ export const Techniques = withWindowSize(
techniqueColumnsResponsive() {
if (this.props && this.props.windowSize) {
- return this.props.windowSize.width < 930 ? 2 : this.props.windowSize.width < 1200 ? 3 : 4;
+ return this.props.windowSize.width < 930
+ ? 2
+ : this.props.windowSize.width < 1200
+ ? 3
+ : 4;
} else {
return 4;
}
@@ -224,10 +258,16 @@ export const Techniques = withWindowSize(
const params = { limit: limitResults };
this.setState({ isSearching: true });
const output = await this.getMitreTechniques(params);
- const totalItems = (((output || {}).data || {}).data || {}).total_affected_items;
+ const totalItems = (((output || {}).data || {}).data || {})
+ .total_affected_items;
let mitreTechniques = [];
mitreTechniques.push(...output.data.data.affected_items);
- if (totalItems && output.data && output.data.data && totalItems > limitResults) {
+ if (
+ totalItems &&
+ output.data &&
+ output.data.data &&
+ totalItems > limitResults
+ ) {
const extraResults = await Promise.all(
Array(Math.ceil((totalItems - params.limit) / params.limit))
.fill()
@@ -237,7 +277,7 @@ export const Techniques = withWindowSize(
offset: limitResults * (1 + index),
});
return response.data.data.affected_items;
- })
+ }),
);
mitreTechniques.push(...extraResults.flat());
}
@@ -246,16 +286,22 @@ export const Techniques = withWindowSize(
buildObjTechniques(techniques) {
const techniquesObj = [];
- techniques.forEach((element) => {
- const mitreObj = this.state.mitreTechniques.find((item) => item.id === element);
+ techniques.forEach(element => {
+ const mitreObj = this.state.mitreTechniques.find(
+ item => item.id === element,
+ );
if (mitreObj) {
const mitreTechniqueName = mitreObj.name;
const mitreTechniqueID =
mitreObj.source === MITRE_ATTACK
? mitreObj.external_id
- : mitreObj.references.find((item) => item.source === MITRE_ATTACK).external_id;
+ : mitreObj.references.find(item => item.source === MITRE_ATTACK)
+ .external_id;
mitreTechniqueID
- ? techniquesObj.push({ id: mitreTechniqueID, name: mitreTechniqueName })
+ ? techniquesObj.push({
+ id: mitreTechniqueID,
+ name: mitreTechniqueName,
+ })
: '';
}
});
@@ -268,47 +314,59 @@ export const Techniques = withWindowSize(
let hash = {};
let tacticsToRender: Array
= [];
const currentTechniques = Object.keys(tacticsObject)
- .map((tacticsKey) => ({
+ .map(tacticsKey => ({
tactic: tacticsKey,
- techniques: this.buildObjTechniques(tacticsObject[tacticsKey].techniques),
+ techniques: this.buildObjTechniques(
+ tacticsObject[tacticsKey].techniques,
+ ),
}))
- .filter((tactic) => this.props.selectedTactics[tactic.tactic])
- .map((tactic) => tactic.techniques)
+ .filter(tactic => this.props.selectedTactics[tactic.tactic])
+ .map(tactic => tactic.techniques)
.flat()
- .filter((techniqueID, index, array) => array.indexOf(techniqueID) === index);
+ .filter(
+ (techniqueID, index, array) => array.indexOf(techniqueID) === index,
+ );
tacticsToRender = currentTechniques
- .filter((technique) =>
+ .filter(technique =>
this.state.filteredTechniques
? this.state.filteredTechniques.includes(technique.id)
: technique.id && hash[technique.id]
? false
- : (hash[technique.id] = true)
+ : (hash[technique.id] = true),
)
- .map((technique) => {
+ .map(technique => {
return {
id: technique.id,
label: `${technique.id} - ${technique.name}`,
quantity:
- (techniquesCount.find((item) => item.key === technique.id) || {}).doc_count || 0,
+ (techniquesCount.find(item => item.key === technique.id) || {})
+ .doc_count || 0,
};
})
- .filter((technique) => (this.state.hideAlerts ? technique.quantity !== 0 : true));
+ .filter(technique =>
+ this.state.hideAlerts ? technique.quantity !== 0 : true,
+ );
const tacticsToRenderOrdered = tacticsToRender
.sort((a, b) => b.quantity - a.quantity)
.map((item, idx) => {
const tooltipContent = `View details of ${item.label} (${item.id})`;
const toolTipAnchorClass =
- 'wz-display-inline-grid' + (this.state.hover === item.id ? ' wz-mitre-width' : ' ');
+ 'wz-display-inline-grid' +
+ (this.state.hover === item.id ? ' wz-mitre-width' : ' ');
return (
this.setState({ hover: item.id })}
onMouseLeave={() => this.setState({ hover: '' })}
key={idx}
- style={{ border: '1px solid #8080804a', maxWidth: 'calc(25% - 8px)', maxHeight: 41 }}
+ style={{
+ border: '1px solid #8080804a',
+ maxWidth: 'calc(25% - 8px)',
+ maxHeight: 41,
+ }}
>
);
});
- if (this.state.isSearching || this.state.loadingAlerts || this.props.isLoading) {
+ if (
+ this.state.isSearching ||
+ this.state.loadingAlerts ||
+ this.props.isLoading
+ ) {
return (
-
-
+
+
);
}
@@ -387,7 +460,7 @@ export const Techniques = withWindowSize(
return (
+
);
}
}
openDiscover(e, techniqueID) {
- this.addFilter({ key: 'rule.mitre.id', value: techniqueID, negate: false });
+ this.addFilter({
+ key: 'rule.mitre.id',
+ value: techniqueID,
+ negate: false,
+ });
this.props.onSelectedTabChanged('events');
}
openDashboard(e, techniqueID) {
- this.addFilter({ key: 'rule.mitre.id', value: techniqueID, negate: false });
+ this.addFilter({
+ key: 'rule.mitre.id',
+ value: techniqueID,
+ negate: false,
+ });
this.props.onSelectedTabChanged('dashboard');
}
@@ -430,7 +515,8 @@ export const Techniques = withWindowSize(
params: { query: filter.value },
type: 'phrase',
negate: filter.negate || false,
- index: AppState.getCurrentPattern() || getSettingDefaultValue('pattern'),
+ index:
+ AppState.getCurrentPattern() || getSettingDefaultValue('pattern'),
},
query: { match_phrase: matchPhrase },
$state: { store: 'appState' },
@@ -438,13 +524,14 @@ export const Techniques = withWindowSize(
filterManager.addFilters([newFilter]);
}
- onChange = (searchValue) => {
+ onChange = searchValue => {
if (!searchValue) {
- this._isMount && this.setState({ filteredTechniques: false, isSearching: false });
+ this._isMount &&
+ this.setState({ filteredTechniques: false, isSearching: false });
}
};
- onSearch = async (searchValue) => {
+ onSearch = async searchValue => {
try {
if (searchValue) {
this._isMount && this.setState({ isSearching: true });
@@ -453,12 +540,18 @@ export const Techniques = withWindowSize(
search: searchValue,
},
});
- const filteredTechniques = (((response || {}).data || {}).data.affected_items || []).map(
- (item) => [item].filter((reference) => reference.source === MITRE_ATTACK)[0].external_id
+ const filteredTechniques = (
+ ((response || {}).data || {}).data.affected_items || []
+ ).map(
+ item =>
+ [item].filter(reference => reference.source === MITRE_ATTACK)[0]
+ .external_id,
);
- this._isMount && this.setState({ filteredTechniques, isSearching: false });
+ this._isMount &&
+ this.setState({ filteredTechniques, isSearching: false });
} else {
- this._isMount && this.setState({ filteredTechniques: false, isSearching: false });
+ this._isMount &&
+ this.setState({ filteredTechniques: false, isSearching: false });
}
} catch (error) {
const options = {
@@ -474,7 +567,8 @@ export const Techniques = withWindowSize(
},
};
getErrorOrchestrator().handleError(options);
- this._isMount && this.setState({ filteredTechniques: false, isSearching: false });
+ this._isMount &&
+ this.setState({ filteredTechniques: false, isSearching: false });
}
};
async closeActionsMenu() {
@@ -507,7 +601,7 @@ export const Techniques = withWindowSize(
-
+
Techniques
@@ -518,26 +612,26 @@ export const Techniques = withWindowSize(
Hide techniques with no alerts
this.hideAlerts()}
+ onChange={e => this.hideAlerts()}
/>
-
+
-
+
{this.renderFacet()}
@@ -552,5 +646,5 @@ export const Techniques = withWindowSize(
);
}
- }
+ },
);
diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/check-module-enabled.tsx b/plugins/main/public/components/overview/vulnerabilities/common/components/check-module-enabled.tsx
new file mode 100644
index 0000000000..f254dbde95
--- /dev/null
+++ b/plugins/main/public/components/overview/vulnerabilities/common/components/check-module-enabled.tsx
@@ -0,0 +1,115 @@
+import React, { useEffect, useState } from 'react';
+import {
+ clusterReq,
+ clusterNodes,
+} from '../../../../../controllers/management/components/management/configuration/utils/wz-fetch';
+import { WzRequest } from '../../../../../react-services';
+import { webDocumentationLink } from '../../../../../../common/services/web_documentation';
+import { EuiCallOut, EuiLink } from '@elastic/eui';
+import { UI_LOGGER_LEVELS } from '../../../../../../common/constants';
+import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types';
+import { getErrorOrchestrator } from '../../../../../react-services/common-services';
+import { useUserPermissionsRequirements } from '../../../../common/hooks';
+
+async function checkVDIsEnabledCluster() {
+ // Get nodes
+ const responseNodes = await clusterNodes();
+
+ const nodes = responseNodes.data.data.affected_items.map(({ name }) => name);
+
+ // Check if at least some of the nodes has the module enabled
+ for (const node of nodes) {
+ const responseNodeWmodules = await WzRequest.apiReq(
+ 'GET',
+ `/cluster/${node}/configuration/wmodules/wmodules`,
+ {},
+ );
+ const vdConfiguration =
+ responseNodeWmodules.data.data?.affected_items?.[0]?.wmodules?.find(
+ ({ ['vulnerability-detection']: wmodule }) => wmodule,
+ );
+ if (vdConfiguration?.['vulnerability-detection']?.enabled === 'yes') {
+ return true;
+ }
+ }
+ return false;
+}
+
+async function checkVDIsEnabledManager() {
+ const responseWmodules = await WzRequest.apiReq(
+ 'GET',
+ `/manager/configuration/wmodules/wmodules`,
+ {},
+ );
+
+ const vdConfiguration =
+ responseWmodules.data.data?.affected_items?.[0]?.wmodules?.find(
+ ({ ['vulnerability-detection']: wmodule }) => wmodule,
+ );
+ return vdConfiguration?.['vulnerability-detection']?.enabled === 'yes';
+}
+
+export const ModuleEnabledCheck = () => {
+ const [data, setData] = useState<{ enabled: boolean } | null>(null);
+ const [userPermissionRequirements] = useUserPermissionsRequirements([
+ { action: 'cluster:status', resource: '*:*:*' },
+ { action: 'cluster:read', resource: 'node:id:*' },
+ { action: 'manager:read', resource: '*:*:*' },
+ ]);
+
+ const checkVDIsEnabled = async () => {
+ try {
+ // Check cluster status
+ setData(null);
+ const clusterStatus = await clusterReq();
+
+ // Check if the module is enabled
+ const enabled =
+ clusterStatus.data.data.enabled === 'yes' &&
+ clusterStatus.data.data.running === 'yes'
+ ? await checkVDIsEnabledCluster()
+ : await checkVDIsEnabledManager();
+ setData({ enabled });
+ } catch (error) {
+ const options = {
+ context: `${ModuleEnabledCheck.name}.useEffect`,
+ level: UI_LOGGER_LEVELS.ERROR,
+ severity: UI_ERROR_SEVERITIES.BUSINESS,
+ error: {
+ error: error,
+ message: error.message || error,
+ title: 'Error checking if the module is enabled',
+ },
+ };
+ getErrorOrchestrator().handleError(options);
+ }
+ };
+
+ useEffect(() => {
+ /* Only check if the module is enabled if the user has the expected permissions to
+ do the API requests */
+ if (!userPermissionRequirements) {
+ checkVDIsEnabled();
+ }
+ }, [userPermissionRequirements]);
+
+ return data?.enabled === false ? (
+
+
+ Vulnerabilies detection module is not enabled. You can learn to how to
+ configure following the{' '}
+
+ documentation
+
+ .
+
+
+ ) : null;
+};
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
new file mode 100644
index 0000000000..c021ace475
--- /dev/null
+++ b/plugins/main/public/components/overview/vulnerabilities/common/hocs/validate-vulnerabilities-states-index-pattern.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import { compose } from 'redux';
+import { connect } from 'react-redux';
+import { withGuardAsync, withReduxProvider } from '../../../../common/hocs';
+import {
+ getAngularModule,
+ getCore,
+ 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 '../../../../common/loading/loading-spinner-data-source';
+
+const INDEX_PATTERN_CREATION_NO_INDEX = 'INDEX_PATTERN_CREATION_NO_INDEX';
+
+async function checkExistenceIndexPattern(indexPatternID: string) {
+ const response = await getSavedObjects().client.get(
+ 'index-pattern',
+ indexPatternID,
+ );
+
+ return response?.error?.statusCode !== 404;
+}
+
+async function checkExistenceIndices(indexPatternTitle: string) {
+ try {
+ const fields = await SavedObject.getIndicesFields(indexPatternTitle);
+ return { exist: true, fields };
+ } catch (error) {
+ return { exist: false };
+ }
+}
+
+async function createIndexPattern(indexPattern, fields: any) {
+ 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 validateVulnerabilitiesStateDataSources({
+ vulnerabilitiesStatesindexPatternID: indexPatternID,
+}) {
+ try {
+ // Check the existence of related index pattern
+ const existIndexPattern = await checkExistenceIndexPattern(indexPatternID);
+
+ // If the idnex pattern does not exist, then check the existence of index
+ if (!existIndexPattern) {
+ // 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 the some index match 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.
+ */
+ getCore().application.navigateToApp(vulnerabilityDetection.id);
+ }
+ return {
+ ok: false,
+ data: {},
+ };
+ } 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(
+ withReduxProvider,
+ connect(mapStateToProps),
+ withGuardAsync(
+ validateVulnerabilitiesStateDataSources,
+ ({ error, check }) => ,
+ () => ,
+ ),
+);
diff --git a/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx b/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx
index 3a3ce9aa7a..c8ba68c25f 100644
--- a/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx
+++ b/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx
@@ -1,14 +1,4 @@
import { useState, useEffect } from 'react';
-import { search } from '../../../../common/search-bar/search-bar-service';
-import {
- IIndexPattern,
- IndexPattern,
- Filter,
-} from '../../../../../../../../src/plugins/data/public';
-import {
- ErrorFactory,
- HttpError,
-} from '../../../../../react-services/error-management';
import { SavedObject } from '../../../../../react-services';
interface UseCheckIndexFieldsResult {
@@ -19,17 +9,10 @@ interface UseCheckIndexFieldsResult {
resultIndexData: any;
}
-const useCheckIndexFields = (
- indexPatternId: string,
- indexPattern: IIndexPattern | undefined,
- indexType: string,
- filters?: Filter[],
- query?: any,
-) => {
+const useCheckIndexFields = (indexPatternId: string, indexType: string) => {
const [isError, setIsError] = useState(false);
const [error, setError] = useState(null);
const [isSuccess, setIsSuccess] = useState(false);
- const [resultIndexData, setResultIndexData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
@@ -39,26 +22,7 @@ const useCheckIndexFields = (
// Check that the index exists
await SavedObject.getIndicesFields(indexPatternId, indexType);
setIsSuccess(true);
-
- // Check that the index has data
- search({
- indexPattern: indexPattern as IndexPattern,
- filters,
- query,
- })
- .then((results: any) => {
- setResultIndexData(results);
- setIsLoading(false);
- })
- .catch((error: any) => {
- const searchError = ErrorFactory.create(HttpError, {
- error,
- message: 'Error fetching vulnerabilities',
- });
- setError(searchError);
- setIsError(true);
- setIsLoading(false);
- });
+ setIsLoading(false);
} catch (error) {
setError(error);
setIsError(true);
@@ -69,13 +33,12 @@ const useCheckIndexFields = (
checkIndexFields();
}
- }, [indexPatternId, query, indexPattern]);
+ }, [indexPatternId, indexType]);
return {
isError,
error,
isSuccess,
- resultIndexData,
isLoading,
} as UseCheckIndexFieldsResult;
};
diff --git a/plugins/main/public/components/overview/vulnerabilities/common/vulnerability_detector_adapters.tsx b/plugins/main/public/components/overview/vulnerabilities/common/vulnerability_detector_adapters.tsx
new file mode 100644
index 0000000000..b605aff9c4
--- /dev/null
+++ b/plugins/main/public/components/overview/vulnerabilities/common/vulnerability_detector_adapters.tsx
@@ -0,0 +1,279 @@
+import {
+ AUTHORIZED_AGENTS,
+ VULNERABILITY_IMPLICIT_CLUSTER_MODE_FILTER,
+ WAZUH_ALERTS_PATTERN,
+} from '../../../../../common/constants';
+import { AppState } from '../../../../react-services';
+import { FilterHandler } from '../../../../utils/filter-handler';
+import { Filter } from '../../../../../../../src/plugins/data/public';
+import { FilterManager } from '../../../../../../../src/plugins/data/public';
+import { FilterStateStore } from '../../../../../../../src/plugins/data/common';
+
+const SESSION_STORAGE_FILTERS_NAME = 'wazuh_persistent_searchbar_filters';
+const SESSION_STORAGE_PREV_FILTER_NAME = 'wazuh_persistent_current_filter';
+
+/**
+ * When the component is unmounted, the original filters that arrived
+ * when the component was mounted are added.
+ * Both when the component is mounted and unmounted, the index pattern is
+ * updated so that the pin action adds the agent with the correct index pattern.
+ */
+export const vulnerabilityIndexFiltersAdapter = (
+ filterManager: FilterManager,
+ defaultIndexPatternID: string,
+) => {
+ const filterHandler = new FilterHandler(AppState.getCurrentPattern());
+ const initialFilters: Filter[] = [];
+ const storagePreviousFilters = sessionStorage.getItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ );
+ if (storagePreviousFilters) {
+ const previousFilters = JSON.parse(storagePreviousFilters);
+ const previousFiltersWithoutImplicitFilters = previousFilters.filter(
+ (filter: Filter) => !filter?.$state?.isImplicit,
+ );
+ previousFiltersWithoutImplicitFilters.forEach((filter: Filter) => {
+ initialFilters.push(filter);
+ });
+ }
+
+ /**
+ Add vulnerability module implicit filters
+ */
+ const isCluster = AppState.getClusterInfo().status == 'enabled';
+ const managerFilter = filterHandler.managerQuery(
+ isCluster
+ ? AppState.getClusterInfo().cluster
+ : AppState.getClusterInfo().manager,
+ true,
+ VULNERABILITY_IMPLICIT_CLUSTER_MODE_FILTER[
+ AppState.getClusterInfo().status
+ ],
+ );
+
+ // rule.groups is added so that the events tab can use it
+ const ruleGroupsFilter = filterHandler.ruleGroupQuery(
+ 'vulnerability-detector',
+ );
+ initialFilters.unshift(ruleGroupsFilter);
+ initialFilters.unshift(managerFilter);
+ const vulnerabilitiesFilters = initialFilters.map((filter: Filter) => {
+ return {
+ ...filter,
+ meta: {
+ ...filter.meta,
+ index: defaultIndexPatternID,
+ },
+ };
+ });
+ sessionStorage.setItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ JSON.stringify(initialFilters),
+ );
+ /* The rule.groups filter is removed. It is not applicable to the case of the vulnerabilities module since it has its own index */
+ const vulnerabilitiesFiltersWithoutRuleGroup = vulnerabilitiesFilters.filter(
+ (filter: Filter) => filter.meta.key !== 'rule.groups',
+ );
+ const implicitPinnedAgent = getImplicitPinnedAgent(
+ initialFilters,
+ defaultIndexPatternID,
+ );
+ if (implicitPinnedAgent) {
+ const filtersWithoutNormalAgents =
+ vulnerabilitiesFiltersWithoutRuleGroup.filter(x => {
+ return x.meta.key !== 'agent.id';
+ });
+ filtersWithoutNormalAgents.push(implicitPinnedAgent);
+ filterManager.setFilters(filtersWithoutNormalAgents);
+ } else {
+ filterManager.setFilters(vulnerabilitiesFiltersWithoutRuleGroup);
+ }
+};
+
+export const restorePrevIndexFiltersAdapter = (
+ previousFilters: Filter[],
+ toIndexPattern: string | null,
+ filterManager: FilterManager,
+ defaultIndexPatternID: string,
+) => {
+ const filterHandler = new FilterHandler(AppState.getCurrentPattern());
+ const isCluster = AppState.getClusterInfo().status == 'enabled';
+ const cleanedFilters = cleanFilters(
+ previousFilters,
+ defaultIndexPatternID,
+ ).filter(
+ (filter: Filter) =>
+ filter?.meta?.key !==
+ VULNERABILITY_IMPLICIT_CLUSTER_MODE_FILTER[
+ AppState.getClusterInfo().status
+ ],
+ );
+ /* Restore original manager implicit filter */
+ const managerFilter = filterHandler.managerQuery(
+ isCluster
+ ? AppState.getClusterInfo().cluster
+ : AppState.getClusterInfo().manager,
+ isCluster,
+ );
+ cleanedFilters.unshift(managerFilter);
+ const implicitPinnedAgent = getImplicitPinnedAgent(
+ previousFilters,
+ toIndexPattern ?? WAZUH_ALERTS_PATTERN,
+ );
+ if (implicitPinnedAgent) {
+ const cleanedFiltersWithoutNormalAgents = cleanedFilters.filter(x => {
+ return x.meta.key !== 'agent.id';
+ });
+ cleanedFiltersWithoutNormalAgents.push(implicitPinnedAgent);
+ filterManager.setFilters(cleanedFiltersWithoutNormalAgents);
+ } else {
+ filterManager.setFilters(cleanedFilters);
+ }
+};
+
+export const onUpdateAdapter = (
+ filters: Filter[],
+ filterManager: FilterManager,
+ onFiltersUpdated?: (filters: Filter[]) => void,
+) => {
+ const prevStoragePattern = sessionStorage.getItem(
+ SESSION_STORAGE_PREV_FILTER_NAME,
+ );
+ const storagePreviousFilters = sessionStorage.getItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ );
+ /**
+ * If there are persisted filters, it is necessary to add them when
+ * updating the filters in the filterManager
+ */
+ const cleanedFilters = cleanFilters(
+ filters,
+ prevStoragePattern ?? WAZUH_ALERTS_PATTERN,
+ );
+ if (storagePreviousFilters) {
+ const previousFilters = JSON.parse(storagePreviousFilters);
+ const previousImplicitFilters = previousFilters.filter(
+ (filter: Filter) => filter?.$state?.isImplicit,
+ );
+ const cleanedPreviousImplicitFilters = cleanFilters(
+ previousImplicitFilters,
+ prevStoragePattern ?? WAZUH_ALERTS_PATTERN,
+ );
+ /* Normal filters added to storagePreviousFilters are added to keep them between dashboard and inventory tab */
+ const newFilters = filters.filter(
+ (filter: Filter) => !filter?.$state?.isImplicit,
+ );
+
+ sessionStorage.setItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ JSON.stringify([...previousImplicitFilters, ...newFilters]),
+ );
+
+ filterManager.setFilters([
+ ...cleanedPreviousImplicitFilters,
+ ...cleanedFilters,
+ ]);
+ onFiltersUpdated &&
+ onFiltersUpdated([...cleanedPreviousImplicitFilters, ...cleanedFilters]);
+ } else {
+ filterManager.setFilters(cleanedFilters);
+ onFiltersUpdated && onFiltersUpdated(cleanedFilters);
+ }
+};
+
+/**
+ * Verify if a pinned agent exists, identifying it by its meta.isImplicit attribute or by the agentId query param URL.
+ * We also compare the agent.id filter with the agentId query param because the OSD filter definition does not include the "isImplicit" attribute that Wazuh adds.
+ * There may be cases where the "isImplicit" attribute is lost, since any action regarding filters that is done with the
+ * filterManager ( addFilters, setFilters, setGlobalFilters, setAppFilters)
+ * does a mapAndFlattenFilters mapping to the filters that removes any attributes that are not part of the filter definition.
+ * */
+const getImplicitPinnedAgent = (
+ filters: Filter[],
+ toIndexPattern: string,
+): Filter | undefined => {
+ const pinnedAgentByFilterManager = filters.find(
+ (filter: Filter) =>
+ filter?.meta?.key === 'agent.id' && !!filter?.$state?.isImplicit,
+ );
+ const url = window.location.href;
+ const regex = new RegExp('agentId=' + '[^&]*');
+ const match = url.match(regex);
+ const isPinnedAgentByUrl = match && match[0];
+ if (pinnedAgentByFilterManager && isPinnedAgentByUrl) {
+ return {
+ ...pinnedAgentByFilterManager,
+ meta: {
+ ...pinnedAgentByFilterManager.meta,
+ index: toIndexPattern,
+ },
+ $state: { store: FilterStateStore.APP_STATE, isImplicit: true },
+ };
+ }
+
+ if (isPinnedAgentByUrl) {
+ const agentId = match[0].split('=')[1];
+ return {
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'agent.id',
+ negate: false,
+ params: { query: agentId },
+ type: 'phrase',
+ index: toIndexPattern,
+ },
+ query: {
+ match: {
+ 'agent.id': {
+ query: agentId,
+ type: 'phrase',
+ },
+ },
+ },
+ $state: { store: 'appState', isImplicit: true },
+ };
+ }
+ return undefined;
+};
+
+/**
+ * Return cleaned filters.
+ * Clean the known issue with the auto loaded agent.id filters from the searchbar
+ * and filters those filters that are not related to the default index pattern.
+ * This cleanup adjusts the index pattern of a pinned agent, if applicable.
+ * @param previousFilters
+ * @returns
+ */
+const cleanFilters = (
+ previousFilters: Filter[],
+ indexPatternToClean: string,
+) => {
+ const mappedFilters = previousFilters.filter(
+ (filter: Filter) =>
+ filter?.meta?.controlledBy !== AUTHORIZED_AGENTS && // remove auto loaded agent.id filters
+ filter?.meta?.index !== indexPatternToClean,
+ );
+ return mappedFilters;
+};
+
+export const updateFiltersStorage = (filters: Filter[]) => {
+ const storagePreviousFilters = sessionStorage.getItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ );
+ if (storagePreviousFilters) {
+ const previousFilters = JSON.parse(storagePreviousFilters);
+ const previousImplicitFilters = previousFilters.filter(
+ (filter: Filter) => filter?.$state?.isImplicit,
+ );
+ /* Normal filters added to storagePreviousFilters are added to keep them between dashboard and inventory tab */
+ const newFilters = filters.filter(
+ (filter: Filter) => !filter?.$state?.isImplicit,
+ );
+
+ sessionStorage.setItem(
+ SESSION_STORAGE_FILTERS_NAME,
+ JSON.stringify([...previousImplicitFilters, ...newFilters]),
+ );
+ }
+};
diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx
index 1fd7a8f421..9174ced121 100644
--- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx
+++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx
@@ -37,8 +37,15 @@ import { useDocViewer } from '../../../../common/doc-viewer/use-doc-viewer';
import { withErrorBoundary } from '../../../../common/hocs';
import { search } from '../../../../common/search-bar/search-bar-service';
import { exportSearchToCSV } from '../../../../common/data-grid/data-grid-service';
-import { WAZUH_INDEX_TYPE_VULNERABILITIES } from '../../../../../../common/constants';
-import useCheckIndexFields from '../../common/hooks/useCheckIndexFields';
+import {
+ vulnerabilityIndexFiltersAdapter,
+ onUpdateAdapter,
+ restorePrevIndexFiltersAdapter,
+} from '../../common/vulnerability_detector_adapters';
+import { compose } from 'redux';
+import { withVulnerabilitiesStateDataSource } from '../../common/hocs/validate-vulnerabilities-states-index-pattern';
+import { ModuleEnabledCheck } from '../../common/components/check-module-enabled';
+import { DataSourceFilterManagerVulnerabilitiesStates } from '../../../../../react-services/data-sources';
const InventoryVulsComponent = () => {
const appConfig = useAppConfig();
@@ -46,7 +53,15 @@ const InventoryVulsComponent = () => {
appConfig.data['vulnerabilities.pattern'];
const { searchBarProps } = useSearchBar({
defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID,
+ onMount: vulnerabilityIndexFiltersAdapter,
+ onUpdate: onUpdateAdapter,
+ onUnMount: restorePrevIndexFiltersAdapter,
});
+
+ const fetchFilters = DataSourceFilterManagerVulnerabilitiesStates.getFilters(
+ searchBarProps.filters,
+ VULNERABILITIES_INDEX_PATTERN_ID,
+ );
const { isLoading, filters, query, indexPatterns } = searchBarProps;
const SearchBar = getPlugins().data.ui.SearchBar;
const [results, setResults] = useState({} as SearchResponse);
@@ -97,26 +112,12 @@ const InventoryVulsComponent = () => {
indexPattern: indexPattern as IndexPattern,
});
- const {
- isError,
- error,
- isSuccess,
- resultIndexData,
- isLoading: isLoadingCheckIndex,
- } = useCheckIndexFields(
- VULNERABILITIES_INDEX_PATTERN_ID,
- indexPatterns?.[0],
- WAZUH_INDEX_TYPE_VULNERABILITIES,
- filters,
- query,
- );
-
useEffect(() => {
- if (!isLoading && isSuccess) {
+ if (!isLoading) {
setIndexPattern(indexPatterns?.[0] as IndexPattern);
search({
indexPattern: indexPatterns?.[0] as IndexPattern,
- filters,
+ filters: fetchFilters,
query,
pagination,
sorting,
@@ -138,13 +139,13 @@ const InventoryVulsComponent = () => {
JSON.stringify(searchBarProps),
JSON.stringify(pagination),
JSON.stringify(sorting),
- isLoadingCheckIndex,
+ JSON.stringify(fetchFilters),
]);
const onClickExportResults = async () => {
const params = {
indexPattern: indexPatterns?.[0] as IndexPattern,
- filters,
+ filters: fetchFilters,
query,
fields: columnVisibility.visibleColumns,
pagination: {
@@ -169,100 +170,97 @@ const InventoryVulsComponent = () => {
return (
-
- <>
- {isLoading || isLoadingCheckIndex ? (
-
- ) : (
-
- )}
- {isSearching ? : null}
- {!isLoading &&
- !isSearching &&
- (isError ||
- results?.hits?.total === 0 ||
- resultIndexData?.hits?.total === 0) ? (
-
- ) : null}
- {!isLoading &&
- !isSearching &&
- isSuccess &&
- results?.hits?.total > 0 ? (
-
- {}}
- tooltip={
- results?.hits?.total &&
- results?.hits?.total > MAX_ENTRIES_PER_QUERY
- ? {
- ariaLabel: 'Warning',
- content: `The query results has exceeded the limit of 10,000 hits. To provide a better experience the table only shows the first ${formatNumWithCommas(
- MAX_ENTRIES_PER_QUERY,
- )} hits.`,
- iconType: 'alert',
- position: 'top',
- }
- : undefined
- }
- />
-
- Export Formated
-
- >
- ),
- }}
- />
- ) : null}
- {inspectedHit && (
- setInspectedHit(undefined)} size='m'>
-
-
- Document details
-
-
-
-
-
-
-
-
-
-
- )}
- >
-
+ <>
+
+
+ <>
+ {isLoading ? (
+
+ ) : (
+
+ )}
+ {isSearching ? : null}
+ {!isLoading && !isSearching && results?.hits?.total === 0 ? (
+
+ ) : null}
+ {!isLoading && !isSearching && results?.hits?.total > 0 ? (
+
+ {}}
+ tooltip={
+ results?.hits?.total &&
+ results?.hits?.total > MAX_ENTRIES_PER_QUERY
+ ? {
+ ariaLabel: 'Warning',
+ content: `The query results has exceeded the limit of 10,000 hits. To provide a better experience the table only shows the first ${formatNumWithCommas(
+ MAX_ENTRIES_PER_QUERY,
+ )} hits.`,
+ iconType: 'alert',
+ position: 'top',
+ }
+ : undefined
+ }
+ />
+
+ Export Formated
+
+ >
+ ),
+ }}
+ />
+ ) : null}
+ {inspectedHit && (
+ setInspectedHit(undefined)} size='m'>
+
+
+ Document details
+
+
+
+
+
+
+
+
+
+
+ )}
+ >
+
+ >
);
};
-export const InventoryVuls = withErrorBoundary(InventoryVulsComponent);
+export const InventoryVuls = compose(
+ withErrorBoundary,
+ withVulnerabilitiesStateDataSource,
+)(InventoryVulsComponent);
diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx
index c29bc6393b..6508987edb 100644
--- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx
+++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx
@@ -1,4 +1,5 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
+import { SearchResponse } from '../../../../../../../../src/core/server';
import { getPlugins } from '../../../../../kibana-services';
import { ViewMode } from '../../../../../../../../src/plugins/embeddable/public';
import { getDashboardPanels } from './dashboard_panels';
@@ -10,9 +11,26 @@ import { getKPIsPanel } from './dashboard_panels_kpis';
import { useAppConfig } from '../../../../common/hooks';
import { withErrorBoundary } from '../../../../common/hocs';
import { DiscoverNoResults } from '../../common/components/no_results';
-import { WAZUH_INDEX_TYPE_VULNERABILITIES } from '../../../../../../common/constants';
import { LoadingSpinner } from '../../common/components/loading_spinner';
-import useCheckIndexFields from '../../common/hooks/useCheckIndexFields';
+import {
+ vulnerabilityIndexFiltersAdapter,
+ restorePrevIndexFiltersAdapter,
+ onUpdateAdapter,
+ updateFiltersStorage,
+} from '../../common/vulnerability_detector_adapters';
+import { search } from '../../../../common/search-bar/search-bar-service';
+import { IndexPattern } from '../../../../../../../../src/plugins/data/common';
+import {
+ ErrorFactory,
+ ErrorHandler,
+ HttpError,
+} from '../../../../../react-services/error-management';
+import { compose } from 'redux';
+import { withVulnerabilitiesStateDataSource } from '../../common/hocs/validate-vulnerabilities-states-index-pattern';
+import { ModuleEnabledCheck } from '../../common/components/check-module-enabled';
+import { DataSourceFilterManagerVulnerabilitiesStates } from '../../../../../react-services/data-sources';
+import { DashboardContainerInput } from '../../../../../../../../src/plugins/dashboard/public';
+
const plugins = getPlugins();
const SearchBar = getPlugins().data.ui.SearchBar;
@@ -29,29 +47,58 @@ const DashboardVulsComponent: React.FC = () => {
appConfig.data['vulnerabilities.pattern'];
const { searchBarProps } = useSearchBar({
defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID,
+ onMount: vulnerabilityIndexFiltersAdapter,
+ onUpdate: onUpdateAdapter,
+ onUnMount: restorePrevIndexFiltersAdapter,
});
- const {
- isLoading: isLoadingSearchbar,
- filters,
- query,
- indexPatterns,
- } = searchBarProps;
- const { isError, error, isSuccess, resultIndexData, isLoading } =
- useCheckIndexFields(
- VULNERABILITIES_INDEX_PATTERN_ID,
- indexPatterns?.[0],
- WAZUH_INDEX_TYPE_VULNERABILITIES,
- filters,
- query,
- );
+ /* This function is responsible for updating the storage filters so that the
+ filters between dashboard and inventory added using visualizations call to actions.
+ Without this feature, filters added using visualizations call to actions are
+ not maintained between dashboard and inventory tabs */
+ const handleFilterByVisualization = (newInput: DashboardContainerInput) => {
+ updateFiltersStorage(newInput.filters);
+ };
+
+ const fetchFilters = DataSourceFilterManagerVulnerabilitiesStates.getFilters(
+ searchBarProps.filters,
+ VULNERABILITIES_INDEX_PATTERN_ID,
+ );
+
+ const { isLoading, query, indexPatterns } = searchBarProps;
+
+ const [isSearching, setIsSearching] = useState(false);
+ const [results, setResults] = useState({} as SearchResponse);
+
+ useEffect(() => {
+ if (!isLoading) {
+ search({
+ indexPattern: indexPatterns?.[0] as IndexPattern,
+ filters: fetchFilters,
+ query,
+ })
+ .then(results => {
+ setResults(results);
+ setIsSearching(false);
+ })
+ .catch(error => {
+ const searchError = ErrorFactory.create(HttpError, {
+ error,
+ message: 'Error fetching vulnerabilities',
+ });
+ ErrorHandler.handleError(searchError);
+ setIsSearching(false);
+ });
+ }
+ }, [JSON.stringify(searchBarProps), JSON.stringify(fetchFilters)]);
return (
<>
<>
- {isLoading || isLoadingSearchbar ? : null}
- {!isLoading && !isLoadingSearchbar ? (
+
+ {isLoading ? : null}
+ {!isLoading ? (
{
showQueryBar={true}
/>
) : null}
- {!isLoadingSearchbar &&
- !isLoading &&
- (isError ||
- !resultIndexData ||
- resultIndexData?.hits?.total === 0) ? (
-
+ {isSearching ? : null}
+ {!isLoading && !isSearching && results?.hits?.total === 0 ? (
+
) : null}
- {!isLoadingSearchbar &&
- !isLoading &&
- isSuccess &&
- resultIndexData &&
- resultIndexData?.hits?.total !== 0 ? (
+ {!isLoading && !isSearching && results?.hits?.total > 0 ? (
{
VULNERABILITIES_INDEX_PATTERN_ID,
),
isFullScreenMode: false,
- filters: searchBarProps.filters ?? [],
+ filters: fetchFilters ?? [],
useMargins: false,
id: 'vulnerability-detector-dashboard-tab-filters',
timeRange: {
@@ -98,6 +138,7 @@ const DashboardVulsComponent: React.FC = () => {
},
hidePanelTitles: true,
}}
+ onInputUpdated={handleFilterByVisualization}
/>
{
viewMode: ViewMode.VIEW,
panels: getKPIsPanel(VULNERABILITIES_INDEX_PATTERN_ID),
isFullScreenMode: false,
- filters: searchBarProps.filters ?? [],
+ filters: fetchFilters ?? [],
useMargins: true,
id: 'kpis-vulnerability-detector-dashboard-tab',
timeRange: {
@@ -121,13 +162,14 @@ const DashboardVulsComponent: React.FC = () => {
},
hidePanelTitles: true,
}}
+ onInputUpdated={handleFilterByVisualization}
/>
{
},
hidePanelTitles: false,
}}
+ onInputUpdated={handleFilterByVisualization}
/>
) : null}
@@ -152,4 +195,7 @@ const DashboardVulsComponent: React.FC = () => {
);
};
-export const DashboardVuls = withErrorBoundary(DashboardVulsComponent);
+export const DashboardVuls = compose(
+ withErrorBoundary,
+ withVulnerabilitiesStateDataSource,
+)(DashboardVulsComponent);
diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels.ts b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels.ts
index 3269a8d65b..b7c98727e1 100644
--- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels.ts
+++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels.ts
@@ -245,7 +245,7 @@ const getVisStateTopVulnerabilitiesEndpoints = (indexPatternId: string) => {
enabled: true,
type: 'terms',
params: {
- field: 'agent.id',
+ field: 'agent.name',
orderBy: '1',
order: 'desc',
size: 10,
@@ -253,7 +253,7 @@ const getVisStateTopVulnerabilitiesEndpoints = (indexPatternId: string) => {
otherBucketLabel: 'Other',
missingBucket: false,
missingBucketLabel: 'Missing',
- customLabel: 'package.path',
+ customLabel: 'Endpoint name',
},
schema: 'segment',
},
diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels_filters.ts b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels_filters.ts
index 7d8e2c64be..6f3c07cb35 100644
--- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels_filters.ts
+++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard_panels_filters.ts
@@ -6,7 +6,7 @@ const getVisStateFilter = (
indexPatternId: string,
title: string,
label: string,
- fieldName: string
+ fieldName: string,
) => {
return {
id,
@@ -79,9 +79,11 @@ const getVisStateFilter = (
};
export const getDashboardFilters = (
- indexPatternId: string
+ indexPatternId: string,
): {
- [panelId: string]: DashboardPanelState;
+ [panelId: string]: DashboardPanelState<
+ EmbeddableInput & { [k: string]: unknown }
+ >;
} => {
return {
topPackageSelector: {
@@ -100,7 +102,7 @@ export const getDashboardFilters = (
indexPatternId,
'Top packages vulnerabilities',
'Top 5 packages',
- 'package.name'
+ 'package.name',
),
},
},
@@ -120,7 +122,7 @@ export const getDashboardFilters = (
indexPatternId,
'Top operating system vulnerabilities',
'Top 5 OS',
- 'host.os.full'
+ 'host.os.full',
),
},
},
@@ -140,7 +142,7 @@ export const getDashboardFilters = (
indexPatternId,
'Agent filter',
'Top 5 agents',
- 'agent.id'
+ 'agent.name',
),
},
},
@@ -160,7 +162,7 @@ export const getDashboardFilters = (
indexPatternId,
'Top vulnerabilities',
'Top 5 vulnerabilities',
- 'vulnerability.id'
+ 'vulnerability.id',
),
},
},
diff --git a/plugins/main/public/components/wz-agent-selector/wz-agent-selector.js b/plugins/main/public/components/wz-agent-selector/wz-agent-selector.js
index 3eeb3c0d62..9a2f166307 100644
--- a/plugins/main/public/components/wz-agent-selector/wz-agent-selector.js
+++ b/plugins/main/public/components/wz-agent-selector/wz-agent-selector.js
@@ -74,6 +74,10 @@ class WzAgentSelector extends Component {
const agentFilters = currentAppliedFilters.filter(x => {
return x.meta.key !== 'agent.id';
});
+ const cookieCurrentPattern =
+ AppState.getCurrentPattern() || getSettingDefaultValue('pattern');
+ const currentPattern =
+ this.props?.moduleIndexPatternTitle ?? cookieCurrentPattern;
const filter = {
meta: {
alias: null,
@@ -82,8 +86,7 @@ class WzAgentSelector extends Component {
negate: false,
params: { query: agentIdList[0] },
type: 'phrase',
- index:
- AppState.getCurrentPattern() || getSettingDefaultValue('pattern'),
+ index: currentPattern,
},
query: {
match: {
diff --git a/plugins/main/public/controllers/management/components/management/common/resources-handler.ts b/plugins/main/public/controllers/management/components/management/common/resources-handler.ts
index de4583b4f0..f0b57c0625 100644
--- a/plugins/main/public/controllers/management/components/management/common/resources-handler.ts
+++ b/plugins/main/public/controllers/management/components/management/common/resources-handler.ts
@@ -93,7 +93,7 @@ export class ResourcesHandler {
fileName: string,
content: string,
overwrite: boolean,
- relativeDirname: string,
+ relativeDirname?: string,
) {
try {
const result = await WzRequest.apiReq(
@@ -102,7 +102,9 @@ export class ResourcesHandler {
{
params: {
overwrite: overwrite,
- relative_dirname: relativeDirname,
+ ...(this.resource !== 'lists'
+ ? { relative_dirname: relativeDirname }
+ : {}),
},
body: content.toString(),
origin: 'raw',
@@ -119,7 +121,7 @@ export class ResourcesHandler {
* @param {Resource} resource
* @param {String} fileName
*/
- async deleteFile(fileName: string, relativeDirname: string = '') {
+ async deleteFile(fileName: string, relativeDirname?: string) {
try {
const result = await WzRequest.apiReq(
'DELETE',
diff --git a/plugins/main/public/controllers/overview/components/__snapshots__/stats.test.tsx.snap b/plugins/main/public/controllers/overview/components/__snapshots__/stats.test.tsx.snap
index 624404e142..6f6d02f5a1 100644
--- a/plugins/main/public/controllers/overview/components/__snapshots__/stats.test.tsx.snap
+++ b/plugins/main/public/controllers/overview/components/__snapshots__/stats.test.tsx.snap
@@ -34,12 +34,13 @@ exports[`Stats component renders correctly to match the snapshot 1`] = `
-
-
-
+
@@ -67,12 +68,13 @@ exports[`Stats component renders correctly to match the snapshot 1`] = `