diff --git a/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx b/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx index 5f1d8567fb..7a9ee0d576 100644 --- a/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx +++ b/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx @@ -1,17 +1,38 @@ import './loading-spinner.scss'; import React from 'react'; -import { EuiTitle, EuiPanel, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiTitle, + EuiPanel, + EuiEmptyPrompt, + EuiLoadingSpinner, +} from '@elastic/eui'; import { FormattedMessage } from '@osd/i18n/react'; -export function LoadingSpinner() { +interface LoadingSpinner { + message?: React.ReactNode; +} + +export function LoadingSpinner({ message }: LoadingSpinner) { return ( - + } + icon={} title={ - +

- + {message ? ( + message + ) : ( + + )}

} diff --git a/plugins/main/public/components/index.js b/plugins/main/public/components/index.js index e547529f37..38527e0751 100644 --- a/plugins/main/public/components/index.js +++ b/plugins/main/public/components/index.js @@ -15,8 +15,6 @@ import { WzVisualize } from './visualize/wz-visualize'; import { WzMenuWrapper } from '../components/wz-menu/wz-menu-wrapper'; import { WzAgentSelectorWrapper } from '../components/wz-agent-selector/wz-agent-selector-wrapper'; import { WzBlankScreen } from '../components/wz-blank-screen/wz-blank-screen'; -import { ClusterDisabled } from '../components/management/cluster/cluster-disabled'; -import { ClusterTimelions } from '../components/management/cluster/cluster-timelions'; import { KibanaVisWrapper } from '../components/management/cluster/cluster-visualization'; import { ToastNotificationsModal } from '../components/notifications/modal'; import { getAngularModule } from '../kibana-services'; @@ -29,8 +27,6 @@ app.value('WzVisualize', WzVisualize); app.value('WzMenuWrapper', WzMenuWrapper); app.value('WzAgentSelectorWrapper', WzAgentSelectorWrapper); app.value('WzBlankScreen', WzBlankScreen); -app.value('ClusterDisabled', ClusterDisabled); -app.value('ClusterTimelions', ClusterTimelions); app.value('KibanaVisualization', KibanaVisWrapper); app.value('ToastNotificationsModal', ToastNotificationsModal); app.value('WzUpdatesNotification', WzUpdatesNotification); diff --git a/plugins/main/public/components/management/cluster/cluster-disabled.js b/plugins/main/public/components/management/cluster/cluster-disabled.js index 396814e9b1..e980c9b7fd 100644 --- a/plugins/main/public/components/management/cluster/cluster-disabled.js +++ b/plugins/main/public/components/management/cluster/cluster-disabled.js @@ -2,47 +2,54 @@ import React, { Component, Fragment } from 'react'; import { EuiLink, EuiEmptyPrompt } from '@elastic/eui'; import { withErrorBoundary } from '../../common/hocs'; import { webDocumentationLink } from '../../../../common/services/web_documentation'; -export const ClusterDisabled = withErrorBoundary (class ClusterDisabled extends Component { - constructor(props) { - super(props); - this.state = {}; - } +export const ClusterDisabled = withErrorBoundary( + class ClusterDisabled extends Component { + constructor(props) { + super(props); + this.state = {}; + } - render() { - return ( - - {!this.props.enabled - ? 'The cluster is disabled' - : !this.props.running - ? 'The cluster is not running' - : ''} - - } - body={ - - {!this.props.enabled && ( -

- Visit the documentation on{' '} - - this link - {' '} - to learn about how to enable it. -

- )} - {!this.props.running && ( -

The cluster is enabled but it is not running.

- )} -
- } - /> - ); - } -}) + render() { + return ( + + {this.props.error + ? this.props.error.title + : !this.props.enabled + ? 'The cluster is disabled' + : !this.props.running + ? 'The cluster is not running' + : ''} + + } + body={ + + {this.props.error ? ( + this.props.error.message + ) : !this.props.enabled ? ( +

+ Visit the documentation on{' '} + + this link + {' '} + to learn about how to enable it. +

+ ) : !this.props.running ? ( +

The cluster is enabled but it is not running.

+ ) : null} +
+ } + /> + ); + } + }, +); diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx new file mode 100644 index 0000000000..d76fd15081 --- /dev/null +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -0,0 +1,173 @@ +import React from 'react'; +import { + EuiFlexItem, + EuiButtonIcon, + EuiCard, + EuiDescriptionList, + EuiSpacer, + EuiToolTip, + EuiFlexGroup, + EuiTitle, +} from '@elastic/eui'; +import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; +import { getPlugins } from '../../../../kibana-services'; +import { DiscoverNoResults } from '../../../common/no-results/no-results'; +import { getDashboardConfigurationPanels } from '../dashboard/dashboard_configuration_panels'; +import '../dashboard/cluster_dashboard.scss'; +import { tFilter } from '../../../common/data-source'; + +interface ConfigurationCardsProps { + goBack: () => void; + configuration: any; + searchBarProps: any; + results: any; + indexPatternId?: string; + filters: tFilter[]; +} + +const plugins = getPlugins(); + +const DashboardByRenderer = plugins.dashboard.DashboardContainerByValueRenderer; + +export const ConfigurationCards = ({ + goBack, + configuration, + searchBarProps, + results, + indexPatternId, + filters, +}: ConfigurationCardsProps) => { + const configurationItemsList = [ + { + title: 'Disabled', + description: String(configuration?.disabled), + }, + { + title: 'Hidden', + description: String(configuration?.hidden), + }, + { + title: 'Name', + description: configuration?.name, + }, + { + title: 'Node name', + description: configuration?.node_name, + }, + { + title: 'Node type', + description: configuration?.node_type, + }, + { + title: 'Bind address', + description: configuration?.bind_addr, + }, + { + title: 'IP', + description: configuration?.nodes[0] || '-', + }, + { + title: 'Port', + description: configuration?.port, + }, + ]; + + return ( + + + + + + + + + + +

Overview

+
+
+
+
+ + + + {results?.hits?.total > 0 ? ( + + ) : ( + + )} + + + +

Configuration

+
+ } + > + + ({ + title: ( + + {item.title} + + ), + description: item.description, + }))} + titleProps={{ + className: 'cluster-descriptionList-title', + }} + descriptionProps={{ + className: 'color-grey cluster-descriptionList-description', + }} + /> + + + + + + ); +}; diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx new file mode 100644 index 0000000000..9e8874add2 --- /dev/null +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -0,0 +1,237 @@ +import React from 'react'; +import { + EuiFlexItem, + EuiButtonEmpty, + EuiCard, + EuiDescriptionList, + EuiSpacer, + EuiToolTip, + EuiFlexGroup, + EuiTitle, +} from '@elastic/eui'; +import { getDashboardPanels } from '../dashboard/dashboard_panels'; +import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; +import '../dashboard/cluster_dashboard.scss'; +import { getPlugins } from '../../../../kibana-services'; +import { DiscoverNoResults } from '../../../common/no-results/no-results'; +import { tFilter } from '../../../common/data-source'; + +interface OverviewCardsProps { + goAgents: () => void; + goNodes: () => void; + goConfiguration: () => void; + configuration: any; + status: any; + version: any; + nodesCount: number; + nodeList: any[]; + agentsCount: number; + searchBarProps: any; + results: any; + indexPatternId: string; + clusterName?: string; + filters: tFilter[]; +} + +const plugins = getPlugins(); + +const DashboardByRenderer = plugins.dashboard.DashboardContainerByValueRenderer; + +export const OverviewCards = ({ + goAgents, + goNodes, + goConfiguration, + configuration, + status, + version, + nodesCount, + nodeList, + agentsCount, + searchBarProps, + results, + indexPatternId, + clusterName, + filters, +}: OverviewCardsProps) => { + return ( + <> + + + + + +

Details

+
+
+ + + View Overview + + +
+ } + > + + + + + + + Information + + } + > + + + + Nodes + + + ), + description: ( + + + {nodesCount} + + + ), + }, + { + title: ( + + + Agents + + + ), + description: ( + + + {agentsCount} + + + ), + }, + ]} + titleProps={{ + className: 'cluster-descriptionList-title', + }} + descriptionProps={{ + className: 'cluster-descriptionList-description', + }} + /> + + + + {results?.hits?.total > 0 ? ( +
+ +
+ ) : ( + + )} + + ); +}; diff --git a/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss b/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss new file mode 100644 index 0000000000..ccb47c4acb --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss @@ -0,0 +1,21 @@ +.ct-dashboard-responsive { + margin: 0 -8px; + @media (max-width: 767px) { + .react-grid-layout { + height: auto !important; + } + .dshLayout-isMaximizedPanel { + height: 100% !important; + } + } +} + +.cluster-descriptionList-title { + width: 25% !important; + margin-top: 8px !important; +} + +.cluster-descriptionList-description { + width: 75% !important; + margin-top: 8px !important; +} diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx new file mode 100644 index 0000000000..0db6f747c7 --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -0,0 +1,211 @@ +import React, { useState, useEffect } from 'react'; +import { getCore, getPlugins } from '../../../../kibana-services'; +import { SearchResponse } from '../../../../../../../src/core/server'; +import { IndexPattern } from '../../../../../../../src/plugins/data/common'; +import { I18nProvider } from '@osd/i18n/react'; +import useSearchBar from '../../../common/search-bar/use-search-bar'; +import { + ErrorFactory, + ErrorHandler, + HttpError, +} from '../../../../react-services/error-management'; +import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { withErrorBoundary } from '../../../common/hocs/error-boundary/with-error-boundary'; +import { EuiSpacer, EuiFlexItem } from '@elastic/eui'; + +import { OverviewCards } from '../components/overview_cards'; +import { endpointSummary } from '../../../../utils/applications'; +import { WzRequest } from '../../../../react-services'; +import { ConfigurationCards } from '../components/configuration_cards'; +import { NodeList } from '../node-list'; +import { + AlertsDataSource, + AlertsDataSourceRepository, + PatternDataSource, + tParsedIndexPattern, + useDataSource, +} from '../../../common/data-source'; + +const SearchBar = getPlugins().data.ui.SearchBar; + +interface DashboardCTProps { + statusRunning: string; +} + +interface ClusterDashboardState { + showConfig: boolean; + showNodes: boolean; + nodeList: any; + configuration: any; + version: any; + nodesCount: number; + agentsCount: number; +} + +const DashboardCT: React.FC = ({ statusRunning }) => { + const { + filters, + dataSource, + fetchFilters, + isLoading: isDataSourceLoading, + fetchData, + setFilters, + } = useDataSource({ + DataSource: AlertsDataSource, + repository: new AlertsDataSourceRepository(), + }); + + const [state, setState] = useState({ + showConfig: false, + showNodes: false, + nodeList: [], + configuration: undefined, + version: undefined, + nodesCount: 0, + agentsCount: 0, + }); + + const [results, setResults] = useState({} as SearchResponse); + + const { searchBarProps } = useSearchBar({ + indexPattern: dataSource?.indexPattern as IndexPattern, + filters, + setFilters, + }); + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; + + useEffect(() => { + if (isDataSourceLoading) { + return; + } + fetchData({ + query, + dateRange: { + from: dateRangeFrom, + to: dateRangeTo, + }, + }) + .then(results => { + setResults(results); + }) + .catch(error => { + const searchError = ErrorFactory.create(HttpError, { + error, + message: 'Error fetching vulnerabilities', + }); + ErrorHandler.handleError(searchError); + }); + }, [ + JSON.stringify(fetchFilters), + JSON.stringify(query), + dateRangeFrom, + dateRangeTo, + ]); + + const setBooleans = (component: string | null) => { + setState({ + ...state, + showConfig: component === 'showConfig', + showNodes: component === 'showNodes', + }); + }; + + const goAgents = () => { + getCore().application.navigateToApp(endpointSummary.id, { + path: '#/agents-preview', + }); + }; + + const goBack = () => { + setBooleans(null); + }; + + const goNodes = () => { + setBooleans('showNodes'); + }; + + const goConfiguration = () => { + setBooleans('showConfig'); + }; + + useEffect(() => { + const getData = async () => { + const data = await Promise.all([ + WzRequest.apiReq('GET', '/cluster/nodes', {}), + WzRequest.apiReq('GET', '/cluster/local/config', {}), + WzRequest.apiReq('GET', '/', {}), + WzRequest.apiReq('GET', '/agents', { limit: 1 }), + WzRequest.apiReq('GET', '/cluster/healthcheck', {}), + ]); + + const nodeList = data[0]?.data?.data || {} || false; + const clusterConfig = data[1]?.data?.data || {} || false; + const agents = data[3]?.data?.data || {} || false; + setState({ + ...state, + configuration: clusterConfig.affected_items[0], + version: data[2]?.data?.data?.api_version || false, + nodesCount: nodeList.total_affected_items, + agentsCount: agents.total_affected_items - 1, + nodeList: nodeList?.affected_items ?? [], + }); + }; + + getData(); + }, []); + + return ( + + + {isDataSourceLoading && !dataSource ? ( + + ) : !state.showNodes ? ( +
+ +
+ ) : null} + + {!isDataSourceLoading && + dataSource && + !state.showConfig && + !state.showNodes ? ( + + ) : null} + {state.showConfig ? ( + + ) : null} + {state.showNodes ? : null} +
+
+ ); +}; + +export const ClusterDashboard = withErrorBoundary(DashboardCT); diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts new file mode 100644 index 0000000000..81459af1cb --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts @@ -0,0 +1,86 @@ +import { DashboardPanelState } from '../../../../../../../../src/plugins/dashboard/public/application'; +import { EmbeddableInput } from '../../../../../../../../src/plugins/embeddable/public'; + +/* WARNING: The panel id must be unique including general and agents visualizations. Otherwise, the visualizations will not refresh when we pin an agent, because they are cached by id */ + +/* Overview visualizations */ + +const getVisStateTop5Nodes = (indexPatternId?: string) => { + return { + id: 'Wazuh-App-Cluster-monitoring-Overview-Node-Pie', + title: 'Top 5 nodes', + type: 'pie', + params: { + type: 'pie', + addTooltip: true, + addLegend: true, + legendPosition: 'right', + isDonut: true, + labels: { show: false, values: true, last_level: true, truncate: 100 }, + }, + uiState: { spy: { mode: { name: 'table' } } }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [ + { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, + { + id: '2', + enabled: true, + type: 'terms', + schema: 'segment', + params: { + field: 'cluster.node', + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + size: 5, + order: 'desc', + orderBy: '1', + }, + }, + ], + }, + }; +}; + +/* Definition of panels */ + +export const getDashboardConfigurationPanels = ( + indexPatternId?: string, +): { + [panelId: string]: DashboardPanelState< + EmbeddableInput & { [k: string]: unknown } + >; +} => { + return { + '1': { + gridData: { + w: 48, + h: 13, + x: 0, + y: 0, + i: '1', + }, + type: 'visualization', + explicitInput: { + id: '1', + savedVis: getVisStateTop5Nodes(indexPatternId), + }, + }, + }; +}; diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts b/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts new file mode 100644 index 0000000000..c4871aca44 --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts @@ -0,0 +1,122 @@ +import { DashboardPanelState } from '../../../../../../../../src/plugins/dashboard/public/application'; +import { EmbeddableInput } from '../../../../../../../../src/plugins/embeddable/public'; + +/* WARNING: The panel id must be unique including general and agents visualizations. Otherwise, the visualizations will not refresh when we pin an agent, because they are cached by id */ + +/* Overview visualizations */ + +const getVisStateClusterAlertsSummary = ( + indexPatternId: string, + clusterName?: string, +) => { + let expression = `.es(index=${indexPatternId},q="cluster.name: ${clusterName}").label("${clusterName} cluster")`; + expression = expression.replace(/'/g, '"'); + return { + id: 'Wazuh-App-Cluster-monitoring-Overview-Manager', + title: 'Cluster alerts summary', + type: 'timelion', + params: { expression, interval: 'auto' }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [], + }, + }; +}; + +const getVisStateAlertsByNodeSummary = ( + indexPatternId: string, + nodeList: any[], + clusterName?: string, +) => { + let expression = ''; + for (const node of nodeList) { + expression += `.es(index=${indexPatternId},q="cluster.name: ${clusterName} AND cluster.node: ${node.name}").label("${node.name}"),`; + } + expression = expression.substring(0, expression.length - 1); + expression = expression.replace(/'/g, '"'); + return { + id: 'Wazuh-App-Cluster-monitoring-Overview', + title: 'Alerts by node summary', + type: 'timelion', + params: { expression, interval: 'auto' }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [], + }, + }; +}; + +/* Definition of panels */ + +export const getDashboardPanels = ( + indexPatternId: string, + nodeList: any[], + clusterName?: string, +): { + [panelId: string]: DashboardPanelState< + EmbeddableInput & { [k: string]: unknown } + >; +} => { + return { + '1': { + gridData: { + w: 24, + h: 13, + x: 0, + y: 0, + i: '1', + }, + type: 'visualization', + explicitInput: { + id: '1', + savedVis: getVisStateClusterAlertsSummary(indexPatternId, clusterName), + }, + }, + '2': { + gridData: { + w: 24, + h: 13, + x: 24, + y: 0, + i: '2', + }, + type: 'visualization', + explicitInput: { + id: '2', + savedVis: getVisStateAlertsByNodeSummary( + indexPatternId, + nodeList, + clusterName, + ), + }, + }, + }; +}; diff --git a/plugins/main/public/components/management/cluster/node-list.tsx b/plugins/main/public/components/management/cluster/node-list.tsx index d4e37c834b..034a0932c4 100644 --- a/plugins/main/public/components/management/cluster/node-list.tsx +++ b/plugins/main/public/components/management/cluster/node-list.tsx @@ -58,27 +58,23 @@ export const NodeList = withErrorBoundary( render() { return ( - - - - - - this.props.goBack()} - /> - - - - -

Cluster nodes

-
-
-
+ + + + this.props.goBack()} + /> + + + + +

Cluster nodes

+
diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx new file mode 100644 index 0000000000..1c2c064016 --- /dev/null +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { compose } from 'redux'; +import { + withErrorBoundary, + withGlobalBreadcrumb, + withGuardAsync, + withReduxProvider, + withUserAuthorizationPrompt, +} from '../../../../../components/common/hocs'; +import { cluster } from '../../../../../utils/applications'; +import { WzRequest } from '../../../../../react-services'; +import { ClusterDisabled } from '../../../../../components/management/cluster/cluster-disabled'; +import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; +import { LoadingSpinner } from '../../../../../components/common/loading-spinner/loading-spinner'; +import { FormattedMessage } from '@osd/i18n/react'; + +interface ClusterOverviewState { + clusterEnabled: boolean; + isClusterRunning: boolean; + statusRunning: string; +} + +const checkClusterIsEnabledAndRunning = async () => { + try { + const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); + const clusterEnabled = status?.data?.data?.enabled; + const isClusterEnabled = clusterEnabled === 'yes'; + const statusRunning = status?.data?.data?.running; + const isClusterRunning = statusRunning === 'yes'; + return { + ok: !(isClusterEnabled && isClusterRunning), + data: { + clusterEnabled, + isClusterRunning, + statusRunning, + }, + }; + } catch (error) { + return { + ok: true, + data: { + error: { title: 'There was a problem', message: error.message }, + }, + }; + } +}; + +export const ClusterOverview = compose( + withErrorBoundary, + withReduxProvider, + withGlobalBreadcrumb([{ text: cluster.breadcrumbLabel }]), + withUserAuthorizationPrompt([ + { action: 'cluster:status', resource: '*:*:*' }, + ]), + withGuardAsync( + checkClusterIsEnabledAndRunning, + ({ clusterEnabled, isClusterRunning, error }) => ( + + ), + () => ( + + } + /> + ), + ), +)( + ({ + clusterEnabled, + isClusterRunning, + statusRunning, + }: ClusterOverviewState) => { + return ( + <> + {clusterEnabled && isClusterRunning ? ( + + ) : null} + + ); + }, +); diff --git a/plugins/main/public/controllers/management/components/management/management-main.js b/plugins/main/public/controllers/management/components/management/management-main.js index 3a6e5982b7..573b88c8c2 100644 --- a/plugins/main/public/controllers/management/components/management/management-main.js +++ b/plugins/main/public/controllers/management/components/management/management-main.js @@ -27,6 +27,7 @@ import { SECTION_DECODERS_SECTION, SECTION_RULES_SECTION, } from './common/constants'; +import { ClusterOverview } from './cluster/cluster-overview'; class WzManagementMain extends Component { constructor(props) { @@ -41,6 +42,7 @@ class WzManagementMain extends Component { {(section === 'groups' && ) || (section === 'status' && ) || + (section === 'monitoring' && ) || (section === 'reporting' && ) || (section === 'statistics' && ) || (section === 'logs' && ) || diff --git a/plugins/main/public/controllers/management/index.js b/plugins/main/public/controllers/management/index.js index f879da31d1..79281a2010 100644 --- a/plugins/main/public/controllers/management/index.js +++ b/plugins/main/public/controllers/management/index.js @@ -11,7 +11,6 @@ */ import { ManagementController } from './management'; -import { ClusterController } from './monitoring'; import WzManagement from './components/management/management-provider'; import WzManagementConfiguration from './components/management/configuration/configuration-main'; import { getAngularModule } from '../../kibana-services'; @@ -23,6 +22,5 @@ WzManagementConfiguration.displayName = 'WzManagementConfiguration'; app .controller('managementController', ManagementController) - .controller('clusterController', ClusterController) .value('WzManagement', WzManagement) .value('WzManagementConfiguration', WzManagementConfiguration); diff --git a/plugins/main/public/controllers/management/monitoring.js b/plugins/main/public/controllers/management/monitoring.js deleted file mode 100644 index 4c0f1f04a9..0000000000 --- a/plugins/main/public/controllers/management/monitoring.js +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Wazuh app - Cluster monitoring controller - * Copyright (C) 2015-2022 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 { FilterHandler } from '../../utils/filter-handler'; -import { AppState } from '../../react-services/app-state'; -import { GenericRequest } from '../../react-services/generic-request'; -import { WzRequest } from '../../react-services/wz-request'; -import { ErrorHandler } from '../../react-services/error-handler'; -import { TabVisualizations } from '../../factories/tab-visualizations'; -import store from '../../redux/store'; -import { updateGlobalBreadcrumb } from '../../redux/actions/globalBreadcrumbActions'; -import { ModulesHelper } from '../../components/common/modules/modules-helper'; -import { getCore, getDataPlugin } from '../../kibana-services'; -import { cluster, endpointSummary } from '../../utils/applications'; - -export function ClusterController( - $scope, - $rootScope, - $timeout, - errorHandler, - $window, - $location, - discoverPendingUpdates, - rawVisualizations, - loadedVisualizations, - visHandlers, -) { - const tabVisualizations = new TabVisualizations(); - getDataPlugin().query.timefilter.timefilter.setRefreshInterval({ - pause: true, - value: 0, - }); - $scope.search = term => { - $scope.$broadcast('wazuhSearch', { term }); - }; - - const clusterEnabled = - AppState.getClusterInfo() && AppState.getClusterInfo().status === 'enabled'; - $scope.isClusterEnabled = clusterEnabled; - $scope.isClusterRunning = true; - $scope.authorized = true; - $location.search('tabView', 'cluster-monitoring'); - $location.search('tab', 'monitoring'); - $location.search('_a', null); - const filterHandler = new FilterHandler(AppState.getCurrentPattern()); - discoverPendingUpdates.removeAll(); - tabVisualizations.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - tabVisualizations.setTab('monitoring'); - tabVisualizations.assign({ - monitoring: 2, - }); - - $scope.loading = true; - $scope.showConfig = false; - $scope.showNodes = false; - $scope.currentNode = null; - $scope.nodeSearchTerm = ''; - - /** - * This set default boolean flags for a given component - * @param {String} component - */ - const setBooleans = component => { - $scope.showConfig = component === 'showConfig'; - $scope.showNodes = component === 'showNodes'; - $scope.currentNode = null; - }; - - /** - * This navigates to agents preview - */ - $scope.goAgents = () => { - getCore().application.navigateToApp(endpointSummary.id, { - path: '#/agents-preview', - }); - }; - - /** - * This navigates to configuration - */ - $scope.goConfiguration = () => { - setBooleans('showConfig'); - tabVisualizations.assign({ - monitoring: 1, - }); - assignFilters(); - $rootScope.$broadcast('updateVis'); - }; - - /** - * This navigates to nodes - */ - $scope.goNodes = () => { - setBooleans('showNodes'); - tabVisualizations.assign({ - monitoring: 1, - }); - assignFilters(); - $scope.nodeProps = { goBack: () => $scope.goBack() }; - $rootScope.$broadcast('updateVis'); - }; - - /** - * This navigates back - */ - $scope.goBack = () => { - setBooleans(null); - tabVisualizations.assign({ - monitoring: 2, - }); - assignFilters(); - $rootScope.$broadcast('updateVis'); - }; - - //listeners - $scope.$on('wazuhShowClusterNode', async (event, parameters) => { - try { - tabVisualizations.assign({ - monitoring: 1, - }); - $scope.currentNode = parameters.node; - const data = await WzRequest.apiReq('GET', '/cluster/healthcheck', { - node: $scope.currentNode.name, - }); - - $scope.currentNode.healthCheck = data.data.data.affected_items[0]; - - if ( - $scope.currentNode.healthCheck && - $scope.currentNode.healthCheck.status - ) { - $scope.currentNode.healthCheck.status.last_sync_integrity.duration = - 'n/a'; - $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = - 'n/a'; - $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = - 'n/a'; - - if ( - $scope.currentNode.healthCheck.status.last_sync_integrity - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_integrity - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_integrity.duration = `${ - (end - start) / 1000 - }s`; - } - - if ( - $scope.currentNode.healthCheck.status.last_sync_agentinfo - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_agentinfo - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = `${ - (end - start) / 1000 - }s`; - } - - if ( - $scope.currentNode.healthCheck.status.last_sync_agentgroups - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_agentgroups - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = `${ - (end - start) / 1000 - }s`; - } - } - - assignFilters($scope.currentNode.name); - $rootScope.$broadcast('updateVis'); - - $scope.$applyAsync(); - } catch (error) { - ErrorHandler.handle(error, 'Cluster'); - } - }); - - let filters = []; - /** - * This creatie custom filters for visualizations for a given node - * @param {Object} node - */ - const assignFilters = async (node = false) => { - try { - filters = []; - filters.push( - filterHandler.managerQuery(AppState.getClusterInfo().cluster, true), - ); - if (node) { - filters.push(filterHandler.nodeQuery(node)); - } - const discoverScope = await ModulesHelper.getDiscoverScope(); - discoverScope.loadFilters(filters); - } catch (error) { - ErrorHandler.handle( - 'An error occurred while creating custom filters for visualizations', - 'Cluster', - { warning: true }, - ); - } - }; - - const clusterStatus = async () => { - try { - const status = await WzRequest.apiReq('GET', '/cluster/status', {}); - $scope.authorized = true; - return status; - } catch (error) { - if (error === '3013 - Permission denied: Resource type: *:*') - $scope.authorized = false; - } - }; - - /** - * This set some required settings at init - */ - const load = async () => { - try { - visHandlers.removeAll(); - discoverPendingUpdates.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - const status = await clusterStatus(); - if (!status) { - $scope.permissions = [{ action: 'cluster:status', resource: '*:*:*' }]; - $scope.loading = false; - $scope.$applyAsync(); - return; - } - $scope.status = status.data.data.running; - if ($scope.status === 'no') { - $scope.isClusterRunning = false; - $scope.loading = false; - return; - } - - const data = await Promise.all([ - WzRequest.apiReq('GET', '/cluster/nodes', {}), - WzRequest.apiReq('GET', '/cluster/local/config', {}), - WzRequest.apiReq('GET', '/', {}), - WzRequest.apiReq('GET', '/agents', { limit: 1 }), - WzRequest.apiReq('GET', '/cluster/healthcheck', {}), - ]); - - const nodeList = ((data[0] || {}).data || {}).data || {} || false; - const clusterConfig = ((data[1] || {}).data || {}).data || {} || false; - const version = - (((data[2] || {}).data || {}).data || {}).api_version || false; - const agents = ((data[3] || {}).data || {}).data || {} || false; - - $scope.nodesCount = nodeList.total_affected_items; - $scope.configuration = clusterConfig.affected_items[0]; - $scope.version = version; - $scope.agentsCount = agents.total_affected_items - 1; - - nodeList.name = $scope.configuration.name; - nodeList.master_node = $scope.configuration.node_name; - const { id, title } = await getDataPlugin().indexPatterns.get( - AppState.getCurrentPattern(), - ); - - const visData = await GenericRequest.request( - 'POST', - `/elastic/visualizations/cluster-monitoring/${AppState.getCurrentPattern()}`, - { nodes: nodeList, pattern: { id, title } }, - ); - - rawVisualizations.assignItems(visData.data.raw); - assignFilters(); - $rootScope.$broadcast('updateVis'); - - $scope.loading = false; - $scope.$applyAsync(); - return; - } catch (error) { - $scope.loading = false; - ErrorHandler.handle(error, 'Cluster'); - } - }; - - $scope.falseAllExpand = () => { - $scope.expandArray = [false, false]; - }; - - $scope.expand = i => { - const oldValue = $scope.expandArray[i]; - $scope.falseAllExpand(); - $scope.expandArray[i] = !oldValue; - }; - - $scope.expandArray = [false, false]; - - const breadcrumb = [{ text: cluster.breadcrumbLabel }]; - store.dispatch(updateGlobalBreadcrumb(breadcrumb)); - if (clusterEnabled) load(); - - //listeners - $scope.$on('$destroy', () => { - discoverPendingUpdates.removeAll(); - tabVisualizations.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - visHandlers.removeAll(); - }); -} diff --git a/plugins/main/public/templates/management/management.html b/plugins/main/public/templates/management/management.html index efa961dc3a..4d69103378 100644 --- a/plugins/main/public/templates/management/management.html +++ b/plugins/main/public/templates/management/management.html @@ -30,515 +30,6 @@ - -
- -
- -
- - -
- -
- - -
-
- -
- - - - - - -
-
- - - - - - -
-
{{loadingStatus}}
-
-
- -
- - -
- - - -
-
- Details  - - -
-
- IP address - {{configuration.nodes[0] || '-'}} -
-
- Running - {{ status || 'no' }} -
-
- Version - {{version}} -
-
-
- - - -
-
- Information -
- -
- Nodes - {{nodesCount}} -
- -
- Agents - {{agentsCount}} -
-
-
- -
- - - -
-
-
- - There are no results for selected time range. Try another - one. -
-
-
- - - -
- -
- - - - - -
-
-
-

- - Overview -

-
-
- -
- -
- - - - - - - - Top 5 nodes - - - There are no results for selected time range. Try another - one. - -
- -
-
- Configuration -
- -
- Disabled - {{configuration.disabled}} -
-
- Hidden - {{configuration.hidden}} -
-
- Name - {{configuration.name}} -
-
- Node name - {{configuration.node_name}} -
-
- Node type - {{configuration.node_type}} -
-
- Bind address - {{configuration.bind_addr}} -
-
- IP - {{configuration.nodes[0] || '-'}} -
-
- Port - {{configuration.port}} -
-
-
-
-
- - - -
- -
- - -
- -
-
-

- - Node {{ currentNode.name }} -

-
-
- -
-
- - {{ currentNode.name }} alerts summary - - -
-
- - - -
- -
-
- Node information -
-
- IP - {{currentNode.healthCheck.info.ip}} -
-
- Version - {{currentNode.healthCheck.info.version}} -
-
- Type - {{currentNode.healthCheck.info.type}} -
-
- Name - {{currentNode.healthCheck.info.name}} -
-
- Active agents - {{ currentNode.healthCheck.info.n_active_agents }} -
-
-
- - -
-
- Last files integrity synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_integrity.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_integrity.duration}} -
-
- Total shared files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.shared}} -
-
- Total missing files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.missing}} -
-
- Total extra but valid files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.extra_valid}} -
-
- Total extra files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.extra}} -
-
-
-
- - - -
- -
-
- Last agents information synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_agentinfo.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_agentinfo.duration}} -
-
- Total agent info - {{currentNode.healthCheck.status.last_sync_agentinfo.total_agentinfo}} -
-
-
- - -
-
- Last agents groups synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_agentgroups.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_agentgroups.duration}} -
-
- Total agent info - {{currentNode.healthCheck.status.last_sync_agentgroups.total_agentgroups}} -
-
-
-
- -
- -
- -
diff --git a/plugins/main/server/integration-files/visualizations/cluster/index.ts b/plugins/main/server/integration-files/visualizations/cluster/index.ts index 92c5a937bf..de48bede22 100644 --- a/plugins/main/server/integration-files/visualizations/cluster/index.ts +++ b/plugins/main/server/integration-files/visualizations/cluster/index.ts @@ -9,8 +9,6 @@ * * Find more information about this on the LICENSE file. */ -import monitoring from './monitoring'; import statistics from './statistics'; -export { monitoring}; -export { statistics } +export { statistics }; diff --git a/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts b/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts deleted file mode 100644 index d6a0ad9cca..0000000000 --- a/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Wazuh app - Cluster monitoring visualizations - * Copyright (C) 2015-2022 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 default [ - { - _id: 'Wazuh-App-Cluster-monitoring-Overview', - _type: 'visualization', - _source: { - title: 'App Cluster Overview', - visState: JSON.stringify({ - title: 'App Cluster Overview', - type: 'timelion', - params: { expression: '.es(*)', interval: 'auto' }, - aggs: [], - }), - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Manager', - _type: 'visualization', - _source: { - title: 'App Cluster Overview Manager', - visState: JSON.stringify({ - title: 'App Cluster Overview Manager', - type: 'timelion', - params: { expression: '.es(q=agent.id:000)', interval: 'auto' }, - aggs: [], - }), - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Node', - _source: { - title: 'App Cluster Overview Node', - visState: JSON.stringify({ - title: 'App Cluster Overview Node', - type: 'histogram', - params: { - type: 'histogram', - grid: { categoryLines: false, style: { color: '#eee' } }, - categoryAxes: [ - { - id: 'CategoryAxis-1', - type: 'category', - position: 'bottom', - show: true, - style: {}, - scale: { type: 'linear' }, - labels: { show: true, filter: true, truncate: 100 }, - title: {}, - }, - ], - valueAxes: [ - { - id: 'ValueAxis-1', - name: 'LeftAxis-1', - type: 'value', - position: 'left', - show: true, - style: {}, - scale: { type: 'linear', mode: 'normal' }, - labels: { show: true, rotate: 0, filter: false, truncate: 100 }, - title: { text: 'Count' }, - }, - ], - seriesParams: [ - { - show: 'true', - type: 'histogram', - mode: 'stacked', - data: { label: 'Count', id: '1' }, - valueAxis: 'ValueAxis-1', - drawLinesBetweenPoints: true, - showCircles: true, - }, - ], - addTooltip: true, - addLegend: false, - legendPosition: 'right', - times: [], - addTimeMarker: false, - }, - aggs: [ - { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, - { - id: '2', - enabled: true, - type: 'date_histogram', - schema: 'segment', - params: { - field: 'timestamp', - interval: 'auto', - customInterval: '2h', - min_doc_count: 1, - extended_bounds: {}, - }, - }, - ], - }), - uiStateJSON: JSON.stringify({ - spy: { mode: { name: null, fill: false } }, - vis: { legendOpen: false }, - }), - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - _type: 'visualization', - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Node-Pie', - _type: 'visualization', - _source: { - title: 'App Cluster Overview Node Pie', - visState: JSON.stringify({ - title: 'App Cluster Overview Node Pie', - type: 'pie', - params: { - type: 'pie', - addTooltip: true, - addLegend: true, - legendPosition: 'right', - isDonut: true, - labels: { show: false, values: true, last_level: true, truncate: 100 }, - }, - aggs: [ - { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, - { - id: '2', - enabled: true, - type: 'terms', - schema: 'segment', - params: { - field: 'cluster.node', - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - size: 5, - order: 'desc', - orderBy: '1', - }, - }, - ], - }), - uiStateJSON: JSON.stringify({ spy: { mode: { name: 'table' } } }), - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, -]; diff --git a/plugins/main/server/lib/generate-alerts/generate-alerts-script.js b/plugins/main/server/lib/generate-alerts/generate-alerts-script.js index f718d55e47..9510171002 100644 --- a/plugins/main/server/lib/generate-alerts/generate-alerts-script.js +++ b/plugins/main/server/lib/generate-alerts/generate-alerts-script.js @@ -22,7 +22,14 @@ import { randomElements, randomArrayItem, } from './sample-data/common'; -import { PCI_DSS, GDPR, HIPAA, GPG13, NIST_800_53, tsc } from './sample-data/regulatory-compliance'; +import { + PCI_DSS, + GDPR, + HIPAA, + GPG13, + NIST_800_53, + tsc, +} from './sample-data/regulatory-compliance'; import * as Audit from './sample-data/audit'; import * as Authentication from './sample-data/authentication'; @@ -143,7 +150,9 @@ function generateAlert(params) { 'iamPolicyGrantGlobal', ]); - const beforeDate = new Date(new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000); + const beforeDate = new Date( + new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000, + ); switch (randomType) { case 'guarddutyPortProbe': { const typeAlert = AWS.guarddutyPortProbe; @@ -151,29 +160,39 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.instanceDetails = { ...randomArrayItem(AWS.instanceDetails) }; - alert.data.aws.resource.instanceDetails.iamInstanceProfile.arn = interpolateAlertProps( - typeAlert.data.aws.resource.instanceDetails.iamInstanceProfile.arn, - alert + alert.data.aws.resource.instanceDetails = { + ...randomArrayItem(AWS.instanceDetails), + }; + alert.data.aws.resource.instanceDetails.iamInstanceProfile.arn = + interpolateAlertProps( + typeAlert.data.aws.resource.instanceDetails.iamInstanceProfile.arn, + alert, + ); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, ); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); alert.data.aws.accountId = randomArrayItem(AWS.accountId); - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); - alert.data.aws.service.action.portProbeAction.portProbeDetails.remoteIpDetails = { - ...randomArrayItem(AWS.remoteIpDetails), - }; + alert.data.aws.service.action.portProbeAction.portProbeDetails.remoteIpDetails = + { + ...randomArrayItem(AWS.remoteIpDetails), + }; alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws.service.count = `${randomIntervalInteger(400, 4000)}`; @@ -181,7 +200,10 @@ function generateAlert(params) { alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -193,36 +215,49 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.accessKeyDetails.userName = randomArrayItem(Users); + alert.data.aws.resource.accessKeyDetails.userName = + randomArrayItem(Users); alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws.accountId = randomArrayItem(AWS.accountId); alert.data.aws.service.action.awsApiCallAction.remoteIpDetails = { ...randomArrayItem(AWS.remoteIpDetails), }; - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); alert.data.aws.createdAt = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); - alert.data.aws.description = interpolateAlertProps(alert.data.aws.description, alert); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, + ); + alert.data.aws.description = interpolateAlertProps( + alert.data.aws.description, + alert, + ); const count = `${randomIntervalInteger(400, 4000)}`; alert.data.aws.service.additionalInfo.recentApiCalls.count = count; alert.data.aws.service.count = count; alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -234,28 +269,40 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.instanceDetails = { ...randomArrayItem(AWS.instanceDetails) }; + alert.data.aws.resource.instanceDetails = { + ...randomArrayItem(AWS.instanceDetails), + }; alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; - alert.data.aws.description = interpolateAlertProps(alert.data.aws.description, alert); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); + alert.data.aws.description = interpolateAlertProps( + alert.data.aws.description, + alert, + ); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, + ); alert.data.aws.accountId = randomArrayItem(AWS.accountId); alert.data.aws.createdAt = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.service.action.networkConnectionAction.remoteIpDetails = { - ...randomArrayItem(AWS.remoteIpDetails), - }; - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.action.networkConnectionAction.remoteIpDetails = + { + ...randomArrayItem(AWS.remoteIpDetails), + }; + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); alert.data.aws.service.additionalInfo = { localPort: `${randomArrayItem(Ports)}`, @@ -266,10 +313,16 @@ function generateAlert(params) { alert.data.aws.service.count = `${randomIntervalInteger(400, 4000)}`; alert.data.aws.service.action.networkConnectionAction.localIpDetails.ipAddressV4 = alert.data.aws.resource.instanceDetails.networkInterfaces.privateIpAddress; - alert.data.aws.arn = interpolateAlertProps(typeAlert.data.aws.arn, alert); + alert.data.aws.arn = interpolateAlertProps( + typeAlert.data.aws.arn, + alert, + ); alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -281,23 +334,32 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.summary.Timestamps = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.summary.Timestamps = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `macie/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_macie-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s' + 'Y-M-D-h-m-s', )}-0b1ede94-f399-4e54-8815-1c6587eee3b1//firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws['created-at'] = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.url = interpolateAlertProps(typeAlert.data.aws.url, alert); - alert.data.aws['alert-arn'] = interpolateAlertProps(typeAlert.data.aws['alert-arn'], alert); + alert.data.aws.url = interpolateAlertProps( + typeAlert.data.aws.url, + alert, + ); + alert.data.aws['alert-arn'] = interpolateAlertProps( + typeAlert.data.aws['alert-arn'], + alert, + ); alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); @@ -317,25 +379,31 @@ function generateAlert(params) { alert.agent = { id: '000', ip: alert.agent.ip, - name: alert.agent.name + name: alert.agent.name, }; if (params.manager && params.manager.name) { alert.agent.name = params.manager.name; - }; + } - const beforeDate = new Date(new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000); + const beforeDate = new Date( + new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000, + ); const IntraID = randomArrayItem(Office.arrayUuidOffice); const OrgID = randomArrayItem(Office.arrayUuidOffice); const objID = randomArrayItem(Office.arrayUuidOffice); const userKey = randomArrayItem(Office.arrayUuidOffice); const userID = randomArrayItem(Office.arrayUserId); const userType = randomArrayItem([0, 2, 4]); - const resultStatus = randomArrayItem(['Succeeded', 'PartiallySucceeded', 'Failed']); + const resultStatus = randomArrayItem([ + 'Succeeded', + 'PartiallySucceeded', + 'Failed', + ]); const log = randomArrayItem(Office.arrayLogs); const ruleData = Office.officeRules[log.RecordType]; - alert.agent.id = '000' + alert.agent.id = '000'; alert.rule = ruleData.rule; alert.decoder = randomArrayItem(Office.arrayDecoderOffice); alert.GeoLocation = randomArrayItem(GeoLocation); @@ -362,13 +430,30 @@ function generateAlert(params) { alert.data.gcp = { insertId: 'uk1zpe23xcj', jsonPayload: { - authAnswer: GCP.arrayAuthAnswer[Math.floor(GCP.arrayAuthAnswer.length * Math.random())], - protocol: GCP.arrayProtocol[Math.floor(GCP.arrayProtocol.length * Math.random())], - queryName: GCP.arrayQueryName[Math.floor(GCP.arrayQueryName.length * Math.random())], - queryType: GCP.arrayQueryType[Math.floor(GCP.arrayQueryType.length * Math.random())], + authAnswer: + GCP.arrayAuthAnswer[ + Math.floor(GCP.arrayAuthAnswer.length * Math.random()) + ], + protocol: + GCP.arrayProtocol[ + Math.floor(GCP.arrayProtocol.length * Math.random()) + ], + queryName: + GCP.arrayQueryName[ + Math.floor(GCP.arrayQueryName.length * Math.random()) + ], + queryType: + GCP.arrayQueryType[ + Math.floor(GCP.arrayQueryType.length * Math.random()) + ], responseCode: - GCP.arrayResponseCode[Math.floor(GCP.arrayResponseCode.length * Math.random())], - sourceIP: GCP.arraySourceIP[Math.floor(GCP.arraySourceIP.length * Math.random())], + GCP.arrayResponseCode[ + Math.floor(GCP.arrayResponseCode.length * Math.random()) + ], + sourceIP: + GCP.arraySourceIP[ + Math.floor(GCP.arraySourceIP.length * Math.random()) + ], vmInstanceId: '4980113928800839680.000000', vmInstanceName: '531339229531.instance-1', }, @@ -376,14 +461,24 @@ function generateAlert(params) { receiveTimestamp: '2019-11-11T02:42:05.05853152Z', resource: { labels: { - location: GCP.arrayLocation[Math.floor(GCP.arrayLocation.length * Math.random())], - project_id: GCP.arrayProject[Math.floor(GCP.arrayProject.length * Math.random())], - source_type: GCP.arraySourceType[Math.floor(GCP.arraySourceType.length * Math.random())], + location: + GCP.arrayLocation[ + Math.floor(GCP.arrayLocation.length * Math.random()) + ], + project_id: + GCP.arrayProject[ + Math.floor(GCP.arrayProject.length * Math.random()) + ], + source_type: + GCP.arraySourceType[ + Math.floor(GCP.arraySourceType.length * Math.random()) + ], target_type: 'external', }, type: GCP.arrayType[Math.floor(GCP.arrayType.length * Math.random())], }, - severity: GCP.arraySeverity[Math.floor(GCP.arraySeverity.length * Math.random())], + severity: + GCP.arraySeverity[Math.floor(GCP.arraySeverity.length * Math.random())], timestamp: '2019-11-11T02:42:04.34921449Z', }; @@ -459,13 +554,21 @@ function generateAlert(params) { switch (alertCategory) { case 'Rootkit': { - const rootkitCategory = randomArrayItem(Object.keys(PolicyMonitoring.rootkits)); - const rootkit = randomArrayItem(PolicyMonitoring.rootkits[rootkitCategory]); + const rootkitCategory = randomArrayItem( + Object.keys(PolicyMonitoring.rootkits), + ); + const rootkit = randomArrayItem( + PolicyMonitoring.rootkits[rootkitCategory], + ); alert.data = { - title: interpolateAlertProps(PolicyMonitoring.rootkitsData.data.title, alert, { - _rootkit_category: rootkitCategory, - _rootkit_file: rootkit, - }), + title: interpolateAlertProps( + PolicyMonitoring.rootkitsData.data.title, + alert, + { + _rootkit_category: rootkitCategory, + _rootkit_file: rootkit, + }, + ), }; alert.rule = { ...PolicyMonitoring.rootkitsData.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 10); @@ -480,9 +583,13 @@ function generateAlert(params) { }; alert.rule = { ...PolicyMonitoring.trojansData.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 10); - alert.full_log = interpolateAlertProps(PolicyMonitoring.trojansData.full_log, alert, { - _trojan_signature: trojan.signature, - }); + alert.full_log = interpolateAlertProps( + PolicyMonitoring.trojansData.full_log, + alert, + { + _trojan_signature: trojan.signature, + }, + ); break; } default: { @@ -497,7 +604,7 @@ function generateAlert(params) { alert.syscheck.path = randomArrayItem( alert.agent.name === 'Windows' ? IntegrityMonitoring.pathsWindows - : IntegrityMonitoring.pathsLinux + : IntegrityMonitoring.pathsLinux, ); alert.syscheck.uname_after = randomArrayItem(Users); alert.syscheck.gname_after = 'root'; @@ -513,10 +620,14 @@ function generateAlert(params) { break; case 'modified': alert.rule = IntegrityMonitoring.regulatory[1]; - alert.syscheck.mtime_before = new Date(alert.syscheck.mtime_after.getTime() - 1000 * 60); + alert.syscheck.mtime_before = new Date( + alert.syscheck.mtime_after.getTime() - 1000 * 60, + ); alert.syscheck.inode_before = randomIntervalInteger(0, 100000); alert.syscheck.sha1_after = randomElements(40, 'abcdef0123456789'); - alert.syscheck.changed_attributes = [randomArrayItem(IntegrityMonitoring.attributes)]; + alert.syscheck.changed_attributes = [ + randomArrayItem(IntegrityMonitoring.attributes), + ]; alert.syscheck.md5_after = randomElements(32, 'abcdef0123456789'); alert.syscheck.sha256_after = randomElements(60, 'abcdef0123456789'); break; @@ -560,7 +671,10 @@ function generateAlert(params) { alert.data.virustotal.source = { sha1: randomElements(40, 'abcdef0123456789'), file: randomArrayItem(Virustotal.sourceFile), - alert_id: `${randomElements(10, '0123456789')}.${randomElements(7, '0123456789')}`, + alert_id: `${randomElements(10, '0123456789')}.${randomElements( + 7, + '0123456789', + )}`, md5: randomElements(32, 'abcdef0123456789'), }; @@ -571,10 +685,13 @@ function generateAlert(params) { alert.data.virustotal.malicious + alert.data.virustotal.positives; alert.rule.description = `VirusTotal: Alert - ${alert.data.virustotal.source.file} - ${alert.data.virustotal.positives} engines detected this file`; alert.data.virustotal.permalink = randomArrayItem(Virustotal.permalink); - alert.data.virustotal.scan_date = new Date(Date.parse(alert.timestamp) - 4 * 60000); + alert.data.virustotal.scan_date = new Date( + Date.parse(alert.timestamp) - 4 * 60000, + ); } else { alert.data.virustotal.malicious = '0'; - alert.rule.description = 'VirusTotal: Alert - No records in VirusTotal database'; + alert.rule.description = + 'VirusTotal: Alert - No records in VirusTotal database'; } } @@ -605,7 +722,9 @@ function generateAlert(params) { alert.data.osquery = dataOsquery.osquery; alert.data.osquery.calendarTime = alert.timestamp; alert.rule.description = dataOsquery.rule.description; - randomIntervalInteger(0, 99) === 0 ? (alert.data.osquery.action = 'removed') : null; + randomIntervalInteger(0, 99) === 0 + ? (alert.data.osquery.action = 'removed') + : null; } } @@ -687,39 +806,56 @@ function generateAlert(params) { case 'invalidLoginPassword': { alert.location = Authentication.invalidLoginPassword.location; alert.rule = { ...Authentication.invalidLoginPassword.rule }; - alert.rule.groups = [...Authentication.invalidLoginPassword.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.invalidLoginPassword.full_log, alert); + alert.rule.groups = [ + ...Authentication.invalidLoginPassword.rule.groups, + ]; + alert.full_log = interpolateAlertProps( + Authentication.invalidLoginPassword.full_log, + alert, + ); break; } case 'invalidLoginUser': { alert.location = Authentication.invalidLoginUser.location; alert.rule = { ...Authentication.invalidLoginUser.rule }; alert.rule.groups = [...Authentication.invalidLoginUser.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.invalidLoginUser.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.invalidLoginUser.full_log, + alert, + ); break; } case 'multipleAuthenticationFailures': { alert.location = Authentication.multipleAuthenticationFailures.location; alert.rule = { ...Authentication.multipleAuthenticationFailures.rule }; - alert.rule.groups = [...Authentication.multipleAuthenticationFailures.rule.groups]; + alert.rule.groups = [ + ...Authentication.multipleAuthenticationFailures.rule.groups, + ]; alert.rule.frequency = randomIntervalInteger(5, 50); alert.full_log = interpolateAlertProps( Authentication.multipleAuthenticationFailures.full_log, - alert + alert, ); break; } case 'windowsInvalidLoginPassword': { alert.location = Authentication.windowsInvalidLoginPassword.location; alert.rule = { ...Authentication.windowsInvalidLoginPassword.rule }; - alert.rule.groups = [...Authentication.windowsInvalidLoginPassword.rule.groups]; + alert.rule.groups = [ + ...Authentication.windowsInvalidLoginPassword.rule.groups, + ]; alert.rule.frequency = randomIntervalInteger(5, 50); - alert.data.win = { ...Authentication.windowsInvalidLoginPassword.data_win }; + alert.data.win = { + ...Authentication.windowsInvalidLoginPassword.data_win, + }; alert.data.win.eventdata.ipAddress = randomArrayItem(IPs); alert.data.win.eventdata.ipPort = randomArrayItem(Ports); alert.data.win.system.computer = randomArrayItem(Win_Hostnames); alert.data.win.system.eventID = `${randomIntervalInteger(1, 600)}`; - alert.data.win.system.eventRecordID = `${randomIntervalInteger(10000, 50000)}`; + alert.data.win.system.eventRecordID = `${randomIntervalInteger( + 10000, + 50000, + )}`; alert.data.win.system.processID = `${randomIntervalInteger(1, 1200)}`; alert.data.win.system.systemTime = alert.timestamp; alert.data.win.system.processID = `${randomIntervalInteger(1, 1200)}`; @@ -727,7 +863,7 @@ function generateAlert(params) { alert.data.win.system.threadID = `${randomIntervalInteger(1, 500)}`; alert.full_log = interpolateAlertProps( Authentication.windowsInvalidLoginPassword.full_log, - alert + alert, ); break; } @@ -743,7 +879,10 @@ function generateAlert(params) { tty: 'ssh', }; alert.decoder = { ...Authentication.userLoginFailed.decoder }; - alert.full_log = interpolateAlertProps(Authentication.userLoginFailed.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.userLoginFailed.full_log, + alert, + ); break; } case 'passwordCheckFailed': { @@ -755,23 +894,31 @@ function generateAlert(params) { }; alert.predecoder.program_name = 'unix_chkpwd'; alert.decoder = { ...Authentication.passwordCheckFailed.decoder }; - alert.full_log = interpolateAlertProps(Authentication.passwordCheckFailed.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.passwordCheckFailed.full_log, + alert, + ); break; } case 'nonExistentUser': { alert.location = Authentication.nonExistentUser.location; alert.rule = { ...Authentication.nonExistentUser.rule }; alert.rule.groups = [...Authentication.nonExistentUser.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.nonExistentUser.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.nonExistentUser.full_log, + alert, + ); break; } case 'bruteForceTryingAccessSystem': { alert.location = Authentication.bruteForceTryingAccessSystem.location; alert.rule = { ...Authentication.bruteForceTryingAccessSystem.rule }; - alert.rule.groups = [...Authentication.bruteForceTryingAccessSystem.rule.groups]; + alert.rule.groups = [ + ...Authentication.bruteForceTryingAccessSystem.rule.groups, + ]; alert.full_log = interpolateAlertProps( Authentication.bruteForceTryingAccessSystem.full_log, - alert + alert, ); break; } @@ -782,25 +929,32 @@ function generateAlert(params) { alert.data = { srcip: randomArrayItem(IPs), }; - alert.full_log = interpolateAlertProps(Authentication.reverseLoockupError.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.reverseLoockupError.full_log, + alert, + ); } case 'insecureConnectionAttempt': { alert.location = Authentication.insecureConnectionAttempt.location; alert.rule = { ...Authentication.insecureConnectionAttempt.rule }; - alert.rule.groups = [...Authentication.insecureConnectionAttempt.rule.groups]; + alert.rule.groups = [ + ...Authentication.insecureConnectionAttempt.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), }; alert.full_log = interpolateAlertProps( Authentication.insecureConnectionAttempt.full_log, - alert + alert, ); } case 'authenticationSuccess': { alert.location = Authentication.authenticationSuccess.location; alert.rule = { ...Authentication.authenticationSuccess.rule }; - alert.rule.groups = [...Authentication.authenticationSuccess.rule.groups]; + alert.rule.groups = [ + ...Authentication.authenticationSuccess.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), @@ -808,13 +962,18 @@ function generateAlert(params) { }; alert.full_log = interpolateAlertProps( Authentication.authenticationSuccess.full_log, - alert + alert, ); } case 'maximumAuthenticationAttemptsExceeded': { - alert.location = Authentication.maximumAuthenticationAttemptsExceeded.location; - alert.rule = { ...Authentication.maximumAuthenticationAttemptsExceeded.rule }; - alert.rule.groups = [...Authentication.maximumAuthenticationAttemptsExceeded.rule.groups]; + alert.location = + Authentication.maximumAuthenticationAttemptsExceeded.location; + alert.rule = { + ...Authentication.maximumAuthenticationAttemptsExceeded.rule, + }; + alert.rule.groups = [ + ...Authentication.maximumAuthenticationAttemptsExceeded.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), @@ -822,7 +981,7 @@ function generateAlert(params) { }; alert.full_log = interpolateAlertProps( Authentication.maximumAuthenticationAttemptsExceeded.full_log, - alert + alert, ); } default: { @@ -915,7 +1074,10 @@ function generateAlert(params) { alert.decoder = { ...Apache.decoder }; alert.full_log = interpolateAlertProps(typeAlert.full_log, alert, { - _timestamp_apache: formatDate(new Date(alert.timestamp), 'E N D h:m:s.l Y'), + _timestamp_apache: formatDate( + new Date(alert.timestamp), + 'E N D h:m:s.l Y', + ), _pi_id: randomIntervalInteger(10000, 30000), }); } @@ -951,35 +1113,51 @@ function generateAlert(params) { interpolateAlertProps(typeAlert.full_log, alert, { _user_agent: userAgent, _date: formatDate(new Date(beforeDate), 'D/N/Y:h:m:s +0000'), - }) + }), ); } alert.previous_output = previousOutput.join('\n'); } } - if (params.github){ + if (params.github) { alert.location = GitHub.LOCATION; alert.decoder = GitHub.DECODER; const alertType = randomArrayItem(GitHub.ALERT_TYPES); const actor = randomArrayItem(GitHub.ACTORS); alert.data = { - github : { ...alertType.data.github } + github: { ...alertType.data.github }, }; alert.data.github.org = randomArrayItem(GitHub.ORGANIZATION_NAMES); - alert.data.github.repo && (alert.data.github.repo = `${alert.data.github.org}/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); - alert.data.github.repository && (alert.data.github.repository = `${alert.data.github.org}/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); + alert.data.github.repo && + (alert.data.github.repo = `${alert.data.github.org}/${randomArrayItem( + GitHub.REPOSITORY_NAMES, + )}`); + alert.data.github.repository && + (alert.data.github.repository = `${ + alert.data.github.org + }/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); alert.data.github.actor = actor.name; - alert.data.github.actor_location && alert.data.github.actor_location.country_code && (alert.data.github.actor_location.country_code = actor.country_code); - alert.data.github.user && (alert.data.github.user = randomArrayItem(GitHub.USER_NAMES)); - alert.data.github.config && alert.data.github.config.url && (alert.data.github.config.url = randomArrayItem(GitHub.SERVER_ADDRESS_WEBHOOK)); + alert.data.github.actor_location && + alert.data.github.actor_location.country_code && + (alert.data.github.actor_location.country_code = actor.country_code); + alert.data.github.user && + (alert.data.github.user = randomArrayItem(GitHub.USER_NAMES)); + alert.data.github.config && + alert.data.github.config.url && + (alert.data.github.config.url = randomArrayItem( + GitHub.SERVER_ADDRESS_WEBHOOK, + )); alert.data.github['@timestamp'] = alert.timestamp; - alert.data.github.created_at && (alert.data.github.created_at = alert.timestamp); + alert.data.github.created_at && + (alert.data.github.created_at = alert.timestamp); alert.rule = { - ...alertType.rule + ...alertType.rule, }; } - + + alert['@timestamp'] = alert.timestamp; + return alert; } @@ -1037,7 +1215,8 @@ function randomDate(inf, sup) { return formatDate(lastWeek, 'Y-M-DTh:m:s.l+0000'); } -const formatterNumber = (number, zeros = 0) => ('0'.repeat(zeros) + `${number}`).slice(-zeros); +const formatterNumber = (number, zeros = 0) => + ('0'.repeat(zeros) + `${number}`).slice(-zeros); const monthNames = { long: [ 'January', @@ -1053,28 +1232,49 @@ const monthNames = { 'November', 'December', ], - short: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + short: [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ], }; const dayNames = { - long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + long: [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ], short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], }; function formatDate(date, format) { // It could use "moment" library to format strings too const tokens = { - D: (d) => formatterNumber(d.getDate(), 2), // 01-31 - A: (d) => dayNames.long[d.getDay()], // 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' - E: (d) => dayNames.short[d.getDay()], // 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' - M: (d) => formatterNumber(d.getMonth() + 1, 2), // 01-12 - J: (d) => monthNames.long[d.getMonth()], // 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' - N: (d) => monthNames.short[d.getMonth()], // 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' - Y: (d) => d.getFullYear(), // 2020 - h: (d) => formatterNumber(d.getHours(), 2), // 00-23 - m: (d) => formatterNumber(d.getMinutes(), 2), // 00-59 - s: (d) => formatterNumber(d.getSeconds(), 2), // 00-59 - l: (d) => formatterNumber(d.getMilliseconds(), 3), // 000-999 + D: d => formatterNumber(d.getDate(), 2), // 01-31 + A: d => dayNames.long[d.getDay()], // 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' + E: d => dayNames.short[d.getDay()], // 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' + M: d => formatterNumber(d.getMonth() + 1, 2), // 01-12 + J: d => monthNames.long[d.getMonth()], // 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' + N: d => monthNames.short[d.getMonth()], // 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + Y: d => d.getFullYear(), // 2020 + h: d => formatterNumber(d.getHours(), 2), // 00-23 + m: d => formatterNumber(d.getMinutes(), 2), // 00-59 + s: d => formatterNumber(d.getSeconds(), 2), // 00-59 + l: d => formatterNumber(d.getMilliseconds(), 3), // 000-999 }; return format.split('').reduce((accum, token) => { @@ -1098,7 +1298,9 @@ function interpolateAlertProps(str, alert, extra = {}) { matches.reduce((accum, cur) => { const match = cur.match(/{([\w\._]+)}/); const items = match[1].split('.'); - const value = items.reduce((a, c) => (a && a[c]) || extra[c] || undefined, alert) || cur; + const value = + items.reduce((a, c) => (a && a[c]) || extra[c] || undefined, alert) || + cur; return accum.replace(cur, value); }, str)) || str