From 73d3de0c83ee063cb0131133b4c3d565bbfb7fef Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Mon, 23 Oct 2023 14:16:45 -0300 Subject: [PATCH 1/8] Add data grid hook --- .../data_grid/use_data_grid.ts | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts diff --git a/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts b/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts new file mode 100644 index 0000000000..abb6bae576 --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts @@ -0,0 +1,140 @@ +import { EuiDataGridCellValueElementProps, EuiDataGridColumn, EuiDataGridProps, EuiDataGridSorting } from "@elastic/eui" +import { useEffect, useMemo, useState } from "react"; +import { SearchResponse } from "@opensearch-project/opensearch/api/types"; +import { IFieldType, IndexPattern } from "../../../../../../../src/plugins/data/common"; + +type tDataGridProps = { + indexPattern: IndexPattern; + results: SearchResponse; + defaultColumns: EuiDataGridColumn[]; + DocViewInspectButton: ({ rowIndex }: EuiDataGridCellValueElementProps) => React.JSX.Element + ariaLabelledBy: string; +}; + +export const parseColumns = (fields: IFieldType[]): EuiDataGridColumn[] => { + console.log(fields); + // filter the fields that are not esStype keyword + const notKeywordFields = fields.filter((field) => field.esTypes && !field.esTypes.includes('keyword')); + return notKeywordFields.map((field) => { + return { + id: field.name, + display: field.name, + schema: field.type, + actions: { + showHide: true, + }, + }; + }) || []; +} + +export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => { + const { indexPattern, DocViewInspectButton, results, defaultColumns } = props; + /** Columns **/ + const [columns, setColumns] = useState(defaultColumns); + const [columnVisibility, setVisibility] = useState(() => + columns.map(({ id }) => id) + ); + /** Rows */ + const [rows, setRows] = useState([]); + const rowCount = results ? results?.hits?.total as number : 0; + /** Sorting **/ + // get default sorting from default columns + const getDefaultSorting = () => { + const defaultSort = columns.find((column) => column.isSortable || column.defaultSortDirection); + return defaultSort ? [{ id: defaultSort.id, direction: defaultSort.defaultSortDirection || 'desc' }] : []; + } + const defaultSorting: EuiDataGridSorting['columns'] = getDefaultSorting(); + const [sortingColumns, setSortingColumns] = useState(defaultSorting); + const onSort = (sortingColumns) => {setSortingColumns(sortingColumns)}; + /** Pagination **/ + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 20 }); + const onChangeItemsPerPage = useMemo(() => (pageSize) => + setPagination((pagination) => ({ + ...pagination, + pageSize, + pageIndex: 0, + })), [rows, rowCount]); + const onChangePage = (pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })) + + useEffect(() => { + setRows(results?.hits?.hits || []) + }, [results, results?.hits, results?.hits?.total]) + + + useEffect(() => { + setPagination((pagination) => ({ ...pagination, pageIndex: 0 })); + }, [rowCount]) + + const parseData = (resultsHits: SearchResponse['hits']['hits']): any[] => { + const data = resultsHits.map((hit) => { + if (!hit) { + return {} + } + const source = hit._source as object; + const data = { + ...source, + _id: hit._id, + _index: hit._index, + _type: hit._type, + _score: hit._score, + }; + return data; + }); + return data; + } + + const renderCellValue = ({ rowIndex, columnId, setCellProps }) => { + const rowsParsed = parseData(rows); + function getFormatted(rowIndex, columnId) { + if (columnId.includes('.')) { + // when the column is a nested field. The column could have 2 to n levels + // get dinamically the value of the nested field + const nestedFields = columnId.split('.'); + let value = rowsParsed[rowIndex]; + nestedFields.forEach((field) => { + if (value) { + value = value[field]; + } + }); + return value; + } else { + return rowsParsed[rowIndex][columnId].formatted + ? rowsParsed[rowIndex][columnId].formatted + : rowsParsed[rowIndex][columnId]; + } + } + // On the context data always is stored the current page data (pagination) + // then the rowIndex is relative to the current page + const relativeRowIndex = rowIndex % pagination.pageSize; + return rowsParsed.hasOwnProperty(relativeRowIndex) + ? getFormatted(relativeRowIndex, columnId) + : null; + }; + + const leadingControlColumns = useMemo(() => { + return [ + { + id: 'inspectCollapseColumn', + headerCellRender: () => null, + rowCellRender: (props) => DocViewInspectButton({ ...props, rowIndex: props.rowIndex % pagination.pageSize }), + width: 40, + }, + ]; + }, [results]); + + return { + "aria-labelledby": props.ariaLabelledBy, + columns: parseColumns(indexPattern?.fields || []), + columnVisibility: { visibleColumns: columnVisibility, setVisibleColumns: setVisibility }, + renderCellValue: renderCellValue, + leadingControlColumns: leadingControlColumns, + rowCount, + sorting: { columns: sortingColumns, onSort }, + pagination: { + ...pagination, + pageSizeOptions: [20, 50, 100], + onChangeItemsPerPage: onChangeItemsPerPage, + onChangePage: onChangePage, + } + } +} \ No newline at end of file From 0d74b97e79859359ee8642d7a9c551dded06483f Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Mon, 23 Oct 2023 14:18:24 -0300 Subject: [PATCH 2/8] Add doc viewer component and hook --- .../vulnerabilities/doc_viewer/doc_viewer.tsx | 150 ++++++++++++++++++ .../doc_viewer/use_doc_viewer.ts | 28 ++++ 2 files changed, 178 insertions(+) create mode 100644 plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx create mode 100644 plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts diff --git a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx new file mode 100644 index 0000000000..08e170f9d5 --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx @@ -0,0 +1,150 @@ +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { escapeRegExp } from 'lodash'; +import { i18n } from '@osd/i18n'; +import { FieldIcon } from '../../../../../../../src/plugins/opensearch_dashboards_react/public'; +import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; + +const COLLAPSE_LINE_LENGTH = 350; +const DOT_PREFIX_RE = /(.).+?\./g; + +export type tDocViewerProps = { + flattened: any; + formatted: any; + mapping: any; + indexPattern: any; +} + +/** + * Convert a dot.notated.string into a short + * version (d.n.string) + */ +export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.'); + +export function getFieldTypeName(type: string) { + switch (type) { + case 'boolean': + return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', { + defaultMessage: 'Boolean field', + }); + case 'conflict': + return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', { + defaultMessage: 'Conflicting field', + }); + case 'date': + return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', { + defaultMessage: 'Date field', + }); + case 'geo_point': + return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', { + defaultMessage: 'Geo point field', + }); + case 'geo_shape': + return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', { + defaultMessage: 'Geo shape field', + }); + case 'ip': + return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', { + defaultMessage: 'IP address field', + }); + case 'murmur3': + return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', { + defaultMessage: 'Murmur3 field', + }); + case 'number': + return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', { + defaultMessage: 'Number field', + }); + case 'source': + // Note that this type is currently not provided, type for _source is undefined + return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', { + defaultMessage: 'Source field', + }); + case 'string': + return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', { + defaultMessage: 'String field', + }); + case 'nested': + return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', { + defaultMessage: 'Nested field', + }); + default: + return i18n.translate('discover.fieldNameIcons.unknownFieldAriaLabel', { + defaultMessage: 'Unknown field', + }); + } +} + +const DocViewer = (props: tDocViewerProps) => { + const [fieldRowOpen, setFieldRowOpen] = useState({} as Record); + const { flattened, formatted, mapping, indexPattern } = props; + + return (<> + {flattened && ( + + + {Object.keys(flattened) + .sort() + .map((field, index) => { + const value = String(formatted[field]); + const fieldMapping = mapping(field); + const isCollapsible = value.length > COLLAPSE_LINE_LENGTH; + const isCollapsed = isCollapsible && !fieldRowOpen[field]; + const valueClassName = classNames({ + // eslint-disable-next-line @typescript-eslint/naming-convention + osdDocViewer__value: true, + 'truncate-by-height': isCollapsible && isCollapsed, + }); + const isNestedField = + !indexPattern.fields.getByName(field) && + !!indexPattern.fields.getAll().find((patternField) => { + // We only want to match a full path segment + const nestedRootRegex = new RegExp(escapeRegExp(field) + '(\\.|$)'); + return nestedRootRegex.test(patternField.subType?.nested?.path ?? ''); + }); + const fieldType = isNestedField ? 'nested' : indexPattern.fields.getByName(field)?.type; + const typeName = getFieldTypeName(String(fieldType)); + const displayName = field; + const fieldIconProps = { fill: 'none', color: 'gray' } + const scripted = Boolean(fieldMapping?.scripted) + + return ( + + + + + ); + })} + +
+ + + + + + + {displayName} + + + + +
+
+ )}) +}; + +export default DocViewer; \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts new file mode 100644 index 0000000000..d38e58bc3a --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts @@ -0,0 +1,28 @@ +import { tDocViewerProps } from "./doc_viewer" +import { IndexPattern } from "../../../../../../../src/plugins/data/common"; + +type tUseDocViewerInputs = { + indexPattern: IndexPattern; + doc: any; +} + +export const useDocViewer = (props: tUseDocViewerInputs): tDocViewerProps => { + const { indexPattern, doc } = props; + + if (!indexPattern || !doc) { + return { + flattened: {}, + formatted: {}, + indexPattern: undefined, + mapping: undefined + } + } + + const mapping = indexPattern?.fields.getByName; + return { + flattened: indexPattern?.flattenHit(doc), + formatted: indexPattern?.formatHit(doc, 'html'), + indexPattern, + mapping + } +} \ No newline at end of file From 21b1ab5b2cdf42fd800a47a9bb9bb9117897b15a Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Mon, 23 Oct 2023 14:18:46 -0300 Subject: [PATCH 3/8] Add ui utils components --- .../common/components/loading_spinner.scss | 4 + .../common/components/loading_spinner.tsx | 21 ++ .../common/components/no_results.tsx | 198 ++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss create mode 100644 plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx create mode 100644 plugins/main/public/components/overview/vulnerabilities/common/components/no_results.tsx diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss new file mode 100644 index 0000000000..051ab642c1 --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss @@ -0,0 +1,4 @@ +.discoverNoResults { + display: flex; + align-items: center; +} diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx new file mode 100644 index 0000000000..7f505e6167 --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx @@ -0,0 +1,21 @@ +import './loading_spinner.scss'; +import React from 'react'; +import { EuiTitle, EuiPanel, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; +import { FormattedMessage } from '@osd/i18n/react'; + +export function LoadingSpinner() { + return ( + + } + title={ + +

+ +

+
+ } + /> +
+ ); +} diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/no_results.tsx b/plugins/main/public/components/overview/vulnerabilities/common/components/no_results.tsx new file mode 100644 index 0000000000..3d592c867d --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/common/components/no_results.tsx @@ -0,0 +1,198 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; + +import { + EuiCallOut, + EuiCode, + EuiDescriptionList, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +interface Props { + timeFieldName?: string; + queryLanguage?: string; +} + +export const DiscoverNoResults = ({ timeFieldName, queryLanguage }: Props) => { + let timeFieldMessage; + + if (timeFieldName) { + timeFieldMessage = ( + + + + +

+ +

+ +

+ +

+
+
+ ); + } + + let luceneQueryMessage; + + if (queryLanguage === 'lucene') { + const searchExamples = [ + { + description: 200, + title: ( + + + + + + ), + }, + { + description: status:200, + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499], + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499] AND extension:PHP, + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499] AND (extension:php OR extension:html), + title: ( + + + + + + ), + }, + ]; + + luceneQueryMessage = ( + + + + +

+ +

+ +

+ +

+
+ + + + + + +
+ ); + } + + return ( + + + + } + color="warning" + iconType="help" + data-test-subj="discoverNoResults" + /> + {timeFieldMessage} + {luceneQueryMessage} + + + ); +}; From 419b982a2a8a022d4f0bee6eb64432df2e397b5b Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Mon, 23 Oct 2023 14:19:26 -0300 Subject: [PATCH 4/8] Add new vuls inventory component --- .../vulnerabilities/common/constants.ts | 2 +- .../dashboards/inventory/config/index.ts | 21 +++ .../dashboards/inventory/inventory.scss | 8 + .../dashboards/inventory/inventory.tsx | 161 +++++++++++++++++- 4 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts create mode 100644 plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss diff --git a/plugins/main/public/components/overview/vulnerabilities/common/constants.ts b/plugins/main/public/components/overview/vulnerabilities/common/constants.ts index 665c99285a..29536bb7f2 100644 --- a/plugins/main/public/components/overview/vulnerabilities/common/constants.ts +++ b/plugins/main/public/components/overview/vulnerabilities/common/constants.ts @@ -1 +1 @@ -export const VULNERABILITIES_INDEX_PATTERN_ID = 'wazuh-inventory-cve' \ No newline at end of file +export const VULNERABILITIES_INDEX_PATTERN_ID = 'wazuh-states-vulnerabilities'; \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts new file mode 100644 index 0000000000..4d3617cc3e --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts @@ -0,0 +1,21 @@ +import { EuiDataGridColumn } from "@elastic/eui"; + +export const inventoryTableDefaultColumns: EuiDataGridColumn[] = [ + { + id: '@timestamp', + displayAsText: 'timestamp', + defaultSortDirection: 'desc', + }, + { + id: 'agent.id', + displayAsText: 'agent.id', + }, + { + id: 'agent.name', + displayAsText: 'agent.name', + }, + { + id: 'event.severity', + displayAsText: 'event.severity', + } + ] \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss new file mode 100644 index 0000000000..f9387bb638 --- /dev/null +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss @@ -0,0 +1,8 @@ +.vulsInventoryContainer { + height: calc(100vh - 100px); +} + + +.headerIsExpanded .vulsInventoryContainer { + height: calc(100vh - 149px); +} \ No newline at end of file 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 b94a76b6c7..2202974f2f 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -1,7 +1,164 @@ -import React from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { getPlugins } from '../../../../../kibana-services'; +import useSearchBarConfiguration from '../../searchbar/use-search-bar-configuration' +import { IntlProvider } from 'react-intl'; +import { + EuiDataGrid, + EuiPageTemplate, + EuiToolTip, + EuiButtonIcon, + EuiDataGridCellValueElementProps, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiTitle, +} from '@elastic/eui'; +import { Filter, IndexPattern, OpenSearchQuerySortValue } from '../../../../../../../../src/plugins/data/common'; +import { SearchResponse } from '../../../../../../../../src/core/server'; +import DocViewer from '../../doc_viewer/doc_viewer'; +import { DiscoverNoResults } from '../../common/components/no_results'; +import { LoadingSpinner } from '../../common/components/loading_spinner'; +import { useDataGrid } from '../../data_grid/use_data_grid'; +import { inventoryTableDefaultColumns } from './config'; +import { useDocViewer } from '../../doc_viewer/use_doc_viewer'; +import './inventory.scss'; +import { VULNERABILITIES_INDEX_PATTERN_ID } from '../../common/constants'; export const InventoryVuls = () => { + const { searchBarProps } = useSearchBarConfiguration({ + defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID, + }) + const { isLoading, filters, query, indexPatterns } = searchBarProps; + const SearchBar = getPlugins().data.ui.SearchBar; + const [results, setResults] = useState({} as SearchResponse); + const [inspectedHit, setInspectedHit] = useState(undefined); + const [indexPattern, setIndexPattern] = useState(undefined); + const [isSearching, setIsSearching] = useState(false); + + + const onClickInspectDoc = useMemo(() => (index: number) => { + const rowClicked = results.hits.hits[index]; + setInspectedHit(rowClicked); + }, [results]); + + const DocViewInspectButton = ({ rowIndex }: EuiDataGridCellValueElementProps) => { + const inspectHintMsg = 'Inspect document details'; + + return ( + + onClickInspectDoc(rowIndex)} + iconType='inspect' + aria-label={inspectHintMsg} + /> + + ); + }; + + const dataGridProps = useDataGrid({ + ariaLabelledBy: 'Vulnerabilities Inventory Table', + defaultColumns: inventoryTableDefaultColumns, + results, + indexPattern: indexPattern as IndexPattern, + DocViewInspectButton + }) + + const { pagination, sorting } = dataGridProps; + + const docViewerProps = useDocViewer({ + doc: inspectedHit, + indexPattern: indexPattern as IndexPattern, + }) + + useEffect(() => { + if (!isLoading) { + setIndexPattern(indexPatterns?.[0] as IndexPattern); + search(); + } + }, [JSON.stringify(searchBarProps), JSON.stringify(pagination), JSON.stringify(sorting)]); + + /** + * Search in index pattern + */ + const search = async (): Promise => { + const indexPattern = indexPatterns?.[0]; + if (indexPattern) { + setIsSearching(true); + const data = getPlugins().data + const searchSource = await data.search.searchSource.create(); + const timeFilter: Filter['query'] = [{ + range: { + '@timestamp': { + gte: searchBarProps?.dateRangeFrom, + lte: searchBarProps?.dateRangeTo, + format: 'strict_date_optional_time', + }, + }, + }] + const combined = [...timeFilter, ...(filters || [])]; + const fromField = (pagination?.pageIndex || 0) * (pagination?.pageSize || 100); + const sortOrder: OpenSearchQuerySortValue[] = sorting?.columns.map((column) => { + const sortDirection = column.direction === 'asc' ? 'asc' : 'desc'; + return { [column?.id || '']: sortDirection } as OpenSearchQuerySortValue; + }) || []; + + const results = await searchSource + .setParent(undefined) + .setField('filter', combined) + .setField('query', query) + .setField('sort', sortOrder) + .setField('size', pagination?.pageSize) + .setField('from', fromField) + .setField('index', indexPattern as IndexPattern) + .fetch(); + setResults(results); + setIsSearching(false); + } + }; + + const timeField = indexPattern?.timeFieldName ? indexPattern.timeFieldName : undefined; + + return ( -
Inventory dashboard
+ +
+ + <> + {isLoading ? + : + } + {isSearching ? + : null} + {!isLoading && !isSearching && results?.hits?.total === 0 ? + : null} + {!isLoading && !isSearching && results?.hits?.total > 0 ? + : null} + {inspectedHit && ( + setInspectedHit(undefined)} size="m"> + + +

Document Details

+
+
+ + + + + + + +
+ )} + +
+
+
); } From 10b2496ecebf3d2c2b615809a2685070c2df9235 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Mon, 23 Oct 2023 14:19:47 -0300 Subject: [PATCH 5/8] Add vuls inventory in module rendering --- .../main/public/components/common/modules/modules-defaults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/main/public/components/common/modules/modules-defaults.js b/plugins/main/public/components/common/modules/modules-defaults.js index 956a396495..75838b8d9b 100644 --- a/plugins/main/public/components/common/modules/modules-defaults.js +++ b/plugins/main/public/components/common/modules/modules-defaults.js @@ -148,7 +148,7 @@ export const ModulesDefaults = { EventsTab, ], buttons: ['settings'], - availableFor: ['manager', 'agent'], + availableFor: ['manager'], }, mitre: { init: 'dashboard', From 6c5105f375bc1fe5cd394d626ccca0310138046d Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Tue, 24 Oct 2023 11:38:15 -0300 Subject: [PATCH 6/8] Add full height container --- .../dashboards/inventory/inventory.scss | 4 ++-- .../vulnerabilities/dashboards/inventory/inventory.tsx | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss index f9387bb638..df8c71901f 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.scss @@ -1,8 +1,8 @@ .vulsInventoryContainer { - height: calc(100vh - 100px); + height: calc(100vh - 104px); } .headerIsExpanded .vulsInventoryContainer { - height: calc(100vh - 149px); + height: calc(100vh - 153px); } \ No newline at end of file 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 2202974f2f..717beef298 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -75,7 +75,12 @@ export const InventoryVuls = () => { useEffect(() => { if (!isLoading) { setIndexPattern(indexPatterns?.[0] as IndexPattern); - search(); + try { + search(); + }catch(error){ + console.error(error); + // check when filters are wrong and the search fails + } } }, [JSON.stringify(searchBarProps), JSON.stringify(pagination), JSON.stringify(sorting)]); @@ -123,8 +128,8 @@ export const InventoryVuls = () => { return ( -
{ )} -
); } From 94e47a6d70ce40759960a5e90d01c4afaf50de63 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Tue, 24 Oct 2023 11:38:28 -0300 Subject: [PATCH 7/8] Add inventory table columns --- .../dashboards/inventory/config/index.ts | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts index 4d3617cc3e..fce980ecad 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/config/index.ts @@ -3,19 +3,38 @@ import { EuiDataGridColumn } from "@elastic/eui"; export const inventoryTableDefaultColumns: EuiDataGridColumn[] = [ { id: '@timestamp', - displayAsText: 'timestamp', - defaultSortDirection: 'desc', + displayAsText: 'Timestamp', }, { - id: 'agent.id', - displayAsText: 'agent.id', + id: 'package.name', + displayAsText: 'Name', }, { - id: 'agent.name', - displayAsText: 'agent.name', + id: 'package.version', + displayAsText: 'Version', }, { - id: 'event.severity', - displayAsText: 'event.severity', + id: 'package.architecture', + displayAsText: 'Architecture', + }, + { + id: 'vulnerability.severity', + displayAsText: 'Severity', + }, + { + id: 'vulnerability.id', + displayAsText: 'Id', + }, + { + id: 'vulnerability.score.version', + displayAsText: 'Score version', + }, + { + id: 'vulnerability.score.base', + displayAsText: 'Score', + }, + { + id: 'event.created', + displayAsText: 'Detected time', } ] \ No newline at end of file From 5908045e6f1e4b9cd93e1cad648b8851e0e84add Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Wed, 25 Oct 2023 10:54:44 -0300 Subject: [PATCH 8/8] Remove columns fields filter by keyword type --- .../overview/vulnerabilities/data_grid/use_data_grid.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts b/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts index abb6bae576..7ef7cc50e0 100644 --- a/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts +++ b/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts @@ -12,10 +12,7 @@ type tDataGridProps = { }; export const parseColumns = (fields: IFieldType[]): EuiDataGridColumn[] => { - console.log(fields); - // filter the fields that are not esStype keyword - const notKeywordFields = fields.filter((field) => field.esTypes && !field.esTypes.includes('keyword')); - return notKeywordFields.map((field) => { + return fields.map((field) => { return { id: field.name, display: field.name,