From e14d6f32ae061260dc61e1dbc74a1d7b39bdab90 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Mon, 8 Mar 2021 18:03:34 -0300 Subject: [PATCH 01/10] draft CVEs table view --- package.json | 2 +- public/components/agents/vuls/index.ts | 14 + public/components/agents/vuls/inventory.tsx | 307 +++++++++++ .../agents/vuls/inventory/fileDetail.tsx | 505 ++++++++++++++++++ .../agents/vuls/inventory/filterBar.tsx | 67 +++ .../agents/vuls/inventory/flyout.tsx | 140 +++++ .../components/agents/vuls/inventory/index.ts | 3 + .../vuls/inventory/lib/getFilterValues.ts | 27 + .../agents/vuls/inventory/lib/index.ts | 1 + .../agents/vuls/inventory/table.tsx | 270 ++++++++++ public/components/agents/vuls/main.tsx | 44 ++ public/components/agents/vuls/vuls-mockup.js | 229 ++++++++ .../common/modules/main-overview.tsx | 3 + .../common/modules/modules-defaults.js | 5 + 14 files changed, 1616 insertions(+), 1 deletion(-) create mode 100644 public/components/agents/vuls/index.ts create mode 100644 public/components/agents/vuls/inventory.tsx create mode 100644 public/components/agents/vuls/inventory/fileDetail.tsx create mode 100644 public/components/agents/vuls/inventory/filterBar.tsx create mode 100644 public/components/agents/vuls/inventory/flyout.tsx create mode 100644 public/components/agents/vuls/inventory/index.ts create mode 100644 public/components/agents/vuls/inventory/lib/getFilterValues.ts create mode 100644 public/components/agents/vuls/inventory/lib/index.ts create mode 100644 public/components/agents/vuls/inventory/table.tsx create mode 100644 public/components/agents/vuls/main.tsx create mode 100644 public/components/agents/vuls/vuls-mockup.js diff --git a/package.json b/package.json index 20c0b2c2d2..6586644824 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wazuh", - "version": "4.1.1", + "version": "4.2.0", "revision": "4103", "code": "4103-0", "kibana": { diff --git a/public/components/agents/vuls/index.ts b/public/components/agents/vuls/index.ts new file mode 100644 index 0000000000..4a0a2682ad --- /dev/null +++ b/public/components/agents/vuls/index.ts @@ -0,0 +1,14 @@ +/* + * Wazuh app - Integrity monitoring components + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +export { MainVuls } from './main'; +export { Inventory } from './inventory'; diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx new file mode 100644 index 0000000000..0338224920 --- /dev/null +++ b/public/components/agents/vuls/inventory.tsx @@ -0,0 +1,307 @@ +/* + * Wazuh app - Integrity monitoring components + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import React, { Component, Fragment } from 'react'; +import { + EuiPanel, + EuiPage, + EuiTabs, + EuiTab, + EuiTitle, + EuiLoadingSpinner, + EuiEmptyPrompt, + EuiSpacer, + EuiProgress, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiHorizontalRule, + EuiButtonEmpty +} from '@elastic/eui'; +import { + InventoryTable, + FilterBar +} from './inventory/'; +import { WzRequest } from '../../../react-services/wz-request'; +import exportCsv from '../../../react-services/wz-csv'; +import { getToasts } from '../../../kibana-services'; +import { ICustomBadges } from '../../wz-search-bar/components'; +import { filtersToObject } from '../../wz-search-bar'; +import { WzEmptyPromptNoPermissions } from "../../common/permissions/prompt"; + +import mockup from './vuls-mockup'; + +export class Inventory extends Component { + _isMount = false; + state: { + filters: []; + // selectedTabId: 'files' | 'registry'; + totalItemsFile: number; + totalItemsRegistry: number; + isLoading: Boolean; + syscheck: []; + customBadges: ICustomBadges[]; + isConfigured: Boolean; + }; + + props: any; + + constructor(props) { + super(props); + this.state = { + filters: [], + syscheck: [], + // selectedTabId: 'files', + totalItemsFile: 0, + totalItemsRegistry: 0, + isLoading: true, + customBadges: [], + isConfigured: false + } + this.onFiltersChange.bind(this); + } + + async componentDidMount() { + this._isMount = true; + await this.loadAgent(); + } + + componentDidUpdate(prevProps) { + if (JSON.stringify(this.props.agent) !== JSON.stringify(prevProps.agent)){ + this.setState({isLoading: true}, this.loadAgent) + } + } + + componentWillUnmount() { + this._isMount = false; + } + + async loadAgent() { + const agentPlatform = ((this.props.agent || {}).os || {}).platform; + const {totalItemsFile, syscheck} = await this.getItemNumber('file'); + const totalItemsRegistry = agentPlatform === 'windows' ? await this.getItemNumber('registry') : 0; + const isConfigured = await this.isConfigured(); + if (this._isMount){ + this.setState({ totalItemsFile, totalItemsRegistry, syscheck, isLoading: false, isConfigured }); + } + } + + // Do not load the localStorage filters when changing tabs + // componentDidUpdate(prevProps, prevState) { + // const { selectedTabId } = this.state; + // if (selectedTabId !== prevState.selectedTabId) { + // const filters = this.getStoreFilters(this.props); + // this.setState({ filters }); + // } + // } + + // tabs() { + // let auxTabs = [ + // { + // id: 'vulnerabilities', + // name: `Vulnerabilities ${this.state.isLoading === true ? '' : '(' + this.state.totalItemsFile + ')'}`, + // disabled: false, + // }, + // ]; + // // const platform = (this.props.agent.os || {}).platform || "other"; + // // platform === 'windows' ? auxTabs.push( + // // { + // // id: 'registry', + // // name: `Windows Registry ${this.state.isLoading === true ? '' : '(' + this.state.totalItemsRegistry + ')'}`, + // // disabled: false, + // // }, + // // ) : null; + // return (auxTabs); + // } + + getStoreFilters(props) { + const { section, selectView, agent } = props; + const filters = JSON.parse(window.localStorage.getItem(`wazuh-${section}-${selectView}-vulnerabilities-${agent['id']}`) || '{}'); + return filters; + } + + setStoreFilters(filters) { + const { section, selectView, agent } = this.props; + window.localStorage.setItem(`wazuh-${section}-${selectView}-vulnerabilities-${agent['id']}`, JSON.stringify(filters)) + } + + onFiltersChange = (filters) => { + // this.setStoreFilters(filters); + this.setState({ filters }); + } + + onTotalItemsChange = (totalItems: number) => { + this.setState({ totalItemsFile: totalItems }); + } + + // onSelectedTabChanged = id => { + // this.setState({ selectedTabId: id }); + // } + + buildFilter() { + const filters = filtersToObject(this.state.filters); + const filter = { + ...filters, + limit: '15' + }; + return filter; + } + + async getItemNumber() { + try { + const agentID = this.props.agent.id; + // const response = await WzRequest.apiReq('GET', `/syscheck/${agentID}`, { + // params: this.buildFilter(), + // }); + const response = mockup; + // if (type === 'file') { + return { + totalItemsFile: ((response.data || {}).data || {}).total_affected_items || 0, + syscheck: ((response.data || {}).data || {}).affected_items || [], + }; + // } + // return ((response.data || {}).data || {}).total_affected_items || 0; + } catch (error) { + this.setState({ isLoading: false }); + this.showToast('danger', error, 3000); + } + } + + renderTabs() { + // const tabs = this.tabs(); + const { isLoading } = this.state; + + return ( + + + +

Vulnerabilities {isLoading === true && }

+
+
+ + this.downloadCsv()}> + Export formatted + + +
+ ) + } + + showToast = (color, title, time) => { + getToasts().add({ + color: color, + title: title, + toastLifeTimeMs: time, + }); + }; + + async downloadCsv() { + const { filters } = this.state; + try { + const filtersObject = filtersToObject(filters); + const formatedFilters = Object.keys(filtersObject).map(key => ({name: key, value: filtersObject[key]})); + this.showToast('success', 'Your download should begin automatically...', 3000); + await exportCsv( + '/syscheck/' + this.props.agent.id, + [ + { name: 'type', value: 'vulnerabilities' }, + ...formatedFilters + ], + `vuls-vulnerabilities` + ); + } catch (error) { + this.showToast('danger', error, 3000); + } + } + + renderTable() { + const { filters, syscheck, totalItemsFile } = this.state; + return ( +
+ + +
+ ) + } + + noConfigured() { + return ( + Vulnerabilities is not configured for this agent} + body={ + + + https://documentation.wazuh.com/current/user-manual/capabilities/file-integrity/index.html + + + } + />); + } + + loadingInventory() { + return + + + + + + ; + } + + async isConfigured() { + try { + const response = await WzRequest.apiReq( + 'GET', + `/agents/${this.props.agent.id}/config/syscheck/syscheck`, + {} + ); + + return (((response.data || {}).data).syscheck || {}).disabled === 'no'; + } catch (error) { + return false; + } + } + + render() { + const { isLoading, isConfigured } = this.state; + if (isLoading) { + return this.loadingInventory() + } + const table = this.renderTable(); + const tabs = this.renderTabs(); + + return isConfigured + ? ( + + {tabs} + + {table} + + ) + : this.noConfigured() + } +} diff --git a/public/components/agents/vuls/inventory/fileDetail.tsx b/public/components/agents/vuls/inventory/fileDetail.tsx new file mode 100644 index 0000000000..bc12bbc4c0 --- /dev/null +++ b/public/components/agents/vuls/inventory/fileDetail.tsx @@ -0,0 +1,505 @@ +/* + * Wazuh app - Integrity monitoring table component + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import React, { Component, Fragment } from 'react'; +import { + EuiAccordion, + EuiFlexGrid, + EuiFlexItem, + EuiFlexGroup, + EuiTitle, + EuiButtonIcon, + EuiIcon, + EuiSpacer, + EuiStat, + EuiToolTip, + EuiBadge, +} from '@elastic/eui'; +import { Discover } from '../../../common/modules/discover'; +import { ModulesHelper } from '../../../common/modules/modules-helper'; +import { ICustomBadges } from '../../../wz-search-bar/components'; +import { buildPhraseFilter, IIndexPattern } from '../../../../../../../src/plugins/data/common'; +import { getIndexPattern } from '../../../overview/mitre/lib'; +import moment from 'moment-timezone'; +import { AppNavigate } from '../../../../react-services/app-navigate'; +import { TruncateHorizontalComponents } from '../../../common/util'; +import { getDataPlugin,getUiSettings } from '../../../../kibana-services'; +// import { RegistryValues } from './registryValues'; +import { TimeService } from '../../../../react-services/time-service'; +import { FilterManager } from '../../../../../../../src/plugins/data/public/'; + +export class FileDetails extends Component { + props!: { + currentFile: { + file: string; + type: string; + }; + implicitFilters: Array; + loadEventsWithFilters: Function; + [key: string]: any; + }; + userSvg = ( + + ); + indexPattern!: IIndexPattern; + discoverFilterManager: FilterManager; + + constructor(props) { + super(props); + + this.state = { + hoverAddFilter: '', + totalHits: 0, + }; + this.viewInEvents.bind(this); + + this.discoverFilterManager = new FilterManager(getUiSettings()); + } + + componentDidMount() { + getIndexPattern().then((idxPtn) => (this.indexPattern = idxPtn)); + } + + details() { + return [ + { + field: 'date', + name: 'Last analysis', + grow: 2, + icon: 'clock', + link: true, + transformValue: TimeService.offset + }, + { + field: 'mtime', + name: 'Last modified', + grow: 2, + icon: 'clock', + link: true, + transformValue: TimeService.offset + }, + { + field: 'uname', + name: 'User', + icon: 'user', + link: true, + }, + { + field: 'uid', + name: 'User ID', + icon: 'user', + link: true, + }, + { + field: 'gname', + name: 'Group', + icon: 'usersRolesApp', + onlyLinux: true, + link: true, + }, + { + field: 'gid', + name: 'Group ID', + onlyLinux: true, + icon: 'usersRolesApp', + link: true, + }, + { + field: 'perm', + name: 'Permissions', + icon: 'lock', + link: false, + transformValue: (value) => this.renderFileDetailsPermissions(value), + }, + { + field: 'size', + name: 'Size', + icon: 'nested', + link: true, + transformValue: (value) => this.renderFileDetailsSize(value), + }, + { + field: 'inode', + name: 'Inode', + icon: 'link', + onlyLinux: true, + link: true, + }, + { + field: 'md5', + name: 'MD5', + checksum: true, + icon: 'check', + link: true, + }, + { + field: 'sha1', + name: 'SHA1', + checksum: true, + icon: 'check', + link: true, + }, + { + field: 'sha256', + name: 'SHA256', + checksum: true, + icon: 'check', + link: true, + }, + ]; + } + + registryDetails() { + return [ + { + field: 'date', + name: 'Last analysis', + grow: 2, + icon: 'clock', + transformValue: TimeService.offset + }, + { + field: 'mtime', + name: 'Last modified', + grow: 2, + icon: 'clock', + }, + { + field: 'sha1', + name: 'SHA1', + checksum: true, + icon: 'check', + }, + ]; + } + + viewInEvents = (ev) => { + const { file } = this.props.currentFile; + if (this.props.view === 'extern') { + AppNavigate.navigateToModule(ev, 'overview', { + tab: 'fim', + tabView: 'events', + filters: { 'syscheck.path': file }, + }); + } else { + AppNavigate.navigateToModule( + ev, + 'overview', + { tab: 'fim', tabView: 'events', filters: { 'syscheck.path': file } }, + () => this.openEventCurrentWindow() + ); + } + }; + + openEventCurrentWindow() { + const { file } = this.props.currentFile; + const filters = [ + { + ...buildPhraseFilter({ name: 'syscheck.path', type: 'text' }, file, this.indexPattern), + $state: { store: 'appState' }, + }, + ]; + + this.props.onSelectedTabChanged('events'); + this.checkFilterManager(filters); + } + + async checkFilterManager(filters) { + const { filterManager } = getDataPlugin().query; + const _filters = filterManager.getFilters(); + if (_filters && _filters.length) { + const syscheckPathFilters = _filters.filter((x) => { + return x.meta.key === 'syscheck.path'; + }); + syscheckPathFilters.map((x) => { + filterManager.removeFilter(x); + }); + filterManager.addFilters([filters]); + const scope = await ModulesHelper.getDiscoverScope(); + scope.updateQueryAndFetch({ query: null }); + } else { + setTimeout(() => { + this.checkFilterManager(filters); + }, 200); + } + } + + addFilter(field, value) { + const { filters, onFiltersChange } = this.props; + const newBadge: ICustomBadges = { field: 'q', value: '' }; + if (field === 'date' || field === 'mtime') { + let value_max = moment(value).add(1, 'day'); + newBadge.value = `${field}>${moment(value).format( + 'YYYY-MM-DD' + )} AND ${field}<${value_max.format('YYYY-MM-DD')}`; + } else { + newBadge.value = `${field}=${field === 'size' ? this.props.currentFile[field] : value}`; + } + !filters.some((item) => item.field === newBadge.field && item.value === newBadge.value) && + onFiltersChange([...filters, newBadge]); + this.props.closeFlyout(); + } + + getDetails() { + const { view } = this.props; + const columns = this.props.type === 'registry_key' || this.props.currentFile.type === 'registry_key' ? this.registryDetails() : this.details(); + const generalDetails = columns.map((item, idx) => { + var value = this.props.currentFile[item.field] || '-'; + if (item.transformValue) { + value = item.transformValue(value, this.props); + } + var link = (item.link && !['events', 'extern'].includes(view)) || false; + const agentPlatform = ((this.props.agent || {}).os || {}).platform; + if (!item.onlyLinux || (item.onlyLinux && this.props.agent && agentPlatform !== 'windows')) { + let className = item.checksum ? 'detail-value detail-value-checksum' : 'detail-value'; + className += item.field === 'perm' ? ' detail-value-perm' : ''; + return ( + + {value} + ) : ( + { + this.setState({ hoverAddFilter: item }); + }} + onMouseLeave={() => { + this.setState({ hoverAddFilter: '' }); + }} + > + {value} + {_.isEqual(this.state.hoverAddFilter, item) && ( + + { + this.addFilter(item.field, value); + }} + iconType="magnifyWithPlus" + aria-label="Next" + iconSize="s" + className="buttonAddFilter" + /> + + )} + + ) + } + description={ + + {item.icon !== 'users' ? ( + + ) : ( + this.userSvg + )} + {item.name} + + } + textAlign="left" + titleSize="xs" + /> + + ); + } + }); + + return ( +
+ {generalDetails} +
+ ); + } + + updateTotalHits = (total) => { + this.setState({ totalHits: total }); + }; + + renderFileDetailsPermissions(value) { + if (((this.props.agent || {}).os || {}).platform === 'windows' && value && value !== '-') { + const components = value + .split(', ') + .map((userNameAndPermissionsFullString) => { + const [_, username, userPermissionsString] = userNameAndPermissionsFullString.match( + /(\S+) \(allowed\): (\S+)/ + ); + const permissions = userPermissionsString.split('|').sort(); + return { username, permissions }; + }) + .sort((a, b) => { + if (a.username > b.username) { + return 1; + } else if (a.username < b.username) { + return -1; + } else { + return 0; + } + }) + .map(({ username, permissions }) => { + return ( + + + {username} + + + ); + }); + return ( + `+${count} users`} + buttonProps={{ size: 'xs' }} + componentsWidthPercentage={0.85} + /> + ); + } + return value; + } + + renderFileDetailsSize(value) { + if (isNaN(value)) { + return 0; + } + const b = 2; + if (0 === value) return '0 Bytes'; + const c = 0 > b ? 0 : b, + d = Math.floor(Math.log(value) / Math.log(1024)); + return ( + parseFloat((value / Math.pow(1024, d)).toFixed(c)) + + ' ' + + ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][d] + ); + } + + render() { + const { fileName, type, implicitFilters, view, currentFile, agent } = this.props; + const inspectButtonText = view === 'extern' ? 'Inspect in FIM' : 'Inspect in Events'; + return ( + + +

Details

+ + } + paddingSize="none" + initialIsOpen={true} + > +
{this.getDetails()}
+
+ { (type === 'registry_key' || currentFile.type === 'registry_key') && <> + + +

+ Registry values +

+ + } + paddingSize="none" + initialIsOpen={true} + > + + + {/* */} + + +
} + + + {this.state.totalHits || 0} hits + + } + buttonContent={ + +

+ Recent events + {view !== 'events' && ( + + + this.viewInEvents(ev)} + type="popout" + aria-label={inspectButtonText} + /> + + + )} +

+
+ } + paddingSize="none" + initialIsOpen={true} + > + + + this.updateTotalHits(total)} + /> + + +
+
+ ); + } +} diff --git a/public/components/agents/vuls/inventory/filterBar.tsx b/public/components/agents/vuls/inventory/filterBar.tsx new file mode 100644 index 0000000000..176bc34869 --- /dev/null +++ b/public/components/agents/vuls/inventory/filterBar.tsx @@ -0,0 +1,67 @@ +/* + * Wazuh app - Integrity monitoring components + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ +import React, { Component } from 'react'; +import { getFilterValues } from './lib'; +import { WzSearchBar, IFilter, IWzSuggestItem } from '../../../../components/wz-search-bar' +import { ICustomBadges } from '../../../wz-search-bar/components'; +import { + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import moment from 'moment'; + +export class FilterBar extends Component { + formatDate = (date: String) => { + return moment(date).format('YYYY-MM-DD'); + } + // TODO: Change the type + suggestions: IWzSuggestItem[] = [ + {type: 'q', label: 'name', description:"Name of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('file', value, this.props.agent.id, {type:'file'})}, + ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'perm', description:"Permisions of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('perm', value, this.props.agent.id)}]: []), + {type: 'q', label: 'mtime', description:"Date the file was modified", operators:['=','!=', '>', '<'], values: async (value) => getFilterValues('mtime', value, this.props.agent.id,{}, this.formatDate)}, + {type: 'q', label: 'date', description:"Date of registration of the event", operators:['=','!=', '>', '<'], values: async (value) => getFilterValues('date', value, this.props.agent.id, {}, this.formatDate)}, + {type: 'q', label: 'uname', description:"Owner of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('uname', value, this.props.agent.id)}, + {type: 'q', label: 'uid', description:"Id of the onwner file", operators:['=','!=', '~'], values: async (value) => getFilterValues('uid', value, this.props.agent.id)}, + ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'gname', description:"Name of the group owner file", operators:['=','!=', '~'], values: async (value) => getFilterValues('gname', value, this.props.agent.id)}]: []), + ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'gid', description:"Id of the group owner", operators:['=','!=', '~'], values: async (value) => getFilterValues('gid', value, this.props.agent.id)}]: []), + {type: 'q', label: 'md5', description:"md5 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('md5', value, this.props.agent.id)}, + {type: 'q', label: 'sha1', description:"sha1 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('sha1', value, this.props.agent.id)}, + {type: 'q', label: 'sha256', description:"sha256 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('sha256', value, this.props.agent.id)}, + ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'inode', description:"Inode of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('inode', value, this.props.agent.id)}]: []), + {type: 'q', label: 'size', description:"Size of the file in Bytes", values: async (value) => getFilterValues('size', value, this.props.agent.id)}, + ] + + props!:{ + onFiltersChange(filters:IFilter[]): void + selectView: 'files' | 'registry' + agent: {id: string, agentPlatform: string} + onChangeCustomBadges?(customBadges: ICustomBadges[]): void + customBadges?: ICustomBadges[] + filters: IFilter[] + } + + render() { + const { onFiltersChange, selectView, filters} = this.props; + return ( + + + + + + ) + } +} \ No newline at end of file diff --git a/public/components/agents/vuls/inventory/flyout.tsx b/public/components/agents/vuls/inventory/flyout.tsx new file mode 100644 index 0000000000..79a1ea2252 --- /dev/null +++ b/public/components/agents/vuls/inventory/flyout.tsx @@ -0,0 +1,140 @@ +/* + * Wazuh app - Integrity monitoring components + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import React, { Component, Fragment } from 'react'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTitle, + EuiLoadingContent, + EuiCallOut, +} from '@elastic/eui'; +import { WzRequest } from '../../../../react-services/wz-request'; +import { FileDetails } from './fileDetail'; +import { AppState } from '../../../../react-services/app-state'; + +export class FlyoutDetail extends Component { + state: { + currentFile: boolean | { [key: string]: string }; + clusterFilter: {}; + isLoading: boolean; + error: boolean; + type: 'file' | 'registry_key'; + }; + + props!: { + fileName: string; + agentId: string; + view: 'inventory' | 'events' | 'extern'; + closeFlyout(): void; + }; + + constructor(props) { + super(props); + this.state = { + currentFile: false, + clusterFilter: {}, + isLoading: true, + error: false, + type: 'file', + }; + } + + async componentDidMount() { + try { + const isCluster = (AppState.getClusterInfo() || {}).status === 'enabled'; + const clusterFilter = isCluster + ? { 'cluster.name': AppState.getClusterInfo().cluster } + : { 'manager.name': AppState.getClusterInfo().manager }; + this.setState({ clusterFilter }); + let currentFile; + if (typeof this.props.item === 'boolean' && typeof this.props.fileName !== undefined) { + const regex = new RegExp('file=' + '[^&]*'); + const match = window.location.href.match(regex); + if (match && match[0]) { + let file = decodeURIComponent(match[0].split('=')[1]); + const data = await WzRequest.apiReq('GET', `/syscheck/${this.props.agentId}`, { + params: { + q: `file=${file};(type=file,type=registry_key)`, + }, + }); + currentFile = ((((data || {}).data || {}).data || {}).affected_items || [])[0]; + } + } else if (this.props.item) { + currentFile = this.props.item; + } else { + let file = this.props.fileName; + const data = await WzRequest.apiReq('GET', `/syscheck/${this.props.agentId}`, { + params: { + q: `file=${file};(type=file,type=registry_key)`, + }, + }); + currentFile = ((((data || {}).data || {}).data || {}).affected_items || [])[0]; + } + if (!currentFile) { + throw false; + } + this.setState({ currentFile, type: currentFile.type, isLoading: false }); + } catch (err) { + this.setState({ + error: `Data could not be fetched for ${this.props.fileName}`, + currentFile: { file: this.props.fileName }, + }); + } + } + + componentWillUnmount() { + window.location.href = window.location.href.replace(new RegExp('&file=' + '[^&]*', 'g'), ''); + } + + render() { + const { type } = this.state; + return ( + this.props.closeFlyout()} + size="l" + aria-labelledby={this.state.currentFile.file} + maxWidth="70%" + className="wz-inventory wzApp" + > + + +

{this.state.currentFile.file}

+
+
+ {this.state.isLoading && ( + + {(this.state.error && ( + + )) || } + + )} + {this.state.currentFile && !this.state.isLoading && ( + + + + )} +
+ ); + } +} diff --git a/public/components/agents/vuls/inventory/index.ts b/public/components/agents/vuls/inventory/index.ts new file mode 100644 index 0000000000..1238860c71 --- /dev/null +++ b/public/components/agents/vuls/inventory/index.ts @@ -0,0 +1,3 @@ +export { FilterBar } from './filterBar'; + +export { InventoryTable } from './table'; \ No newline at end of file diff --git a/public/components/agents/vuls/inventory/lib/getFilterValues.ts b/public/components/agents/vuls/inventory/lib/getFilterValues.ts new file mode 100644 index 0000000000..6c7325feb9 --- /dev/null +++ b/public/components/agents/vuls/inventory/lib/getFilterValues.ts @@ -0,0 +1,27 @@ +/* + * Wazuh app - Integrity monitoring components + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ +import { WzRequest } from '../../../../../react-services/wz-request'; + +export async function getFilterValues(field, value, agentId, filters={}, format=(item) => item) { + + const filter = { + ...filters, + distinct: true, + select: field, + limit: 30, + }; + if (value) { + filter['search'] = value; + } + const result = await WzRequest.apiReq('GET', `/syscheck/${agentId}`, { params: filter }); + return (((result || {}).data || {}).data || {}).affected_items.map((item) => {return format(item[field])}); +} \ No newline at end of file diff --git a/public/components/agents/vuls/inventory/lib/index.ts b/public/components/agents/vuls/inventory/lib/index.ts new file mode 100644 index 0000000000..7427803104 --- /dev/null +++ b/public/components/agents/vuls/inventory/lib/index.ts @@ -0,0 +1 @@ +export { getFilterValues } from './getFilterValues'; \ No newline at end of file diff --git a/public/components/agents/vuls/inventory/table.tsx b/public/components/agents/vuls/inventory/table.tsx new file mode 100644 index 0000000000..dc054175de --- /dev/null +++ b/public/components/agents/vuls/inventory/table.tsx @@ -0,0 +1,270 @@ +/* + * Wazuh app - Integrity monitoring table component + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import React, { Component } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiBasicTable, + Direction, + EuiOverlayMask, +} from '@elastic/eui'; +import { WzRequest } from '../../../../react-services/wz-request'; +import { FlyoutDetail } from './flyout'; +import { filtersToObject, IFilter } from '../../../wz-search-bar'; +// import { TimeService } from '../../../../react-services/time-service'; + +export class InventoryTable extends Component { + state: { + syscheck: [] + error?: string + pageIndex: number + pageSize: number + totalItems: number + sortField: string + isFlyoutVisible: Boolean + sortDirection: Direction + isLoading: boolean + currentItem: { + vul: string + }, + syscheckItem: {} + }; + + props!: { + filters: IFilter[] + agent: any + items: [] + totalItems: number, + onTotalItemsChange: Function + } + + constructor(props) { + super(props); + + this.state = { + syscheck: props.items, + pageIndex: 0, + pageSize: 15, + totalItems: 0, + sortField: 'file', + sortDirection: 'asc', + isLoading: false, + isFlyoutVisible: false, + currentItem: { + vul: "" + }, + syscheckItem: {} + } + } + + async componentDidMount() { + const regex = new RegExp('file=' + '[^&]*'); + const match = window.location.href.match(regex); + this.setState({totalItems: this.props.totalItems}); + if (match && match[0]) { + const file = match[0].split('=')[1]; + this.showFlyout(decodeURIComponent(file), true); + } + } + + closeFlyout() { + this.setState({ isFlyoutVisible: false, currentItem: {} }); + } + + async showFlyout(file, item, redirect = false) { + + let fileData = false; + if (!redirect) { + fileData = this.state.syscheck.filter(item => { + return item.file === file; + }) + } else { + const response = await WzRequest.apiReq('GET', `/syscheck/${this.props.agent.id}`, { + params: { + 'file': file + } + }); + fileData = ((response.data || {}).data || {}).affected_items || []; + } + if (!redirect) + window.location.href = window.location.href += `&file=${file}`; + //if a flyout is opened, we close it and open a new one, so the components are correctly updated on start. + this.setState({ isFlyoutVisible: false }, () => this.setState({ isFlyoutVisible: true, currentItem: file, syscheckItem: item })); + } + + async componentDidUpdate(prevProps) { + const { filters } = this.props; + if (JSON.stringify(filters) !== JSON.stringify(prevProps.filters)) { + this.setState({ pageIndex: 0, isLoading: true }, this.getSyscheck); + } + } + + async getSyscheck() { + const agentID = this.props.agent.id; + try { + const syscheck = await WzRequest.apiReq( + 'GET', + `/syscheck/${agentID}`, + { params: this.buildFilter()}, + ); + + this.props.onTotalItemsChange((((syscheck || {}).data || {}).data || {}).total_affected_items); + + this.setState({ + syscheck: (((syscheck || {}).data || {}).data || {}).affected_items || {}, + totalItems: (((syscheck || {}).data || {}).data || {}).total_affected_items - 1, + isLoading: false, + error: undefined + }); + } catch (error) { + this.setState({error, isLoading: false}) + } +} + + buildSortFilter() { + const { sortField, sortDirection } = this.state; + + const field = (sortField === 'os_name') ? '' : sortField; + const direction = (sortDirection === 'asc') ? '+' : '-'; + + return direction + field; + } + + buildFilter() { + const { pageIndex, pageSize } = this.state; + const filters = filtersToObject(this.props.filters); + const filter = { + ...filters, + offset: pageIndex * pageSize, + limit: pageSize, + sort: this.buildSortFilter(), + type: 'file' + }; + return filter; + } + + onTableChange = ({ page = {}, sort = {} }) => { + const { index: pageIndex, size: pageSize } = page; + const { field: sortField, direction: sortDirection } = sort; + this.setState({ + pageIndex, + pageSize, + sortField, + sortDirection, + isLoading: true, + }, + () => this.getSyscheck() + ); + }; + + columns() { + let width; + (((this.props.agent || {}).os || {}).platform || false) === 'windows' ? width = '60px' : width = '80px'; + return [ + { + field: 'name', + name: 'Name', + sortable: true, + width: '100px' + }, + { + field: 'cve', + name: 'CVE', + sortable: true, + truncateText: true, + width: `${width}` + }, + { + field: 'architecture', + name: 'Architecture', + sortable: true, + width: '100px' + }, + { + field: 'version', + name: 'Version', + sortable: true, + truncateText: true, + width: `${width}` + } + ] + } + + renderTable() { + const getRowProps = item => { + const { itemDetails } = item; + return { + 'data-test-subj': `row-${itemDetails}`, + onClick: () => this.showFlyout(itemDetails, item), + }; + }; + + const { syscheck, pageIndex, pageSize, totalItems, sortField, sortDirection, isLoading, error } = this.state; + const columns = this.columns(); + const pagination = { + pageIndex: pageIndex, + pageSize: pageSize, + totalItemCount: totalItems, + pageSizeOptions: [15, 25, 50, 100], + } + const sorting = { + sort: { + field: sortField, + direction: sortDirection, + }, + }; + + return ( + + + + + + ); + } + + render() { + const table = this.renderTable(); + return ( +
+ {table} + {this.state.isFlyoutVisible && + this.closeFlyout() } > + this.closeFlyout()} + type='vulnerability' + view='inventory' + showViewInEvents={true} + {...this.props} /> + + } +
+ ) + } +} diff --git a/public/components/agents/vuls/main.tsx b/public/components/agents/vuls/main.tsx new file mode 100644 index 0000000000..c33a5d95fb --- /dev/null +++ b/public/components/agents/vuls/main.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Inventory } from './index'; +import '../../common/modules/module.scss'; +import { connect } from 'react-redux'; +import { PromptNoSelectedAgent, PromptNoActiveAgent } from '../prompts'; +import { compose } from 'redux'; +import { withGuard, withUserAuthorizationPrompt } from '../../common/hocs'; + +const mapStateToProps = (state) => ({ + currentAgentData: state.appStateReducers.currentAgentData, +}); + +export const MainVuls = compose( + connect(mapStateToProps), + withGuard( + (props) => !((props.currentAgentData && props.currentAgentData.id) && props.agent), + () => ( + + ) + ), + withGuard( + (props) => { + const agentData = + props.currentAgentData && props.currentAgentData.id ? props.currentAgentData : props.agent; + return agentData.status !== 'active'; + }, + () => + ), + withUserAuthorizationPrompt((props) => { + const agentData = + props.currentAgentData && props.currentAgentData.id ? props.currentAgentData : props.agent; + return [ + { action: 'agent:read', resource: `agent:id:${agentData.id}` }, + { action: 'syscheck:read', resource: `agent:id:${agentData.id}` }, + ]; + }) +)(function MainVuls({ currentAgentData, agent, ...rest }) { + const agentData = currentAgentData && currentAgentData.id ? currentAgentData : agent; + return ( +
+ +
+ ); +}); diff --git a/public/components/agents/vuls/vuls-mockup.js b/public/components/agents/vuls/vuls-mockup.js new file mode 100644 index 0000000000..3bac0943aa --- /dev/null +++ b/public/components/agents/vuls/vuls-mockup.js @@ -0,0 +1,229 @@ +export default { + data: { + data: { + affected_items: [ + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29361', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29362', + }, + { + version: '0.23.9-2', + name: 'libp11-kit0', + architecture: 'amd64', + cve: 'CVE-2020-29363', + } + ], + total_affected_items: 18, + total_failed_items: 0, + failed_items: [], + }, + message: 'All selected vulnerabilities were returned', + error: 0, + }, +}; diff --git a/public/components/common/modules/main-overview.tsx b/public/components/common/modules/main-overview.tsx index e4fbf34ffa..d8db49306b 100644 --- a/public/components/common/modules/main-overview.tsx +++ b/public/components/common/modules/main-overview.tsx @@ -31,6 +31,7 @@ import { Events, Dashboard, Loader, Settings } from '../../common/modules'; import OverviewActions from '../../../controllers/overview/components/overview-actions/overview-actions'; import { MainFim } from '../../agents/fim'; +import { MainVuls } from '../../agents/vuls'; import { MainSca } from '../../agents/sca'; import { MainMitre } from './main-mitre'; import WzReduxProvider from '../../../redux/wz-redux-provider'; @@ -210,6 +211,8 @@ const ModuleTabViewer = compose( {section === 'fim' && selectView==='inventory' && } {section === 'sca' && selectView==='inventory' && } + {section === 'vuls' && selectView==='inventory' && } + {section === 'mitre' && selectView === 'inventory' && } {['pci', 'gdpr', 'hipaa', 'nist', 'tsc'].includes(section) && selectView === 'inventory' && props.onSelectedTabChanged(id)} />} {/* -------------------------------------------------------------------------- */} diff --git a/public/components/common/modules/modules-defaults.js b/public/components/common/modules/modules-defaults.js index 5ed12eeeef..cbd2ba4fc6 100644 --- a/public/components/common/modules/modules-defaults.js +++ b/public/components/common/modules/modules-defaults.js @@ -35,6 +35,11 @@ export const ModulesDefaults = { tabs: [{ id: 'inventory', name: 'Framework' }, { id: 'dashboard', name: 'Dashboard' }, { id: 'events', name: 'Events' }], buttons: ['reporting'] }, + vuls: { + init: 'dashboard', + tabs: [{ id: 'inventory', name: 'Inventory', onlyAgent: false }, { id: 'dashboard', name: 'Dashboard' }, { id: 'events', name: 'Events' }], + buttons: ['reporting', 'settings'] + }, virustotal: { init: 'dashboard', tabs: [{ id: 'dashboard', name: 'Dashboard' }, { id: 'events', name: 'Events' }], From cc25d4e83ee9fde1f3e7b5b18664d45e3548f26e Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Tue, 9 Mar 2021 16:38:07 -0300 Subject: [PATCH 02/10] Added real request, removed mockup --- common/api-info/endpoints.json | 274 +++++++++++-- common/api-info/security-actions.json | 386 +++++++++++------- public/components/agents/vuls/index.ts | 2 +- public/components/agents/vuls/inventory.tsx | 131 +++--- .../agents/vuls/inventory/fileDetail.tsx | 2 +- .../agents/vuls/inventory/filterBar.tsx | 19 +- .../agents/vuls/inventory/flyout.tsx | 2 +- .../vuls/inventory/lib/getFilterValues.ts | 4 +- .../agents/vuls/inventory/table.tsx | 2 +- public/components/agents/vuls/main.tsx | 2 +- .../generate-api-4.0-info.js | 0 .../generate-api-4.0-info.sh | 0 scripts/jest.js | 0 13 files changed, 548 insertions(+), 276 deletions(-) mode change 100644 => 100755 scripts/generate-api-4.0-info/generate-api-4.0-info.js mode change 100644 => 100755 scripts/generate-api-4.0-info/generate-api-4.0-info.sh mode change 100644 => 100755 scripts/jest.js diff --git a/common/api-info/endpoints.json b/common/api-info/endpoints.json index 567b362de4..f4175ddb5c 100644 --- a/common/api-info/endpoints.json +++ b/common/api-info/endpoints.json @@ -81,7 +81,7 @@ }, { "name": "name", - "description": "Filter by agent name", + "description": "Filter by name", "schema": { "type": "string", "format": "alphanumeric" @@ -399,6 +399,58 @@ } ] }, + { + "name": "/agents/:agent_id/stats/:component", + "documentation": "https://documentation.wazuh.com/current/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_component_stats", + "description": "Return Wazuh's {component} statistical information from agent {agent_id}", + "summary": "Get agent's component stats", + "tags": [ + "Agents" + ], + "args": [ + { + "name": ":agent_id", + "description": "Agent ID. All possible values from 000 onwards", + "required": true, + "schema": { + "type": "string", + "minLength": 3, + "description": "Agent ID", + "format": "numbers" + } + }, + { + "name": ":component", + "description": "Selected component stats", + "required": true, + "schema": { + "type": "string", + "enum": [ + "logcollector", + "agent" + ] + } + } + ], + "query": [ + { + "name": "pretty", + "description": "Show results in human-readable format", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "wait_for_complete", + "description": "Disable timeout response", + "schema": { + "type": "boolean", + "default": false + } + } + ] + }, { "name": "/agents/no_group", "documentation": "https://documentation.wazuh.com/current/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_no_group", @@ -1224,21 +1276,20 @@ "schema": { "type": "string", "enum": [ - "ossec-agentlessd", - "ossec-analysisd", - "ossec-authd", - "ossec-csyslogd", - "ossec-dbd", - "ossec-execd", - "ossec-integratord", - "ossec-maild", - "ossec-monitord", - "ossec-logcollector", - "ossec-remoted", - "ossec-reportd", - "ossec-rootcheck", - "ossec-syscheckd", - "ossec-testrule", + "wazuh-agentlessd", + "wazuh-analysisd", + "wazuh-authd", + "wazuh-csyslogd", + "wazuh-dbd", + "wazuh-execd", + "wazuh-integratord", + "wazuh-maild", + "wazuh-monitord", + "wazuh-logcollector", + "wazuh-remoted", + "wazuh-reportd", + "wazuh-rootcheck", + "wazuh-syscheckd", "sca", "wazuh-db", "wazuh-modulesd", @@ -2742,7 +2793,7 @@ }, { "name": "name", - "description": "Filter by agent name", + "description": "Filter by name", "schema": { "type": "string", "format": "alphanumeric" @@ -3196,7 +3247,7 @@ }, { "name": "name", - "description": "Filter by agent name", + "description": "Filter by name", "schema": { "type": "string", "format": "alphanumeric" @@ -4672,21 +4723,20 @@ "schema": { "type": "string", "enum": [ - "ossec-agentlessd", - "ossec-analysisd", - "ossec-authd", - "ossec-csyslogd", - "ossec-dbd", - "ossec-execd", - "ossec-integratord", - "ossec-maild", - "ossec-monitord", - "ossec-logcollector", - "ossec-remoted", - "ossec-reportd", - "ossec-rootcheck", - "ossec-syscheckd", - "ossec-testrule", + "wazuh-agentlessd", + "wazuh-analysisd", + "wazuh-authd", + "wazuh-csyslogd", + "wazuh-dbd", + "wazuh-execd", + "wazuh-integratord", + "wazuh-maild", + "wazuh-monitord", + "wazuh-logcollector", + "wazuh-remoted", + "wazuh-reportd", + "wazuh-rootcheck", + "wazuh-syscheckd", "sca", "wazuh-db", "wazuh-modulesd", @@ -7038,7 +7088,7 @@ }, { "name": "name", - "description": "Filter by agent name", + "description": "Filter by name", "schema": { "type": "string", "format": "alphanumeric" @@ -7428,7 +7478,7 @@ }, { "name": "name", - "description": "Filter by agent name", + "description": "Filter by name", "schema": { "type": "string", "format": "alphanumeric" @@ -8014,6 +8064,133 @@ } } ] + }, + { + "name": "/vulnerability/:agent_id", + "documentation": "https://documentation.wazuh.com/current/user-manual/api/reference.html#operation/api.controllers.vulnerability_controller.get_vulnerability_agent", + "description": "Return the vulnerabilities of an agent", + "summary": "Get vulnerabilities", + "tags": [ + "Vulnerability" + ], + "args": [ + { + "name": ":agent_id", + "description": "Agent ID. All possible values from 000 onwards", + "required": true, + "schema": { + "type": "string", + "minLength": 3, + "description": "Agent ID", + "format": "numbers" + } + } + ], + "query": [ + { + "name": "architecture", + "description": "Filter by architecture", + "schema": { + "type": "string", + "format": "alphanumeric" + } + }, + { + "name": "cve", + "description": "Filter by CVE", + "schema": { + "type": "string", + "format": "alphanumeric" + } + }, + { + "name": "limit", + "description": "Maximum number of elements to return", + "schema": { + "type": "integer", + "format": "int32", + "default": 500, + "minimum": 1, + "maximum": 500 + } + }, + { + "name": "name", + "description": "Filter by name", + "schema": { + "type": "string", + "format": "alphanumeric" + } + }, + { + "name": "offset", + "description": "First element to return in the collection", + "schema": { + "type": "integer", + "format": "int32", + "default": 0, + "minimum": 0 + } + }, + { + "name": "pretty", + "description": "Show results in human-readable format", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "q", + "description": "Query to filter results by. For example q="status=active"", + "schema": { + "type": "string" + } + }, + { + "name": "search", + "description": "Look for elements containing the specified string. To obtain a complementary search, use '-' at the beggining", + "schema": { + "type": "string", + "format": "search" + } + }, + { + "name": "select", + "description": "Select which fields to return (separated by comma). Use '.' for nested fields. For example, '{field1: field2}' may be selected with 'field1.field2'", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "names" + } + } + }, + { + "name": "sort", + "description": "Sort the collection by a field or fields (separated by comma). Use +/- at the beggining to list in ascending or descending order. Use '.' for nested fields. For example, '{field1: field2}' may be selected with 'field1.field2'", + "schema": { + "type": "string", + "format": "sort" + } + }, + { + "name": "version", + "description": "Filter by CVE version", + "schema": { + "type": "string", + "format": "alphanumeric_symbols" + } + }, + { + "name": "wait_for_complete", + "description": "Disable timeout response", + "schema": { + "type": "boolean", + "default": false + } + } + ] } ] }, @@ -8078,6 +8255,15 @@ "description": "Whether the specified command is a custom command or not", "type": "boolean", "default": false + }, + "alert": { + "type": "object", + "properties": { + "data": { + "description": "Alert data depending on the AR executed", + "type": "object" + } + } } }, "required": [ @@ -9392,16 +9578,6 @@ "Groups" ], "query": [ - { - "name": "group_id", - "description": "Group ID. (Name of the group)", - "required": true, - "schema": { - "type": "string", - "description": "Group name", - "format": "group_names" - } - }, { "name": "pretty", "description": "Show results in human-readable format", @@ -9418,6 +9594,14 @@ "default": false } } + ], + "body": [ + { + "name": "group_id", + "description": "Group name", + "type": "string", + "format": "group_names" + } ] }, { diff --git a/common/api-info/security-actions.json b/common/api-info/security-actions.json index 01ba5c04aa..5090eeb2cf 100644 --- a/common/api-info/security-actions.json +++ b/common/api-info/security-actions.json @@ -1,15 +1,17 @@ { "active-response:command": { - "description": "Allow to execute active response commands in the agents", + "description": "Execute active response commands in the agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ "active-response:command" ], "resources": [ - "agent:id:001" + "agent:id:001", + "agent:group:atlantic" ], "effect": "allow" }, @@ -18,16 +20,18 @@ ] }, "agent:delete": { - "description": "Delete system's agents", + "description": "Delete agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ "agent:delete" ], "resources": [ - "agent:id:010" + "agent:id:010", + "agent:group:pacific" ], "effect": "allow" }, @@ -36,9 +40,10 @@ ] }, "agent:read": { - "description": "Access to one or more agents basic information (id, name, group, last keep alive, etc)", + "description": "Access agents information (id, name, group, last keep alive, etc)", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -54,6 +59,7 @@ "GET /agents/{agent_id}/config/{component}/{configuration}", "GET /agents/{agent_id}/group/is_sync", "GET /agents/{agent_id}/key", + "GET /agents/{agent_id}/stats/{component}", "GET /groups/{group_id}/agents", "GET /agents/no_group", "GET /agents/outdated", @@ -84,16 +90,18 @@ ] }, "agent:modify_group": { - "description": "Change the group of specified agent", + "description": "Change the group of agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ "agent:modify_group" ], "resources": [ - "agent:id:*" + "agent:id:004", + "agent:group:us-east" ], "effect": "allow" }, @@ -107,7 +115,7 @@ ] }, "group:modify_assignments": { - "description": "Allow to change the agents assigned to the group", + "description": "Change the agents assigned to the group", "resources": [ "group:id" ], @@ -130,9 +138,10 @@ ] }, "agent:restart": { - "description": "Restart Wazuh for allowed agents", + "description": "Restart agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -152,27 +161,29 @@ ] }, "agent:upgrade": { - "description": "Upgrade the version of an agent", + "description": "Upgrade the version of the agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ "agent:upgrade" ], "resources": [ - "agent:id:*" + "agent:id:001", + "agent:group:mediterranean" ], "effect": "allow" }, "related_endpoints": [ - "PUT /agents/{agent_id}/upgrade", - "PUT /agents/{agent_id}/upgrade_custom", - "GET /agents/{agent_id}/upgrade_result" + "PUT /agents/upgrade", + "PUT /agents/upgrade_custom", + "GET /agents/upgrade_result" ] }, "group:delete": { - "description": "Delete system's groups", + "description": "Delete agent groups", "resources": [ "group:id" ], @@ -190,9 +201,9 @@ ] }, "group:read": { - "description": "Access to one or more groups basic information (id, name, agents, etc)", + "description": "Access agent groups information (id, name, agents, etc)", "resources": [ - "group:id" + "*:*" ], "example": { "actions": [ @@ -214,7 +225,7 @@ ] }, "group:create": { - "description": "Create new groups", + "description": "Create new agent groups", "resources": [ "*:*" ], @@ -232,7 +243,7 @@ ] }, "group:update_config": { - "description": "Change group's configuration", + "description": "Change the configuration of agent groups", "resources": [ "group:id" ], @@ -250,7 +261,7 @@ ] }, "cluster:read": { - "description": "Read Wazuh's cluster configuration", + "description": "Read Wazuh's cluster nodes configuration", "resources": [ "node:id" ], @@ -280,16 +291,16 @@ "GET /cluster/{node_id}/stats/remoted", "GET /cluster/{node_id}/logs", "GET /cluster/{node_id}/logs/summary", - "PUT /cluster/{node_id}/configuration", "PUT /cluster/restart", "GET /cluster/configuration/validation", "GET /cluster/{node_id}/configuration/{component}/{configuration}" ] }, "ciscat:read": { - "description": "Get CIS-CAT results for a list of agents", + "description": "Access CIS-CAT results for agents", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -297,7 +308,8 @@ ], "resources": [ "agent:id:001", - "agent:id:003" + "agent:id:003", + "agent:group:default" ], "effect": "deny" }, @@ -307,7 +319,7 @@ ] }, "cluster:status": { - "description": "Check Wazuh's cluster status", + "description": "Check Wazuh's cluster general status", "resources": [ "*:*" ], @@ -325,7 +337,7 @@ ] }, "cluster:read_api_config": { - "description": "Check Wazuh's cluster API configuration", + "description": "Check Wazuh's cluster nodes API configuration", "resources": [ "*:*" ], @@ -343,28 +355,8 @@ "GET /cluster/api/config" ] }, - "cluster:update_api_config": { - "description": "Modify Wazuh's cluster API configuration", - "resources": [ - "*:*" - ], - "example": { - "actions": [ - "cluster:update_api_config" - ], - "resources": [ - "node:id:worker1", - "node:id:worker3" - ], - "effect": "allow" - }, - "related_endpoints": [ - "PUT /cluster/api/config", - "DELETE /cluster/api/config" - ] - }, "cluster:update_config": { - "description": "Update configuration of Wazuh's cluster node", + "description": "Change the Wazuh's cluster node configuration", "resources": [ "node:id" ], @@ -400,7 +392,7 @@ ] }, "lists:read": { - "description": "Read lists files", + "description": "Read cdb lists files", "resources": [ "list:file" ], @@ -415,13 +407,13 @@ }, "related_endpoints": [ "GET /lists", + "GET /lists/files/{filename}", "GET /lists/files" ] }, "lists:update": { - "description": "Update lists files", + "description": "Update or upload cdb lists files", "resources": [ - "list:file", "*:*" ], "example": { @@ -429,16 +421,16 @@ "lists:update" ], "resources": [ - "list:file:audit-keys" + "*:*:*" ], - "effect": "deny" + "effect": "allow" }, - "related_endpoints": [ - "PUT /lists/files" + "related_endpoints": [ + "PUT /lists/files/{filename}" ] }, "lists:delete": { - "description": "Delete lists files", + "description": "Delete cdb lists files", "resources": [ "list:file" ], @@ -451,8 +443,28 @@ ], "effect": "deny" }, - "related_endpoints": [ - "DELETE /lists/files" + "related_endpoints": [ + "PUT /lists/files/{filename}", + "DELETE /lists/files/{filename}" + ] + }, + "logtest:run": { + "description": "Run logtest tool or end a logtest session", + "resources": [ + "*:*" + ], + "example": { + "actions": [ + "logtest:run" + ], + "resources": [ + "*:*:*" + ], + "effect": "allow" + }, + "related_endpoints": [ + "PUT /logtest", + "DELETE /logtest/sessions/{token}" ] }, "manager:read": { @@ -480,14 +492,13 @@ "GET /manager/stats/remoted", "GET /manager/logs", "GET /manager/logs/summary", - "PUT /manager/configuration", "PUT /manager/restart", "GET /manager/configuration/validation", "GET /manager/configuration/{component}/{configuration}" ] - }, + }, "manager:update_config": { - "description": "Update the configuration of Wazuh manager node", + "description": "Update current Wazuh manager configuration", "resources": [ "*:*" ], @@ -498,7 +509,7 @@ "resources": [ "*:*:*" ], - "effect": "deny" + "effect": "allow" }, "related_endpoints": [ "PUT /manager/configuration" @@ -522,59 +533,98 @@ "GET /manager/api/config" ] }, - "manager:update_api_config": { - "description": "Modify Wazuh manager API configuration", + "manager:restart": { + "description": "Restart Wazuh managers", "resources": [ "*:*" ], "example": { "actions": [ - "manager:update_api_config" + "manager:restart" ], "resources": [ "*:*:*" ], - "effect": "allow" + "effect": "deny" }, "related_endpoints": [ - "PUT /manager/api/config", - "DELETE /manager/api/config" + "PUT /manager/restart" ] }, - "manager:restart": { - "description": "Restart Wazuh manager nodes", + "mitre:read": { + "description": "Access attacks information from MITRE database", "resources": [ "*:*" ], "example": { "actions": [ - "manager:restart" + "mitre:read" ], "resources": [ "*:*:*" ], + "effect": "allow" + }, + "related_endpoints": [ + "GET /mitre" + ] + }, + "rootcheck:clear": { + "description": "Clear the agents rootcheck database", + "resources": [ + "agent:id", + "agent:group" + ], + "example": { + "actions": [ + "rootcheck:clear" + ], + "resources": [ + "agent:id:*" + ], "effect": "deny" }, "related_endpoints": [ - "PUT /manager/restart" + "DELETE /rootcheck" ] }, - "mitre:read": { - "description": "Get attacks information from MITRE database.", + "rootcheck:run": { + "description": "Run agents rootcheck scan", "resources": [ - "*:*" + "agent:id", + "agent:group" ], "example": { "actions": [ - "mitre:read" + "rootcheck:run" ], "resources": [ - "*:*:*" + "agent:id:*" ], "effect": "allow" }, "related_endpoints": [ - "GET /mitre" + "PUT /rootcheck" + ] + }, + "rootcheck:read": { + "description": "Access information from agents rootcheck database", + "resources": [ + "agent:id", + "agent:group" + ], + "example": { + "actions": [ + "rootcheck:read" + ], + "resources": [ + "agent:id:011" + ], + "effect": "allow" + }, + "related_endpoints": [ + "GET /rootcheck/{agent_id}", + "GET /rootcheck/{agent_id}/last_scan" ] }, "rules:read": { @@ -596,30 +646,29 @@ "GET /rules/groups", "GET /rules/requirement/{requirement}", "GET /rules/files", - "GET /rules/files/{filename}/download" + "GET /rules/files/{filename}" ] }, "rules:update": { - "description": "Update rules files", + "description": "Update or upload custom rule files", "resources": [ - "rule:file", - "*:*" + "rule:file" ], "example": { "actions": [ "rules:update" ], "resources": [ - "rule:file:0610-win-ms_logs_rules.xml" + "*:*:*" ], "effect": "allow" }, "related_endpoints": [ - "PUT /rules/files" + "PUT /rules/files/{filename}" ] }, "rules:delete": { - "description": "Delete rules files", + "description": "Delete custom rule files", "resources": [ "rule:file" ], @@ -633,13 +682,15 @@ "effect": "allow" }, "related_endpoints": [ - "DELETE /rules/files" + "PUT /rules/files/{filename}", + "DELETE /rules/files/{filename}" ] - }, + }, "sca:read": { - "description": "Get a list of policies analyzed in the configuration assessment for a given agent", + "description": "Access agents security configuration assessment", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -656,9 +707,10 @@ ] }, "syscheck:run": { - "description": "Run syscheck", + "description": "Run agents syscheck scan", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -674,16 +726,18 @@ ] }, "syscheck:read": { - "description": "Read information from syscheck's database", + "description": "Access information from agents syscheck database", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ "syscheck:read" ], "resources": [ - "agent:id:011" + "agent:id:011", + "agent:group:us-west" ], "effect": "allow" }, @@ -693,9 +747,10 @@ ] }, "syscheck:clear": { - "description": "Clear the syscheck database for specified agents", + "description": "Clear the agents syscheck database", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -728,31 +783,30 @@ "related_endpoints": [ "GET /decoders", "GET /decoders/files", - "GET /decoders/files/{filename}/download", + "GET /decoders/files/{filename}", "GET /decoders/parents" ] }, "decoders:update": { - "description": "Update decoders files", + "description": "Update or upload custom decoder files", "resources": [ - "decoder:file", - "*:*" + "decoder:file" ], "example": { "actions": [ "decoders:update" ], "resources": [ - "decoder:file:*" + "*:*:*" ], "effect": "allow" }, "related_endpoints": [ - "PUT /decoders/files" + "PUT /decoders/files/{filename}" ] }, "decoders:delete": { - "description": "Delete decoders files", + "description": "Delete custom decoder files", "resources": [ "decoder:file" ], @@ -761,18 +815,20 @@ "decoders:delete" ], "resources": [ - "decoder:file:*" + "decoder:file:local_decoder.xml" ], "effect": "allow" }, "related_endpoints": [ - "DELETE /decoders/files" + "PUT /decoders/files/{filename}", + "DELETE /decoders/files/{filename}" ] }, "syscollector:read": { - "description": "Get syscollector information about a specified agents", + "description": "Access agents syscollector information", "resources": [ - "agent:id" + "agent:id", + "agent:group" ], "example": { "actions": [ @@ -805,11 +861,12 @@ ] }, "security:read": { - "description": "Allow read information about system's security resources", + "description": "Access information about system security resources", "resources": [ "policy:id", "role:id", - "user:id" + "user:id", + "rule:id" ], "example": { "actions": [ @@ -818,41 +875,43 @@ "resources": [ "policy:id:*", "role:id:2", - "user:id:5" + "user:id:5", + "rule:id:3" ], "effect": "allow" }, "related_endpoints": [ + "GET /security/users", "GET /security/roles", - "GET /security/policies", - "GET /security/users" + "GET /security/rules", + "GET /security/policies" ] }, - "security:create": { - "description": "Create new system security resources", + "security:create_user": { + "description": "Create new system users", "resources": [ "*:*" ], "example": { "actions": [ - "security:create" + "security:create_user" ], "resources": [ "*:*:*" ], - "effect": "deny" + "effect": "allow" }, "related_endpoints": [ - "POST /security/roles", - "POST /security/policies" + "POST /security/users" ] }, "security:delete": { - "description": "Delete system's security resources", + "description": "Delete system security resources", "resources": [ "policy:id", "role:id", - "user:id" + "user:id", + "rule:id" ], "example": { "actions": [ @@ -861,24 +920,28 @@ "resources": [ "policy:id:*", "role:id:3", - "user:id:4" + "user:id:4", + "rule:id:2" ], "effect": "deny" }, "related_endpoints": [ + "DELETE /security/users", "DELETE /security/roles", + "DELETE /security/rules", "DELETE /security/policies", - "DELETE /security/roles/{role_id}/policies", "DELETE /security/users/{user_id}/roles", - "DELETE /security/users" + "DELETE /security/roles/{role_id}/policies", + "DELETE /security/roles/{role_id}/rules" ] }, "security:update": { - "description": "Allow update the information of system's security resources", + "description": "Update the information of system security resources", "resources": [ "policy:id", "role:id", - "user:id" + "user:id", + "rule:id" ], "example": { "actions": [ @@ -887,38 +950,43 @@ "resources": [ "policy:id:*", "role:id:4", - "user:id:3" + "user:id:3", + "rule:id:4" ], "effect": "deny" }, "related_endpoints": [ + "PUT /security/users/{user_id}", "PUT /security/roles/{role_id}", + "PUT /security/rules/{rule_id}", "PUT /security/policies/{policy_id}", - "POST /security/roles/{role_id}/policies", "POST /security/users/{user_id}/roles", - "PUT /security/users/{user_id}" + "POST /security/roles/{role_id}/policies", + "POST /security/roles/{role_id}/rules" ] }, - "security:create_user": { - "description": "Create new system user", + "security:create": { + "description": "Create new system security resources", "resources": [ "*:*" ], "example": { "actions": [ - "security:create_user" + "security:create" ], "resources": [ "*:*:*" ], - "effect": "allow" + "effect": "deny" }, "related_endpoints": [ - "POST /security/users" + "POST /security/roles", + "POST /security/rules", + "POST /security/policies" ] }, "security:read_config": { - "description": "Read current security configuration", + "description": "Read current system security configuration", "resources": [ "*:*" ], @@ -936,7 +1004,7 @@ ] }, "security:update_config": { - "description": "Update current security configuration", + "description": "Update current system security configuration", "resources": [ "*:*" ], @@ -953,5 +1021,43 @@ "PUT /security/config", "DELETE /security/config" ] + }, + "task:status": { + "description": "Access task's status information", + "resources": [ + "*:*" + ], + "example": { + "actions": [ + "task:status" + ], + "resources": [ + "*:*:*" + ], + "effect": "deny" + }, + "related_endpoints": [ + "GET /tasks/status" + ] + }, + "vulnerability:read": { + "description": "Allow reading agents' vulnerabilities information", + "resources": [ + "agent:id", + "agent:group" + ], + "example": { + "actions": [ + "vulnerability:read" + ], + "resources": [ + "agent:id:011", + "agent:group:us-west" + ], + "effect": "allow" + }, + "related_endpoints": [ + "GET /vulnerability/{agent_id}" + ] } } \ No newline at end of file diff --git a/public/components/agents/vuls/index.ts b/public/components/agents/vuls/index.ts index 4a0a2682ad..234024e797 100644 --- a/public/components/agents/vuls/index.ts +++ b/public/components/agents/vuls/index.ts @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring components + * Wazuh app - Agent vulnerabilities components * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index 0338224920..6fb0877217 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring components + * Wazuh app - Agent vulnerabilities components * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify @@ -38,19 +38,17 @@ import { ICustomBadges } from '../../wz-search-bar/components'; import { filtersToObject } from '../../wz-search-bar'; import { WzEmptyPromptNoPermissions } from "../../common/permissions/prompt"; -import mockup from './vuls-mockup'; +// import mockup from './vuls-mockup'; export class Inventory extends Component { _isMount = false; state: { filters: []; - // selectedTabId: 'files' | 'registry'; - totalItemsFile: number; - totalItemsRegistry: number; + totalItems: number; isLoading: Boolean; - syscheck: []; + items: []; customBadges: ICustomBadges[]; - isConfigured: Boolean; + // isConfigured: Boolean; }; props: any; @@ -59,13 +57,11 @@ export class Inventory extends Component { super(props); this.state = { filters: [], - syscheck: [], - // selectedTabId: 'files', - totalItemsFile: 0, - totalItemsRegistry: 0, + items: [], + totalItems: 0, isLoading: true, - customBadges: [], - isConfigured: false + customBadges: []/*, + isConfigured: false*/ } this.onFiltersChange.bind(this); } @@ -86,12 +82,11 @@ export class Inventory extends Component { } async loadAgent() { - const agentPlatform = ((this.props.agent || {}).os || {}).platform; - const {totalItemsFile, syscheck} = await this.getItemNumber('file'); - const totalItemsRegistry = agentPlatform === 'windows' ? await this.getItemNumber('registry') : 0; - const isConfigured = await this.isConfigured(); + // const agentPlatform = ((this.props.agent || {}).os || {}).platform; + const {totalItems, items} = await this.getItemNumber(); + // const isConfigured = await this.isConfigured(); if (this._isMount){ - this.setState({ totalItemsFile, totalItemsRegistry, syscheck, isLoading: false, isConfigured }); + this.setState({ totalItems, items, isLoading: false/*, isConfigured*/ }); } } @@ -159,17 +154,14 @@ export class Inventory extends Component { async getItemNumber() { try { const agentID = this.props.agent.id; - // const response = await WzRequest.apiReq('GET', `/syscheck/${agentID}`, { - // params: this.buildFilter(), - // }); - const response = mockup; - // if (type === 'file') { - return { - totalItemsFile: ((response.data || {}).data || {}).total_affected_items || 0, - syscheck: ((response.data || {}).data || {}).affected_items || [], - }; - // } - // return ((response.data || {}).data || {}).total_affected_items || 0; + let response = await WzRequest.apiReq('GET', `/vulnerability/${agentID}`, { + params: this.buildFilter(), + }); + // response = mockup; + return { + totalItems: ((response.data || {}).data || {}).total_affected_items || 0, + items: ((response.data || {}).data || {}).affected_items || [], + }; } catch (error) { this.setState({ isLoading: false }); this.showToast('danger', error, 3000); @@ -211,9 +203,9 @@ export class Inventory extends Component { const formatedFilters = Object.keys(filtersObject).map(key => ({name: key, value: filtersObject[key]})); this.showToast('success', 'Your download should begin automatically...', 3000); await exportCsv( - '/syscheck/' + this.props.agent.id, - [ - { name: 'type', value: 'vulnerabilities' }, + '/vulnerability/' + this.props.agent.id, + [{}, + // { name: 'type', value: 'vulnerabilities' }, ...formatedFilters ], `vuls-vulnerabilities` @@ -224,7 +216,7 @@ export class Inventory extends Component { } renderTable() { - const { filters, syscheck, totalItemsFile } = this.state; + const { filters, items, totalItems } = this.state; return (
) } - noConfigured() { - return ( - Vulnerabilities is not configured for this agent} - body={ - - - https://documentation.wazuh.com/current/user-manual/capabilities/file-integrity/index.html - - - } - />); - } + // noConfigured() { + // return ( + // Vulnerabilities is not configured for this agent} + // body={ + // + // + // https://documentation.wazuh.com/current/user-manual/capabilities/file-integrity/index.html + // + // + // } + // />); + // } loadingInventory() { return @@ -272,36 +264,35 @@ export class Inventory extends Component { ; } - async isConfigured() { - try { - const response = await WzRequest.apiReq( - 'GET', - `/agents/${this.props.agent.id}/config/syscheck/syscheck`, - {} - ); + // async isConfigured() { + // try { + // const response = await WzRequest.apiReq( + // 'GET', + // `/agents/${this.props.agent.id}/config/syscheck/syscheck`, + // {} + // ); - return (((response.data || {}).data).syscheck || {}).disabled === 'no'; - } catch (error) { - return false; - } - } + // return (((response.data || {}).data).syscheck || {}).disabled === 'no'; + // } catch (error) { + // return false; + // } + // } render() { - const { isLoading, isConfigured } = this.state; + const { isLoading/*, isConfigured*/ } = this.state; if (isLoading) { return this.loadingInventory() } const table = this.renderTable(); const tabs = this.renderTabs(); - return isConfigured - ? ( + return /*isConfigured ? (*/ {tabs} {table} - ) - : this.noConfigured() + + //) : this.noConfigured() } } diff --git a/public/components/agents/vuls/inventory/fileDetail.tsx b/public/components/agents/vuls/inventory/fileDetail.tsx index bc12bbc4c0..628cd7faf2 100644 --- a/public/components/agents/vuls/inventory/fileDetail.tsx +++ b/public/components/agents/vuls/inventory/fileDetail.tsx @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring table component + * Wazuh app - Agent vulnerabilities table component * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/public/components/agents/vuls/inventory/filterBar.tsx b/public/components/agents/vuls/inventory/filterBar.tsx index 176bc34869..dda07d5f43 100644 --- a/public/components/agents/vuls/inventory/filterBar.tsx +++ b/public/components/agents/vuls/inventory/filterBar.tsx @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring components + * Wazuh app - Agent vulnerabilities components * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify @@ -25,19 +25,10 @@ export class FilterBar extends Component { } // TODO: Change the type suggestions: IWzSuggestItem[] = [ - {type: 'q', label: 'name', description:"Name of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('file', value, this.props.agent.id, {type:'file'})}, - ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'perm', description:"Permisions of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('perm', value, this.props.agent.id)}]: []), - {type: 'q', label: 'mtime', description:"Date the file was modified", operators:['=','!=', '>', '<'], values: async (value) => getFilterValues('mtime', value, this.props.agent.id,{}, this.formatDate)}, - {type: 'q', label: 'date', description:"Date of registration of the event", operators:['=','!=', '>', '<'], values: async (value) => getFilterValues('date', value, this.props.agent.id, {}, this.formatDate)}, - {type: 'q', label: 'uname', description:"Owner of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('uname', value, this.props.agent.id)}, - {type: 'q', label: 'uid', description:"Id of the onwner file", operators:['=','!=', '~'], values: async (value) => getFilterValues('uid', value, this.props.agent.id)}, - ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'gname', description:"Name of the group owner file", operators:['=','!=', '~'], values: async (value) => getFilterValues('gname', value, this.props.agent.id)}]: []), - ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'gid', description:"Id of the group owner", operators:['=','!=', '~'], values: async (value) => getFilterValues('gid', value, this.props.agent.id)}]: []), - {type: 'q', label: 'md5', description:"md5 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('md5', value, this.props.agent.id)}, - {type: 'q', label: 'sha1', description:"sha1 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('sha1', value, this.props.agent.id)}, - {type: 'q', label: 'sha256', description:"sha256 hash", operators:['=','!=', '~'], values: async (value) => getFilterValues('sha256', value, this.props.agent.id)}, - ...(((this.props.agent || {}).os || {}).platform !== 'windows' ? [{type: 'q', label: 'inode', description:"Inode of the file", operators:['=','!=', '~'], values: async (value) => getFilterValues('inode', value, this.props.agent.id)}]: []), - {type: 'q', label: 'size', description:"Size of the file in Bytes", values: async (value) => getFilterValues('size', value, this.props.agent.id)}, + {type: 'q', label: 'name', description:"Filter by package ID", operators:['=','!=', '~'], values: async (value) => getFilterValues('name', value, this.props.agent.id)}, + {type: 'q', label: 'cve', description:"Filter by CVE ID", operators:['=','!=', '~'], values: async (value) => getFilterValues('cve', value, this.props.agent.id)}, + {type: 'q', label: 'version', description:"Filter by CVE version", operators:['=','!=', '~'], values: async (value) => getFilterValues('version', value, this.props.agent.id)}, + {type: 'q', label: 'architecture', description:"Filter by architecture", operators:['=','!=', '~'], values: async (value) => getFilterValues('architecture', value, this.props.agent.id)}, ] props!:{ diff --git a/public/components/agents/vuls/inventory/flyout.tsx b/public/components/agents/vuls/inventory/flyout.tsx index 79a1ea2252..508329d2c7 100644 --- a/public/components/agents/vuls/inventory/flyout.tsx +++ b/public/components/agents/vuls/inventory/flyout.tsx @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring components + * Wazuh app - Agent vulnerabilities components * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/public/components/agents/vuls/inventory/lib/getFilterValues.ts b/public/components/agents/vuls/inventory/lib/getFilterValues.ts index 6c7325feb9..59fec3fa85 100644 --- a/public/components/agents/vuls/inventory/lib/getFilterValues.ts +++ b/public/components/agents/vuls/inventory/lib/getFilterValues.ts @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring components + * Wazuh app - Agent vulnerabilities components * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,6 @@ export async function getFilterValues(field, value, agentId, filters={}, format= if (value) { filter['search'] = value; } - const result = await WzRequest.apiReq('GET', `/syscheck/${agentId}`, { params: filter }); + const result = await WzRequest.apiReq('GET', `/vulnerability/${agentId}`, { params: filter }); return (((result || {}).data || {}).data || {}).affected_items.map((item) => {return format(item[field])}); } \ No newline at end of file diff --git a/public/components/agents/vuls/inventory/table.tsx b/public/components/agents/vuls/inventory/table.tsx index dc054175de..d86f2c2b44 100644 --- a/public/components/agents/vuls/inventory/table.tsx +++ b/public/components/agents/vuls/inventory/table.tsx @@ -1,5 +1,5 @@ /* - * Wazuh app - Integrity monitoring table component + * Wazuh app - Agent vulnerabilities table component * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/public/components/agents/vuls/main.tsx b/public/components/agents/vuls/main.tsx index c33a5d95fb..614f2eac74 100644 --- a/public/components/agents/vuls/main.tsx +++ b/public/components/agents/vuls/main.tsx @@ -31,7 +31,7 @@ export const MainVuls = compose( props.currentAgentData && props.currentAgentData.id ? props.currentAgentData : props.agent; return [ { action: 'agent:read', resource: `agent:id:${agentData.id}` }, - { action: 'syscheck:read', resource: `agent:id:${agentData.id}` }, + { action: 'vulnerability:read', resource: `agent:id:${agentData.id}` }, ]; }) )(function MainVuls({ currentAgentData, agent, ...rest }) { diff --git a/scripts/generate-api-4.0-info/generate-api-4.0-info.js b/scripts/generate-api-4.0-info/generate-api-4.0-info.js old mode 100644 new mode 100755 diff --git a/scripts/generate-api-4.0-info/generate-api-4.0-info.sh b/scripts/generate-api-4.0-info/generate-api-4.0-info.sh old mode 100644 new mode 100755 diff --git a/scripts/jest.js b/scripts/jest.js old mode 100644 new mode 100755 From 118e3a49dbcbe0fb4d3ff4dff10cfd726b086521 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Tue, 9 Mar 2021 19:00:01 -0300 Subject: [PATCH 03/10] Fixed the table filters --- public/components/agents/vuls/inventory.tsx | 82 +------------------ .../agents/vuls/inventory/filterBar.tsx | 11 +-- .../vuls/inventory/lib/getFilterValues.ts | 1 - .../agents/vuls/inventory/table.tsx | 56 +++++-------- 4 files changed, 29 insertions(+), 121 deletions(-) diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index 6fb0877217..998e7df1d9 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -38,8 +38,6 @@ import { ICustomBadges } from '../../wz-search-bar/components'; import { filtersToObject } from '../../wz-search-bar'; import { WzEmptyPromptNoPermissions } from "../../common/permissions/prompt"; -// import mockup from './vuls-mockup'; - export class Inventory extends Component { _isMount = false; state: { @@ -48,7 +46,6 @@ export class Inventory extends Component { isLoading: Boolean; items: []; customBadges: ICustomBadges[]; - // isConfigured: Boolean; }; props: any; @@ -60,8 +57,7 @@ export class Inventory extends Component { items: [], totalItems: 0, isLoading: true, - customBadges: []/*, - isConfigured: false*/ + customBadges: [] } this.onFiltersChange.bind(this); } @@ -82,42 +78,12 @@ export class Inventory extends Component { } async loadAgent() { - // const agentPlatform = ((this.props.agent || {}).os || {}).platform; const {totalItems, items} = await this.getItemNumber(); - // const isConfigured = await this.isConfigured(); if (this._isMount){ - this.setState({ totalItems, items, isLoading: false/*, isConfigured*/ }); + this.setState({ totalItems, items, isLoading: false }); } } - // Do not load the localStorage filters when changing tabs - // componentDidUpdate(prevProps, prevState) { - // const { selectedTabId } = this.state; - // if (selectedTabId !== prevState.selectedTabId) { - // const filters = this.getStoreFilters(this.props); - // this.setState({ filters }); - // } - // } - - // tabs() { - // let auxTabs = [ - // { - // id: 'vulnerabilities', - // name: `Vulnerabilities ${this.state.isLoading === true ? '' : '(' + this.state.totalItemsFile + ')'}`, - // disabled: false, - // }, - // ]; - // // const platform = (this.props.agent.os || {}).platform || "other"; - // // platform === 'windows' ? auxTabs.push( - // // { - // // id: 'registry', - // // name: `Windows Registry ${this.state.isLoading === true ? '' : '(' + this.state.totalItemsRegistry + ')'}`, - // // disabled: false, - // // }, - // // ) : null; - // return (auxTabs); - // } - getStoreFilters(props) { const { section, selectView, agent } = props; const filters = JSON.parse(window.localStorage.getItem(`wazuh-${section}-${selectView}-vulnerabilities-${agent['id']}`) || '{}'); @@ -138,10 +104,6 @@ export class Inventory extends Component { this.setState({ totalItemsFile: totalItems }); } - // onSelectedTabChanged = id => { - // this.setState({ selectedTabId: id }); - // } - buildFilter() { const filters = filtersToObject(this.state.filters); const filter = { @@ -157,7 +119,6 @@ export class Inventory extends Component { let response = await WzRequest.apiReq('GET', `/vulnerability/${agentID}`, { params: this.buildFilter(), }); - // response = mockup; return { totalItems: ((response.data || {}).data || {}).total_affected_items || 0, items: ((response.data || {}).data || {}).affected_items || [], @@ -169,7 +130,6 @@ export class Inventory extends Component { } renderTabs() { - // const tabs = this.tabs(); const { isLoading } = this.state; return ( @@ -205,7 +165,6 @@ export class Inventory extends Component { await exportCsv( '/vulnerability/' + this.props.agent.id, [{}, - // { name: 'type', value: 'vulnerabilities' }, ...formatedFilters ], `vuls-vulnerabilities` @@ -222,7 +181,6 @@ export class Inventory extends Component { Vulnerabilities is not configured for this agent} - // body={ - // - // - // https://documentation.wazuh.com/current/user-manual/capabilities/file-integrity/index.html - // - // - // } - // />); - // } loadingInventory() { return @@ -264,35 +204,21 @@ export class Inventory extends Component { ; } - // async isConfigured() { - // try { - // const response = await WzRequest.apiReq( - // 'GET', - // `/agents/${this.props.agent.id}/config/syscheck/syscheck`, - // {} - // ); - - // return (((response.data || {}).data).syscheck || {}).disabled === 'no'; - // } catch (error) { - // return false; - // } - // } render() { - const { isLoading/*, isConfigured*/ } = this.state; + const { isLoading } = this.state; if (isLoading) { return this.loadingInventory() } const table = this.renderTable(); const tabs = this.renderTabs(); - return /*isConfigured ? (*/ + return {tabs} {table} - //) : this.noConfigured() } } diff --git a/public/components/agents/vuls/inventory/filterBar.tsx b/public/components/agents/vuls/inventory/filterBar.tsx index dda07d5f43..e8abfcb7bf 100644 --- a/public/components/agents/vuls/inventory/filterBar.tsx +++ b/public/components/agents/vuls/inventory/filterBar.tsx @@ -17,13 +17,9 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import moment from 'moment'; export class FilterBar extends Component { - formatDate = (date: String) => { - return moment(date).format('YYYY-MM-DD'); - } - // TODO: Change the type + suggestions: IWzSuggestItem[] = [ {type: 'q', label: 'name', description:"Filter by package ID", operators:['=','!=', '~'], values: async (value) => getFilterValues('name', value, this.props.agent.id)}, {type: 'q', label: 'cve', description:"Filter by CVE ID", operators:['=','!=', '~'], values: async (value) => getFilterValues('cve', value, this.props.agent.id)}, @@ -33,7 +29,6 @@ export class FilterBar extends Component { props!:{ onFiltersChange(filters:IFilter[]): void - selectView: 'files' | 'registry' agent: {id: string, agentPlatform: string} onChangeCustomBadges?(customBadges: ICustomBadges[]): void customBadges?: ICustomBadges[] @@ -41,7 +36,7 @@ export class FilterBar extends Component { } render() { - const { onFiltersChange, selectView, filters} = this.props; + const { onFiltersChange, filters} = this.props; return ( @@ -49,7 +44,7 @@ export class FilterBar extends Component { noDeleteFiltersOnUpdateSuggests filters={filters} onFiltersChange={onFiltersChange} - suggestions={this.suggestions[selectView]} + suggestions={this.suggestions} placeholder='Filter or search vulnerabilities' /> diff --git a/public/components/agents/vuls/inventory/lib/getFilterValues.ts b/public/components/agents/vuls/inventory/lib/getFilterValues.ts index 59fec3fa85..e9660596df 100644 --- a/public/components/agents/vuls/inventory/lib/getFilterValues.ts +++ b/public/components/agents/vuls/inventory/lib/getFilterValues.ts @@ -15,7 +15,6 @@ export async function getFilterValues(field, value, agentId, filters={}, format= const filter = { ...filters, - distinct: true, select: field, limit: 30, }; diff --git a/public/components/agents/vuls/inventory/table.tsx b/public/components/agents/vuls/inventory/table.tsx index d86f2c2b44..0e293de3ca 100644 --- a/public/components/agents/vuls/inventory/table.tsx +++ b/public/components/agents/vuls/inventory/table.tsx @@ -25,7 +25,7 @@ import { filtersToObject, IFilter } from '../../../wz-search-bar'; export class InventoryTable extends Component { state: { - syscheck: [] + items: [] error?: string pageIndex: number pageSize: number @@ -52,11 +52,11 @@ export class InventoryTable extends Component { super(props); this.state = { - syscheck: props.items, + items: props.items, pageIndex: 0, pageSize: 15, totalItems: 0, - sortField: 'file', + sortField: 'name', sortDirection: 'asc', isLoading: false, isFlyoutVisible: false, @@ -68,13 +68,7 @@ export class InventoryTable extends Component { } async componentDidMount() { - const regex = new RegExp('file=' + '[^&]*'); - const match = window.location.href.match(regex); this.setState({totalItems: this.props.totalItems}); - if (match && match[0]) { - const file = match[0].split('=')[1]; - this.showFlyout(decodeURIComponent(file), true); - } } closeFlyout() { @@ -83,21 +77,18 @@ export class InventoryTable extends Component { async showFlyout(file, item, redirect = false) { - let fileData = false; + let itemData = false; if (!redirect) { - fileData = this.state.syscheck.filter(item => { + itemData = this.state.items.filter(item => { return item.file === file; }) } else { - const response = await WzRequest.apiReq('GET', `/syscheck/${this.props.agent.id}`, { - params: { - 'file': file - } + const response = await WzRequest.apiReq('GET', `/vulnerability/${this.props.agent.id}`, { + params: {} }); - fileData = ((response.data || {}).data || {}).affected_items || []; + itemData = ((response.data || {}).data || {}).affected_items || []; } - if (!redirect) - window.location.href = window.location.href += `&file=${file}`; + //if a flyout is opened, we close it and open a new one, so the components are correctly updated on start. this.setState({ isFlyoutVisible: false }, () => this.setState({ isFlyoutVisible: true, currentItem: file, syscheckItem: item })); } @@ -105,24 +96,24 @@ export class InventoryTable extends Component { async componentDidUpdate(prevProps) { const { filters } = this.props; if (JSON.stringify(filters) !== JSON.stringify(prevProps.filters)) { - this.setState({ pageIndex: 0, isLoading: true }, this.getSyscheck); + this.setState({ pageIndex: 0, isLoading: true }, this.getItems); } } - async getSyscheck() { + async getItems() { const agentID = this.props.agent.id; try { - const syscheck = await WzRequest.apiReq( + const items = await WzRequest.apiReq( 'GET', - `/syscheck/${agentID}`, + `/vulnerability/${agentID}`, { params: this.buildFilter()}, ); - this.props.onTotalItemsChange((((syscheck || {}).data || {}).data || {}).total_affected_items); + this.props.onTotalItemsChange((((items || {}).data || {}).data || {}).total_affected_items); this.setState({ - syscheck: (((syscheck || {}).data || {}).data || {}).affected_items || {}, - totalItems: (((syscheck || {}).data || {}).data || {}).total_affected_items - 1, + items: (((items || {}).data || {}).data || {}).affected_items || {}, + totalItems: (((items || {}).data || {}).data || {}).total_affected_items - 1, isLoading: false, error: undefined }); @@ -133,11 +124,9 @@ export class InventoryTable extends Component { buildSortFilter() { const { sortField, sortDirection } = this.state; - - const field = (sortField === 'os_name') ? '' : sortField; const direction = (sortDirection === 'asc') ? '+' : '-'; - return direction + field; + return direction + sortField; } buildFilter() { @@ -147,8 +136,7 @@ export class InventoryTable extends Component { ...filters, offset: pageIndex * pageSize, limit: pageSize, - sort: this.buildSortFilter(), - type: 'file' + sort: this.buildSortFilter() }; return filter; } @@ -163,7 +151,7 @@ export class InventoryTable extends Component { sortDirection, isLoading: true, }, - () => this.getSyscheck() + () => this.getItems() ); }; @@ -209,7 +197,7 @@ export class InventoryTable extends Component { }; }; - const { syscheck, pageIndex, pageSize, totalItems, sortField, sortDirection, isLoading, error } = this.state; + const { items, pageIndex, pageSize, totalItems, sortField, sortDirection, isLoading, error } = this.state; const columns = this.columns(); const pagination = { pageIndex: pageIndex, @@ -228,14 +216,14 @@ export class InventoryTable extends Component { From b5ee5fd03d01c91d1785f5628a357324d4fa3ff7 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Thu, 11 Mar 2021 13:06:06 -0300 Subject: [PATCH 04/10] Flyout + code cleaning --- public/components/agents/vuls/inventory.tsx | 16 +- .../inventory/{fileDetail.tsx => detail.tsx} | 208 ++++-------------- .../agents/vuls/inventory/flyout.tsx | 68 ++---- .../agents/vuls/inventory/table.tsx | 37 +--- 4 files changed, 79 insertions(+), 250 deletions(-) rename public/components/agents/vuls/inventory/{fileDetail.tsx => detail.tsx} (69%) diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index 998e7df1d9..ec305f05f6 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -10,21 +10,16 @@ * Find more information about this on the LICENSE file. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import { EuiPanel, EuiPage, - EuiTabs, - EuiTab, EuiTitle, EuiLoadingSpinner, - EuiEmptyPrompt, EuiSpacer, EuiProgress, EuiFlexGroup, EuiFlexItem, - EuiLink, - EuiHorizontalRule, EuiButtonEmpty } from '@elastic/eui'; import { @@ -36,7 +31,6 @@ import exportCsv from '../../../react-services/wz-csv'; import { getToasts } from '../../../kibana-services'; import { ICustomBadges } from '../../wz-search-bar/components'; import { filtersToObject } from '../../wz-search-bar'; -import { WzEmptyPromptNoPermissions } from "../../common/permissions/prompt"; export class Inventory extends Component { _isMount = false; @@ -86,17 +80,17 @@ export class Inventory extends Component { getStoreFilters(props) { const { section, selectView, agent } = props; - const filters = JSON.parse(window.localStorage.getItem(`wazuh-${section}-${selectView}-vulnerabilities-${agent['id']}`) || '{}'); + const filters = JSON.parse(window.localStorage.getItem(`wazuh-${section}-${selectView}-vulnerability-${agent['id']}`) || '{}'); return filters; } setStoreFilters(filters) { const { section, selectView, agent } = this.props; - window.localStorage.setItem(`wazuh-${section}-${selectView}-vulnerabilities-${agent['id']}`, JSON.stringify(filters)) + window.localStorage.setItem(`wazuh-${section}-${selectView}-vulnerability-${agent['id']}`, JSON.stringify(filters)) } onFiltersChange = (filters) => { - // this.setStoreFilters(filters); + this.setStoreFilters(filters); this.setState({ filters }); } @@ -175,7 +169,7 @@ export class Inventory extends Component { } renderTable() { - const { filters, items, totalItems } = this.state; + const { filters, items, totalItems } = this.state; return (
; loadEventsWithFilters: Function; @@ -90,139 +90,55 @@ export class FileDetails extends Component { details() { return [ { - field: 'date', - name: 'Last analysis', - grow: 2, - icon: 'clock', - link: true, - transformValue: TimeService.offset - }, - { - field: 'mtime', - name: 'Last modified', - grow: 2, - icon: 'clock', - link: true, - transformValue: TimeService.offset - }, - { - field: 'uname', - name: 'User', - icon: 'user', - link: true, - }, - { - field: 'uid', - name: 'User ID', - icon: 'user', - link: true, - }, - { - field: 'gname', - name: 'Group', - icon: 'usersRolesApp', - onlyLinux: true, - link: true, - }, - { - field: 'gid', - name: 'Group ID', - onlyLinux: true, - icon: 'usersRolesApp', - link: true, - }, - { - field: 'perm', - name: 'Permissions', - icon: 'lock', + field: 'name', + name: 'Name', + icon: 'dot', link: false, - transformValue: (value) => this.renderFileDetailsPermissions(value), - }, - { - field: 'size', - name: 'Size', - icon: 'nested', - link: true, - transformValue: (value) => this.renderFileDetailsSize(value), - }, - { - field: 'inode', - name: 'Inode', - icon: 'link', - onlyLinux: true, - link: true, - }, - { - field: 'md5', - name: 'MD5', - checksum: true, - icon: 'check', - link: true, - }, - { - field: 'sha1', - name: 'SHA1', - checksum: true, - icon: 'check', - link: true, }, { - field: 'sha256', - name: 'SHA256', - checksum: true, - icon: 'check', - link: true, - }, - ]; - } - - registryDetails() { - return [ - { - field: 'date', - name: 'Last analysis', - grow: 2, - icon: 'clock', - transformValue: TimeService.offset + field: 'cve', + name: 'CVE', + icon: 'securitySignal', + link: false, }, { - field: 'mtime', - name: 'Last modified', - grow: 2, - icon: 'clock', + field: 'version', + name: 'Version', + icon: 'package', + link: false, }, { - field: 'sha1', - name: 'SHA1', - checksum: true, - icon: 'check', - }, + field: 'architecture', + name: 'Architecture', + icon: 'node', + link: false, + } ]; } viewInEvents = (ev) => { - const { file } = this.props.currentFile; + const { cve } = this.props.currentItem; if (this.props.view === 'extern') { AppNavigate.navigateToModule(ev, 'overview', { - tab: 'fim', + tab: 'vuls', tabView: 'events', - filters: { 'syscheck.path': file }, + filters: { 'data.vulnerability.cve': cve }, }); } else { AppNavigate.navigateToModule( ev, 'overview', - { tab: 'fim', tabView: 'events', filters: { 'syscheck.path': file } }, + { tab: 'vuls', tabView: 'events', filters: { 'data.vulnerability.cve': cve } }, () => this.openEventCurrentWindow() ); } }; openEventCurrentWindow() { - const { file } = this.props.currentFile; + const { cve } = this.props.currentItem; const filters = [ { - ...buildPhraseFilter({ name: 'syscheck.path', type: 'text' }, file, this.indexPattern), + ...buildPhraseFilter({ name: 'data.vulnerability.cve', type: 'text' }, cve, this.indexPattern), $state: { store: 'appState' }, }, ]; @@ -235,15 +151,10 @@ export class FileDetails extends Component { const { filterManager } = getDataPlugin().query; const _filters = filterManager.getFilters(); if (_filters && _filters.length) { - const syscheckPathFilters = _filters.filter((x) => { - return x.meta.key === 'syscheck.path'; - }); - syscheckPathFilters.map((x) => { - filterManager.removeFilter(x); - }); + filterManager.addFilters([filters]); const scope = await ModulesHelper.getDiscoverScope(); - scope.updateQueryAndFetch({ query: null }); + scope.updateQueryAndFetch && scope.updateQueryAndFetch({ query: null }); } else { setTimeout(() => { this.checkFilterManager(filters); @@ -260,7 +171,7 @@ export class FileDetails extends Component { 'YYYY-MM-DD' )} AND ${field}<${value_max.format('YYYY-MM-DD')}`; } else { - newBadge.value = `${field}=${field === 'size' ? this.props.currentFile[field] : value}`; + newBadge.value = `${field}=${field === 'size' ? this.props.currentItem[field] : value}`; } !filters.some((item) => item.field === newBadge.field && item.value === newBadge.value) && onFiltersChange([...filters, newBadge]); @@ -269,14 +180,15 @@ export class FileDetails extends Component { getDetails() { const { view } = this.props; - const columns = this.props.type === 'registry_key' || this.props.currentFile.type === 'registry_key' ? this.registryDetails() : this.details(); + const columns = this.details(); const generalDetails = columns.map((item, idx) => { - var value = this.props.currentFile[item.field] || '-'; + var value = this.props.currentItem[item.field] || '-'; if (item.transformValue) { value = item.transformValue(value, this.props); } var link = (item.link && !['events', 'extern'].includes(view)) || false; const agentPlatform = ((this.props.agent || {}).os || {}).platform; + if (!item.onlyLinux || (item.onlyLinux && this.props.agent && agentPlatform !== 'windows')) { let className = item.checksum ? 'detail-value detail-value-checksum' : 'detail-value'; className += item.field === 'perm' ? ' detail-value-perm' : ''; @@ -346,7 +258,7 @@ export class FileDetails extends Component { this.setState({ totalHits: total }); }; - renderFileDetailsPermissions(value) { + renderDetailsPermissions(value) { if (((this.props.agent || {}).os || {}).platform === 'windows' && value && value !== '-') { const components = value .split(', ') @@ -391,28 +303,15 @@ export class FileDetails extends Component { return value; } - renderFileDetailsSize(value) { - if (isNaN(value)) { - return 0; - } - const b = 2; - if (0 === value) return '0 Bytes'; - const c = 0 > b ? 0 : b, - d = Math.floor(Math.log(value) / Math.log(1024)); - return ( - parseFloat((value / Math.pow(1024, d)).toFixed(c)) + - ' ' + - ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][d] - ); - } render() { - const { fileName, type, implicitFilters, view, currentFile, agent } = this.props; - const inspectButtonText = view === 'extern' ? 'Inspect in FIM' : 'Inspect in Events'; + const { type, implicitFilters, view, currentItem, agent } = this.props; + const id = `${currentItem.name}-${currentItem.cve}-${currentItem.architecture}-${currentItem.version}`; + const inspectButtonText = view === 'extern' ? 'Inspect in Dashboard' : 'Inspect in Events'; return (

Details

@@ -423,32 +322,9 @@ export class FileDetails extends Component { >
{this.getDetails()}
- { (type === 'registry_key' || currentFile.type === 'registry_key') && <> - - -

- Registry values -

- - } - paddingSize="none" - initialIsOpen={true} - > - - - {/* */} - - -
} @@ -485,12 +361,12 @@ export class FileDetails extends Component { initialColumns={[ 'icon', 'timestamp', - 'syscheck.event', + // 'rule.mitre.id', 'rule.description', 'rule.level', 'rule.id', ]} - includeFilters="syscheck" + includeFilters="vulnerability" implicitFilters={implicitFilters} initialFilters={[]} type={type} diff --git a/public/components/agents/vuls/inventory/flyout.tsx b/public/components/agents/vuls/inventory/flyout.tsx index 508329d2c7..eef1a38565 100644 --- a/public/components/agents/vuls/inventory/flyout.tsx +++ b/public/components/agents/vuls/inventory/flyout.tsx @@ -19,21 +19,19 @@ import { EuiLoadingContent, EuiCallOut, } from '@elastic/eui'; -import { WzRequest } from '../../../../react-services/wz-request'; -import { FileDetails } from './fileDetail'; +import { Details } from './detail'; import { AppState } from '../../../../react-services/app-state'; export class FlyoutDetail extends Component { state: { - currentFile: boolean | { [key: string]: string }; + currentItem: boolean | { [key: string]: string }; clusterFilter: {}; isLoading: boolean; error: boolean; - type: 'file' | 'registry_key'; }; props!: { - fileName: string; + vulName: string; agentId: string; view: 'inventory' | 'events' | 'extern'; closeFlyout(): void; @@ -42,11 +40,10 @@ export class FlyoutDetail extends Component { constructor(props) { super(props); this.state = { - currentFile: false, + currentItem: false, clusterFilter: {}, isLoading: true, error: false, - type: 'file', }; } @@ -57,59 +54,38 @@ export class FlyoutDetail extends Component { ? { 'cluster.name': AppState.getClusterInfo().cluster } : { 'manager.name': AppState.getClusterInfo().manager }; this.setState({ clusterFilter }); - let currentFile; - if (typeof this.props.item === 'boolean' && typeof this.props.fileName !== undefined) { - const regex = new RegExp('file=' + '[^&]*'); - const match = window.location.href.match(regex); - if (match && match[0]) { - let file = decodeURIComponent(match[0].split('=')[1]); - const data = await WzRequest.apiReq('GET', `/syscheck/${this.props.agentId}`, { - params: { - q: `file=${file};(type=file,type=registry_key)`, - }, - }); - currentFile = ((((data || {}).data || {}).data || {}).affected_items || [])[0]; - } - } else if (this.props.item) { - currentFile = this.props.item; - } else { - let file = this.props.fileName; - const data = await WzRequest.apiReq('GET', `/syscheck/${this.props.agentId}`, { - params: { - q: `file=${file};(type=file,type=registry_key)`, - }, - }); - currentFile = ((((data || {}).data || {}).data || {}).affected_items || [])[0]; - } - if (!currentFile) { + const currentItem = this.props.item; + + if (!currentItem) { throw false; } - this.setState({ currentFile, type: currentFile.type, isLoading: false }); + this.setState({ currentItem, isLoading: false }); } catch (err) { this.setState({ - error: `Data could not be fetched for ${this.props.fileName}`, - currentFile: { file: this.props.fileName }, + error: `Data could not be fetched for ${this.props.vulName}`, }); } } componentWillUnmount() { - window.location.href = window.location.href.replace(new RegExp('&file=' + '[^&]*', 'g'), ''); } render() { - const { type } = this.state; + const { currentItem } = this.state; + const title = `${currentItem.name} ${currentItem.cve}`; + const id = title.replace(/ /g, '_'); + return ( this.props.closeFlyout()} size="l" - aria-labelledby={this.state.currentFile.file} + aria-labelledby={title} maxWidth="70%" className="wz-inventory wzApp" > -

{this.state.currentFile.file}

+

{title}

{this.state.isLoading && ( @@ -119,15 +95,17 @@ export class FlyoutDetail extends Component { )) || } )} - {this.state.currentFile && !this.state.isLoading && ( + {currentItem && !this.state.isLoading && ( - { - return item.file === file; - }) - } else { - const response = await WzRequest.apiReq('GET', `/vulnerability/${this.props.agent.id}`, { - params: {} - }); - itemData = ((response.data || {}).data || {}).affected_items || []; - } + async showFlyout(item, redirect = false) { //if a flyout is opened, we close it and open a new one, so the components are correctly updated on start. - this.setState({ isFlyoutVisible: false }, () => this.setState({ isFlyoutVisible: true, currentItem: file, syscheckItem: item })); + this.setState({ isFlyoutVisible: false }, () => this.setState({ isFlyoutVisible: true, currentItem: item })); } async componentDidUpdate(prevProps) { @@ -190,10 +171,10 @@ export class InventoryTable extends Component { renderTable() { const getRowProps = item => { - const { itemDetails } = item; + const id = `${item.name}-${item.cve}-${item.architecture}-${item.version}`; return { - 'data-test-subj': `row-${itemDetails}`, - onClick: () => this.showFlyout(itemDetails, item), + 'data-test-subj': `row-${id}`, + onClick: () => this.showFlyout(item), }; }; @@ -242,9 +223,9 @@ export class InventoryTable extends Component { headerZindexLocation="below" onClick={() => this.closeFlyout() } > this.closeFlyout()} type='vulnerability' view='inventory' From 41e69cb4c6e6bb4561f5d1164f765533cd5c215c Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Thu, 11 Mar 2021 13:12:27 -0300 Subject: [PATCH 05/10] Deleted mockup --- public/components/agents/vuls/vuls-mockup.js | 229 ------------------- 1 file changed, 229 deletions(-) delete mode 100644 public/components/agents/vuls/vuls-mockup.js diff --git a/public/components/agents/vuls/vuls-mockup.js b/public/components/agents/vuls/vuls-mockup.js deleted file mode 100644 index 3bac0943aa..0000000000 --- a/public/components/agents/vuls/vuls-mockup.js +++ /dev/null @@ -1,229 +0,0 @@ -export default { - data: { - data: { - affected_items: [ - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29361', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29362', - }, - { - version: '0.23.9-2', - name: 'libp11-kit0', - architecture: 'amd64', - cve: 'CVE-2020-29363', - } - ], - total_affected_items: 18, - total_failed_items: 0, - failed_items: [], - }, - message: 'All selected vulnerabilities were returned', - error: 0, - }, -}; From 9c9aa75caa6297613269cb6d59bb42d3b4ba3a06 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Fri, 12 Mar 2021 17:00:03 -0300 Subject: [PATCH 06/10] Added distinct parameter in field suggestions request --- public/components/agents/vuls/inventory/lib/getFilterValues.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/components/agents/vuls/inventory/lib/getFilterValues.ts b/public/components/agents/vuls/inventory/lib/getFilterValues.ts index e9660596df..59fec3fa85 100644 --- a/public/components/agents/vuls/inventory/lib/getFilterValues.ts +++ b/public/components/agents/vuls/inventory/lib/getFilterValues.ts @@ -15,6 +15,7 @@ export async function getFilterValues(field, value, agentId, filters={}, format= const filter = { ...filters, + distinct: true, select: field, limit: 30, }; From 4d808de412d3c498133bae27d89ca136a17b04bd Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Mon, 5 Apr 2021 11:04:54 +0200 Subject: [PATCH 07/10] feature/vulnerabilities-view-that-affect-each-agent --- public/components/agents/vuls/inventory.tsx | 4 ++-- .../components/agents/vuls/inventory/table.tsx | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index ec305f05f6..3d77759540 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -124,13 +124,13 @@ export class Inventory extends Component { } renderTabs() { - const { isLoading } = this.state; + const { isLoading, totalItems } = this.state; return ( -

Vulnerabilities {isLoading === true && }

+

Vulnerabilities {isLoading === true ? : ({ totalItems })}

diff --git a/public/components/agents/vuls/inventory/table.tsx b/public/components/agents/vuls/inventory/table.tsx index e7f359cd8e..31f18e00e1 100644 --- a/public/components/agents/vuls/inventory/table.tsx +++ b/public/components/agents/vuls/inventory/table.tsx @@ -140,12 +140,6 @@ export class InventoryTable extends Component { let width; (((this.props.agent || {}).os || {}).platform || false) === 'windows' ? width = '60px' : width = '80px'; return [ - { - field: 'name', - name: 'Name', - sortable: true, - width: '100px' - }, { field: 'cve', name: 'CVE', @@ -154,17 +148,23 @@ export class InventoryTable extends Component { width: `${width}` }, { - field: 'architecture', - name: 'Architecture', + field: 'name', + name: 'Name', sortable: true, width: '100px' - }, + }, { field: 'version', name: 'Version', sortable: true, truncateText: true, width: `${width}` + }, + { + field: 'architecture', + name: 'Architecture', + sortable: true, + width: '100px' } ] } From c3f1492d548518e0eabbcd13daa8215855684d60 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Mon, 5 Apr 2021 12:18:05 +0200 Subject: [PATCH 08/10] Deleted empty object with no use --- public/components/agents/vuls/inventory.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index 3d77759540..9c625f9beb 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -158,7 +158,7 @@ export class Inventory extends Component { this.showToast('success', 'Your download should begin automatically...', 3000); await exportCsv( '/vulnerability/' + this.props.agent.id, - [{}, + [ ...formatedFilters ], `vuls-vulnerabilities` From c0f95b4ce115137dfd84cc3c270db6435c78e534 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Mon, 5 Apr 2021 16:34:15 +0200 Subject: [PATCH 09/10] Disables vulnerabilities export csv with no records available --- public/components/agents/vuls/inventory.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/components/agents/vuls/inventory.tsx b/public/components/agents/vuls/inventory.tsx index 9c625f9beb..0044d09979 100644 --- a/public/components/agents/vuls/inventory.tsx +++ b/public/components/agents/vuls/inventory.tsx @@ -123,7 +123,7 @@ export class Inventory extends Component { } } - renderTabs() { + renderTitle() { const { isLoading, totalItems } = this.state; return ( @@ -134,7 +134,7 @@ export class Inventory extends Component { - this.downloadCsv()}> + this.downloadCsv()}> Export formatted @@ -205,11 +205,11 @@ export class Inventory extends Component { return this.loadingInventory() } const table = this.renderTable(); - const tabs = this.renderTabs(); + const title = this.renderTitle(); return - {tabs} + {title} {table} From e5770586779968e0e10b402a13e84946984ccd9e Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Mon, 5 Apr 2021 18:55:51 +0200 Subject: [PATCH 10/10] Code Cleaning --- public/components/agents/vuls/inventory/detail.tsx | 1 - public/components/agents/vuls/inventory/flyout.tsx | 3 --- 2 files changed, 4 deletions(-) diff --git a/public/components/agents/vuls/inventory/detail.tsx b/public/components/agents/vuls/inventory/detail.tsx index 997bb170f0..68d3d8b532 100644 --- a/public/components/agents/vuls/inventory/detail.tsx +++ b/public/components/agents/vuls/inventory/detail.tsx @@ -361,7 +361,6 @@ export class Details extends Component { initialColumns={[ 'icon', 'timestamp', - // 'rule.mitre.id', 'rule.description', 'rule.level', 'rule.id', diff --git a/public/components/agents/vuls/inventory/flyout.tsx b/public/components/agents/vuls/inventory/flyout.tsx index eef1a38565..32bb343111 100644 --- a/public/components/agents/vuls/inventory/flyout.tsx +++ b/public/components/agents/vuls/inventory/flyout.tsx @@ -67,9 +67,6 @@ export class FlyoutDetail extends Component { } } - componentWillUnmount() { - } - render() { const { currentItem } = this.state; const title = `${currentItem.name} ${currentItem.cve}`;