diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f34027a08..43221f848b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@ 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 an "Agents management" menu and moved the sections: "Endpoint Groups" and "Endpoint Summary" which changed its name to "Summary".[#7112](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7112)
+- 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
@@ -27,19 +30,23 @@ All notable changes to the Wazuh app project will be documented in this file.
- Fixed read-only users could not access to Statistics application [#7001](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7001)
- Fixed no-agent-alert spawn with selected agent in agent-welcome view [#7029](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7029)
+- Fixed loading state of the agents status chart in the home overview [#7120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7120)
- Fixed security policy exception when it contained deprecated actions [#7042](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7042)
+- Fixed border on cells in events that disappear when clicked [#7075](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7075)
- Fixed export formatted csv data with special characters from tables [#7048](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7048)
- Fixed column reordering feature [#7072](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7072)
- Fixed filter management to prevent hiding when adding multiple filters [#7077](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7077)
- Fixed agent view Mitre ATT&CK exception [#7116](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7116)
- 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
- Removed agent RBAC filters from dashboard queries [#6945](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6945)
- Removed GET /elastic/statistics API endpoint [#7001](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7001)
- Removed VirusTotal application in favor of Malware Detection [#7038](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7038)
+- Removed processes state column in macOS agents [#7122](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7122)
## Wazuh v4.9.1 - OpenSearch Dashboards 2.13.0 - Revision 04
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}
/>
)}
>
diff --git a/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap b/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap
index d048bbcd49..92dad0b43e 100644
--- a/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap
+++ b/plugins/main/public/components/agents/syscollector/__snapshots__/inventory.test.tsx.snap
@@ -2085,31 +2085,6 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
-
@@ -2118,7 +2093,7 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
>
({
+ ...rest,
+ field,
+ name: rest.name || KeyEquivalence[field] || field,
+});
const windowsColumns = [
{ field: 'name', searchable: true, sortable: true, width: '10%' },
@@ -8,7 +14,8 @@ const windowsColumns = [
{ field: 'priority', searchable: true, sortable: true },
{ field: 'nlwp', searchable: true, sortable: true },
{ field: 'cmd', searchable: true, sortable: true, width: '30%' },
-].map(({field, ...rest}) => ({...rest, field, name: rest.name || KeyEquivalence[field] || field}));
+].map(mapColumns);
+
const linuxColumns = [
{ field: 'name', searchable: true, sortable: true, width: '10%' },
{ field: 'euser', searchable: true, sortable: true },
@@ -22,7 +29,8 @@ const linuxColumns = [
{ field: 'session', searchable: true, sortable: true },
{ field: 'nice', searchable: true, sortable: true },
{ field: 'state', searchable: true, sortable: true, width: '15%' },
-].map(({field, ...rest}) => ({...rest, field, name: rest.name || KeyEquivalence[field] || field}));
+].map(mapColumns);
+
const macColumns = [
{ field: 'name', searchable: true, sortable: true, width: '10%' },
{ field: 'euser', searchable: true, sortable: true },
@@ -30,8 +38,7 @@ const macColumns = [
{ field: 'ppid', searchable: true, sortable: true },
{ field: 'vm_size', searchable: true, sortable: true },
{ field: 'nice', searchable: true, sortable: true },
- { field: 'state', searchable: true, sortable: true, width: '15%' },
-].map(({field, ...rest}) => ({...rest, field, name: rest.name || KeyEquivalence[field] || field}));
+].map(mapColumns);
export const processColumns = {
windows: windowsColumns,
diff --git a/plugins/main/public/components/overview/overview.tsx b/plugins/main/public/components/overview/overview.tsx
index de7a53e7de..91e9c97528 100644
--- a/plugins/main/public/components/overview/overview.tsx
+++ b/plugins/main/public/components/overview/overview.tsx
@@ -40,6 +40,7 @@ export const Overview: React.FC = withRouteResolvers({
savedSearch,
})(() => {
const [agentsCounts, setAgentsCounts] = useState |