From 9157e9a9f6a6ea537b9ae617dfc1b21989c32e1b Mon Sep 17 00:00:00 2001
From: Antonio <34042064+Desvelao@users.noreply.github.com>
Date: Thu, 24 Oct 2024 13:22:50 +0200
Subject: [PATCH] Fix ability to filter form FIM details (#7119)
* fix(fim): fix ability to filter from flyout details in FIM inventory
- Fix FIM inventory flyout filters for Files and Registry tabs
- Add ability to filter from Last analysis and Last modified fields from
Registry details
- Add ability to select the visible columns in the Files and Registry
tables
- Add to Files table the columns: date (hidden), md5 (hidden), sha1 (hidden), sha256 (hidden)
- Add to Registry table the columns: date (hidden)
- Renamed Last Modified to Last modified in the Files table
* chore(changelog): add entry
---
CHANGELOG.md | 3 +
.../components/agents/fim/inventory.tsx | 55 ++---------
.../agents/fim/inventory/fileDetail.tsx | 16 ++--
.../agents/fim/inventory/registry-table.tsx | 63 +++++++++----
.../components/agents/fim/inventory/table.tsx | 94 ++++++++++++++-----
5 files changed, 132 insertions(+), 99 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d428a7785..bc73471823 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ All notable changes to the Wazuh app project will be documented in this file.
- Support for Wazuh 4.10.0
- Added sample data for YARA [#6964](https://github.com/wazuh/wazuh-dashboard-plugins/issues/6964)
- Added a custom filter and visualization for vulnerability.under_evaluation field [#6968](https://github.com/wazuh/wazuh-dashboard-plugins/issues/6968) [#7044](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7044) [#7046](https://github.com/wazuh/wazuh-dashboard-plugins/issues/7046)
+- Added ability to filter from File Integrity Monitoring registry inventory [#7119](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7119)
+- Added new field columns and ability to select the visible fields in the File Integrity Monitoring Files and Registry tables [#7119](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7119)
### Changed
@@ -33,6 +35,7 @@ All notable changes to the Wazuh app project will be documented in this file.
- Fixed filter management to prevent hiding when adding multiple filters [#7077](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7077)
- Fixed vulnerabilities inventory table scroll [#7118](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7118)
- Fixed the filter are displayed cropped on screens of 575px to 767px in vulnerability detection module [#7047](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7047)
+- Fixed ability to filter from files inventory details flyout of File Integrity Monitoring [#7119](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7119)
### Removed
diff --git a/plugins/main/public/components/agents/fim/inventory.tsx b/plugins/main/public/components/agents/fim/inventory.tsx
index fbb5d41b22..6353589cb4 100644
--- a/plugins/main/public/components/agents/fim/inventory.tsx
+++ b/plugins/main/public/components/agents/fim/inventory.tsx
@@ -28,7 +28,6 @@ import { InventoryTable, RegistryTable } from './inventory/';
import { WzRequest } from '../../../react-services/wz-request';
import { getToasts } from '../../../kibana-services';
import { ICustomBadges } from '../../wz-search-bar/components';
-import { filtersToObject } from '../../wz-search-bar';
import { UI_LOGGER_LEVELS } from '../../../../common/constants';
import {
UI_ERROR_SEVERITIES,
@@ -42,7 +41,6 @@ import { webDocumentationLink } from '../../../../common/services/web_documentat
export class Inventory extends Component {
_isMount = false;
state: {
- filters: [];
selectedTabId: 'files' | 'registry';
totalItemsFile: number;
totalItemsRegistry: number;
@@ -57,7 +55,6 @@ export class Inventory extends Component {
constructor(props) {
super(props);
this.state = {
- filters: [],
syscheck: [],
selectedTabId: 'files',
totalItemsFile: 0,
@@ -66,7 +63,6 @@ export class Inventory extends Component {
customBadges: [],
isConfigured: false,
};
- this.onFiltersChange.bind(this);
}
async componentDidMount() {
@@ -135,56 +131,20 @@ export class Inventory extends Component {
return auxTabs;
}
- getStoreFilters(props) {
- const { section, selectView, agent } = props;
- const filters = JSON.parse(
- window.localStorage.getItem(
- `wazuh-${section}-${selectView}-${
- this.state?.selectedTabId || 'files'
- }-${agent['id']}`,
- ) || '{}',
- );
- return filters;
- }
-
- setStoreFilters(filters) {
- const { section, selectView, agent } = this.props;
- window.localStorage.setItem(
- `wazuh-${section}-${selectView}-${this.state?.selectedTabId || 'files'}-${
- agent['id']
- }`,
- JSON.stringify(filters),
- );
- }
-
- onFiltersChange = filters => {
- this.setState({ filters });
- };
-
- onTotalItemsChange = (totalItems: number) => {
- this.setState({ totalItemsFile: totalItems });
- };
-
onSelectedTabChanged = id => {
this.setState({ selectedTabId: id });
};
- buildFilter(type) {
- const filters = filtersToObject(this.state.filters);
- const filter = {
- ...filters,
- limit: type === 'file' ? '15' : '1',
- ...(type === 'registry' ? { q: 'type=registry_key' } : { type }),
- ...(type === 'file' && { sort: '+file' }),
- };
- return filter;
- }
-
async getItemNumber(type: 'file' | 'registry') {
try {
const agentID = this.props.agent.id;
const response = await WzRequest.apiReq('GET', `/syscheck/${agentID}`, {
- params: this.buildFilter(type),
+ params: {
+ limit: 1, // reduce the size because only need the total items. 0 gives error
+ ...(type === 'registry'
+ ? { q: 'type=registry_key' }
+ : { q: 'type=file' }),
+ },
});
if (type === 'file') {
return {
@@ -257,8 +217,6 @@ export class Inventory extends Component {
filters={filters}
items={syscheck}
totalItems={totalItemsFile}
- onFiltersChange={this.onFiltersChange}
- onTotalItemsChange={this.onTotalItemsChange}
/>
)}
{selectedTabId === 'registry' && (
@@ -266,7 +224,6 @@ export class Inventory extends Component {
{...this.props}
filters={filters}
totalItems={totalItemsRegistry}
- onFiltersChange={this.onFiltersChange}
/>
)}
>
diff --git a/plugins/main/public/components/agents/fim/inventory/fileDetail.tsx b/plugins/main/public/components/agents/fim/inventory/fileDetail.tsx
index 0dff1a4698..ea2fe09158 100644
--- a/plugins/main/public/components/agents/fim/inventory/fileDetail.tsx
+++ b/plugins/main/public/components/agents/fim/inventory/fileDetail.tsx
@@ -198,6 +198,7 @@ export class FileDetails extends Component {
name: 'Last analysis',
grow: 2,
icon: 'clock',
+ link: true,
transformValue: formatUIDate,
},
{
@@ -205,6 +206,7 @@ export class FileDetails extends Component {
name: 'Last modified',
grow: 2,
icon: 'clock',
+ link: true,
transformValue: formatUIDate,
},
];
@@ -290,21 +292,19 @@ export class FileDetails extends Component {
}
addFilter(field, value) {
- const { filters, onFiltersChange } = this.props;
- const newBadge: ICustomBadges = { field: 'q', value: '' };
+ const { onFiltersChange } = this.props;
+ let filterUQL = '';
if (field === 'date' || field === 'mtime') {
const value_max = moment(value).add(1, 'day');
- newBadge.value = `${field}>${moment(value).format(
+ filterUQL = `${field}>${moment(value).format(
'YYYY-MM-DD',
- )} AND ${field}<${value_max.format('YYYY-MM-DD')}`;
+ )};${field}<${value_max.format('YYYY-MM-DD')}`;
} else {
- newBadge.value = `${field}=${
+ filterUQL = `${field}=${
field === 'size' ? this.props.currentFile[field] : value
}`;
}
- !filters.some(
- item => item.field === newBadge.field && item.value === newBadge.value,
- ) && onFiltersChange([...filters, newBadge]);
+ onFiltersChange({ q: filterUQL });
this.props.closeFlyout();
}
diff --git a/plugins/main/public/components/agents/fim/inventory/registry-table.tsx b/plugins/main/public/components/agents/fim/inventory/registry-table.tsx
index ee96fff10c..6dfb410c46 100644
--- a/plugins/main/public/components/agents/fim/inventory/registry-table.tsx
+++ b/plugins/main/public/components/agents/fim/inventory/registry-table.tsx
@@ -21,18 +21,10 @@ import { withRouterSearch } from '../../../common/hocs';
import { Route, Switch } from '../../../router-search';
import NavigationService from '../../../../react-services/navigation-service';
-const searchBarWQLOptions = {
- implicitQuery: {
- query: 'type=registry_key',
- conjunction: ';',
- },
-};
-
-const searchBarWQLFilters = { default: { q: 'type=registry_key' } };
-
export const RegistryTable = withRouterSearch(
class RegistryTable extends Component {
state: {
+ filters: {};
syscheck: [];
isFlyoutVisible: Boolean;
currentFile: {
@@ -51,7 +43,7 @@ export const RegistryTable = withRouterSearch(
super(props);
this.state = {
- syscheck: [],
+ filters: {},
isFlyoutVisible: false,
currentFile: {
file: '',
@@ -73,12 +65,13 @@ export const RegistryTable = withRouterSearch(
name: 'Registry',
sortable: true,
searchable: true,
+ show: true,
},
{
field: 'mtime',
name: (
- Last Modified{' '}
+ Last modified{' '}
+ Last analysis{' '}
+
+
+ ),
+ sortable: true,
+ width: '100px',
+ render: formatUIDate,
+ searchable: false,
},
];
}
+ onFiltersChange = filters => {
+ this.setState({
+ filters,
+ });
+ };
+
renderRegistryTable() {
const getRowProps = item => {
const { file } = item;
@@ -111,6 +129,8 @@ export const RegistryTable = withRouterSearch(
const columns = this.columns();
+ const APIendpoint = `/syscheck/${this.props.agent.id}?type=registry_key`;
+
return (
@@ -118,11 +138,11 @@ export const RegistryTable = withRouterSearch(
title='Registry'
tableColumns={columns}
tableInitialSortingField='file'
- endpoint={`/syscheck/${this.props.agent.id}`}
+ endpoint={APIendpoint}
searchBarWQL={{
- options: searchBarWQLOptions,
suggestions: {
field: () => [
+ { label: 'date', description: 'filter by analysis time' },
{ label: 'file', description: 'filter by file' },
{
label: 'mtime',
@@ -133,7 +153,7 @@ export const RegistryTable = withRouterSearch(
try {
const response = await WzRequest.apiReq(
'GET',
- `/syscheck/${this.props.agent.id}`,
+ APIendpoint,
{
params: {
distinct: true,
@@ -142,12 +162,9 @@ export const RegistryTable = withRouterSearch(
sort: `+${field}`,
...(currentValue
? {
- // Add the implicit query
- q: `${searchBarWQLOptions.implicitQuery.query}${searchBarWQLOptions.implicitQuery.conjunction}${field}~${currentValue}`,
+ q: `${field}~${currentValue}`,
}
- : {
- q: `${searchBarWQLOptions.implicitQuery.query}`,
- }),
+ : {}),
},
},
);
@@ -174,11 +191,16 @@ export const RegistryTable = withRouterSearch(
},
},
}}
- filters={searchBarWQLFilters}
+ filters={this.state.filters}
showReload
downloadCsv={`fim-registry-${this.props.agent.id}`}
searchTable={true}
rowProps={getRowProps}
+ saveStateStorage={{
+ system: 'localStorage',
+ key: 'wz-fim-registry-key-table',
+ }}
+ showFieldSelector
/>
@@ -201,6 +223,7 @@ export const RegistryTable = withRouterSearch(
view='inventory'
// showViewInEvents={true}
{...this.props}
+ onFiltersChange={this.onFiltersChange}
/>
)}
>
diff --git a/plugins/main/public/components/agents/fim/inventory/table.tsx b/plugins/main/public/components/agents/fim/inventory/table.tsx
index f9021891db..80157ce30c 100644
--- a/plugins/main/public/components/agents/fim/inventory/table.tsx
+++ b/plugins/main/public/components/agents/fim/inventory/table.tsx
@@ -21,19 +21,10 @@ import { withRouterSearch } from '../../../common/hocs';
import { Route, Switch } from '../../../router-search';
import NavigationService from '../../../../react-services/navigation-service';
-const searchBarWQLOptions = {
- implicitQuery: {
- query: 'type=file',
- conjunction: ';',
- },
-};
-
-const searchBarWQLFilters = { default: { q: 'type=file' } };
-
export const InventoryTable = withRouterSearch(
class InventoryTable extends Component {
state: {
- syscheck: [];
+ filters: any;
isFlyoutVisible: Boolean;
currentFile: {
file: string;
@@ -46,14 +37,13 @@ export const InventoryTable = withRouterSearch(
agent: any;
items: [];
totalItems: number;
- onTotalItemsChange: Function;
};
constructor(props) {
super(props);
this.state = {
- syscheck: props.items,
+ filters: {},
isFlyoutVisible: false,
currentFile: {
file: '',
@@ -80,12 +70,13 @@ export const InventoryTable = withRouterSearch(
sortable: true,
width: '250px',
searchable: true,
+ show: true,
},
{
field: 'mtime',
name: (
- Last Modified{' '}
+ Last modified{' '}
+ Last analysis{' '}
+
+
+ ),
+ sortable: true,
+ width: '100px',
+ render: formatUIDate,
+ searchable: false,
+ },
+ {
+ field: 'md5',
+ name: 'MD5',
+ searchable: true,
+ sortable: true,
+ },
+ {
+ field: 'sha1',
+ name: 'SHA1',
+ searchable: true,
+ sortable: true,
+ },
+ {
+ field: 'sha256',
+ name: 'SHA256',
+ searchable: true,
+ sortable: true,
},
];
}
+ onFiltersChange = filters => {
+ this.setState({
+ filters,
+ });
+ };
+
renderFilesTable() {
const getRowProps = item => {
const { file } = item;
@@ -155,6 +194,8 @@ export const InventoryTable = withRouterSearch(
};
const columns = this.columns();
+ const APIendpoint = `/syscheck/${this.props.agent.id}?type=file`;
+
return (
@@ -162,18 +203,24 @@ export const InventoryTable = withRouterSearch(
title='Files'
tableColumns={columns}
tableInitialSortingField='file'
- endpoint={`/syscheck/${this.props.agent.id}`}
+ endpoint={APIendpoint}
searchBarWQL={{
- options: searchBarWQLOptions,
suggestions: {
field: currentValue => [
+ { label: 'date', description: 'filter by analysis time' },
{ label: 'file', description: 'filter by file' },
{ label: 'gid', description: 'filter by group id' },
{ label: 'gname', description: 'filter by group name' },
+ { label: 'md5', description: 'filter by MD5 checksum' },
{
label: 'mtime',
description: 'filter by modification time',
},
+ { label: 'sha1', description: 'filter by SHA1 checksum' },
+ {
+ label: 'sha256',
+ description: 'filter by SHA256 checksum',
+ },
{ label: 'size', description: 'filter by size' },
{ label: 'uname', description: 'filter by user name' },
{ label: 'uid', description: 'filter by user id' },
@@ -182,7 +229,7 @@ export const InventoryTable = withRouterSearch(
try {
const response = await WzRequest.apiReq(
'GET',
- `/syscheck/${this.props.agent.id}`,
+ APIendpoint,
{
params: {
distinct: true,
@@ -191,12 +238,9 @@ export const InventoryTable = withRouterSearch(
sort: `+${field}`,
...(currentValue
? {
- // Add the implicit query
- q: `${searchBarWQLOptions.implicitQuery.query}${searchBarWQLOptions.implicitQuery.conjunction}${field}~${currentValue}`,
+ q: `${field}~${currentValue}`,
}
- : {
- q: `${searchBarWQLOptions.implicitQuery.query}`,
- }),
+ : {}),
},
},
);
@@ -223,11 +267,16 @@ export const InventoryTable = withRouterSearch(
},
},
}}
- filters={searchBarWQLFilters}
+ filters={this.state.filters}
showReload
downloadCsv={`fim-files-${this.props.agent.id}`}
searchTable={true}
rowProps={getRowProps}
+ saveStateStorage={{
+ system: 'localStorage',
+ key: 'wz-fim-files-table',
+ }}
+ showFieldSelector
/>
@@ -251,6 +300,7 @@ export const InventoryTable = withRouterSearch(
view='inventory'
showViewInEvents={true}
{...this.props}
+ onFiltersChange={this.onFiltersChange}
/>
)}
>