Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt the reporting to the rendering of dashboards #6558

Draft
wants to merge 30 commits into
base: 4.9.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
81d093c
feat(reporting): adapt the screenshot taking to dashboard rendering
Desvelao Mar 27, 2024
09e584c
feat(reporting): remove unused AngularJS service to generate the reports
Desvelao Mar 27, 2024
abb19d8
feat(reporting): remove unused import
Desvelao Mar 27, 2024
3bf249c
feat(reporting): enhance the toast messagen when a PDF report is gene…
Desvelao Mar 27, 2024
23454f8
feat(reporting): replace toast title when generating the PDF reports
Desvelao Mar 27, 2024
495873d
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao Apr 23, 2024
6629215
fix(reporting): time.from parameter
Desvelao Apr 24, 2024
1386a63
feat(reporting): replace query.getOpenSearchQuery from global source …
Desvelao Apr 25, 2024
27ff426
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao Apr 25, 2024
c18ce40
fix(reporting): remove isLoading dependency from Generate repot button
Desvelao Apr 25, 2024
ee1f4aa
feat(reporting): adapt agent inventory report to data source
Desvelao Apr 30, 2024
baff7a5
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao Apr 30, 2024
6fc37b5
fix(reporting): fix date range filter for Today and This week special…
Desvelao Apr 30, 2024
9e940a6
feat(reporting): adapt the dashboard of Threat Hunting and VirusTotal…
Desvelao Apr 30, 2024
73f5cc8
feat(reporting): adapt the dashboard of MITRE ATT&CK to the data source
Desvelao Apr 30, 2024
17bc7f3
fix: remove unused props and imports
Desvelao Apr 30, 2024
456c3dc
feat(reporting): adapt the dashboard of Amazon Web Services to the da…
Desvelao May 2, 2024
315298e
fix(reporting): error converting DOM node from map visualizations to …
Desvelao May 2, 2024
ea584d6
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao May 2, 2024
792ea78
feat(reporting): adapt the dashboard of File Integrity Monitoring to …
Desvelao May 2, 2024
7c7dafb
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao May 3, 2024
c4e3531
feat(reporting): adapt the dashboard of Malware Detection to the data…
Desvelao May 3, 2024
9e97555
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao May 3, 2024
b24e167
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao May 3, 2024
0fdf767
feat(reporting): adapt the dashboard of Docker to the data source
Desvelao May 3, 2024
8ca7000
Merge branch '4.9.0' of https://github.com/wazuh/wazuh-kibana-app int…
Desvelao May 6, 2024
acb073f
feat(reporting): adapt the dashboard of Google Cloud to the data source
Desvelao May 6, 2024
59b075b
fix(google-cloud): add missing button to generate the report on Dashb…
Desvelao May 6, 2024
3c742c0
fix(reporting): malformed table row error due to missing columns on t…
Desvelao May 6, 2024
2f4a4f0
fix: typo
Desvelao May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,23 @@
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { updateReportingCommunicateSearchContext } from '../../../redux/actions/reportingActions';
import { IIndexPattern } from '../../../../../../src/plugins/data/public';
/**
* WORKAROUND: this hook stores the search context to be used by the Generate report button of
* module dashboards
* @param context
*/
export function useReportingCommunicateSearchContext(context: {
isSearching: boolean;
totalResults: number;
indexPattern: IIndexPattern;
filters: any;
time: any;
query: any;
}) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(updateReportingCommunicateSearchContext(context));
return () => dispatch(updateReportingCommunicateSearchContext(null));
}, [JSON.stringify(context)]);
}
Expand Up @@ -16,48 +16,65 @@ import { getUiSettings } from '../../../../kibana-services';
import { ReportingService } from '../../../../react-services';
import $ from 'jquery';
import { WzButton } from '../../../common/buttons';
import { connect } from 'react-redux';

const mapStateToProps = state => ({
dataSourceSearchContext: state.reportingReducers.dataSourceSearchContext,
});

export const ButtonModuleGenerateReport = ({agent, moduleID, disabledReport}) => {
const action = useAsyncAction(async () => {
const reportingService = new ReportingService();
const isDarkModeTheme = getUiSettings().get('theme:darkMode');
if (isDarkModeTheme) {
export const ButtonModuleGenerateReport = connect(mapStateToProps)(
({ agent, moduleID, dataSourceSearchContext }) => {
const disabledReport = ![
!dataSourceSearchContext?.isSearching,
dataSourceSearchContext?.totalResults,
dataSourceSearchContext?.indexPattern,
].every(Boolean);
const totalResults = dataSourceSearchContext?.totalResults;
const action = useAsyncAction(async () => {
const reportingService = new ReportingService();
const isDarkModeTheme = getUiSettings().get('theme:darkMode');
if (isDarkModeTheme) {
//Patch to fix white text in dark-mode pdf reports
const defaultTextColor = '#DFE5EF';

//Patch to fix white text in dark-mode pdf reports
const defaultTextColor = '#DFE5EF';
//Patch to fix dark backgrounds in visualizations dark-mode pdf reports
const $labels = $('.euiButtonEmpty__text, .echLegendItem');
const $vizBackground = $('.echChartBackground');
const defaultVizBackground = $vizBackground.css('background-color');

//Patch to fix dark backgrounds in visualizations dark-mode pdf reports
const $labels = $('.euiButtonEmpty__text, .echLegendItem');
const $vizBackground = $('.echChartBackground');
const defaultVizBackground = $vizBackground.css('background-color');

try {
$labels.css('color', 'black');
$vizBackground.css('background-color', 'transparent');
await reportingService.startVis2Png(moduleID, agent?.id || false)
$vizBackground.css('background-color', defaultVizBackground);
$labels.css('color', defaultTextColor);
} catch (e) {
$labels.css('color', defaultTextColor);
$vizBackground.css('background-color', defaultVizBackground);
try {
$labels.css('color', 'black');
$vizBackground.css('background-color', 'transparent');
await reportingService.startVis2Png(moduleID, agent?.id || false);
$vizBackground.css('background-color', defaultVizBackground);
$labels.css('color', defaultTextColor);
} catch (e) {
$labels.css('color', defaultTextColor);
$vizBackground.css('background-color', defaultVizBackground);
}
} else {
await reportingService.startVis2Png(moduleID, agent?.id || false);
}
} else {
await reportingService.startVis2Png(moduleID, agent?.id || false)
}
}, [agent]);

return (
<WzButton
buttonType='empty'
iconType='document'
isLoading={action.running}
onClick={action.run}
isDisabled={disabledReport}
tooltip={disabledReport ? {position: 'top', content: 'No results match for this search criteria.'} : undefined}
>
Generate report
</WzButton>
)
}
}, [agent]);

return (
<WzButton
buttonType='empty'
iconType='document'
isLoading={action.running}
onClick={action.run}
isDisabled={disabledReport}
tooltip={
disabledReport && totalResults === 0
? {
position: 'top',
content: 'No results match for this search criteria.',
}
: undefined
}
>
Generate report
</WzButton>
);
},
);
92 changes: 54 additions & 38 deletions plugins/main/public/components/common/modules/main-agent.tsx
Expand Up @@ -30,6 +30,14 @@ import { getAngularModule, getCore } from '../../../kibana-services';
import { compose } from 'redux';
import { withGlobalBreadcrumb } from '../hocs';
import { endpointSummary } from '../../../utils/applications';
import {
AlertsDataSource,
AlertsDataSourceRepository,
PatternDataSource,
tParsedIndexPattern,
useDataSource,
} from '../data-source';
import { useAsyncAction } from '../hooks';

export class MainModuleAgent extends Component {
props!: {
Expand Down Expand Up @@ -61,43 +69,6 @@ export class MainModuleAgent extends Component {
this.router = $injector.get('$route');
}

async startReport() {
this.setState({ loadingReport: true });
const syscollectorFilters: any[] = [];
const agent =
(
this.props.agent ||
store.getState().appStateReducers.currentAgentData ||
{}
).id || false;
if (this.props.section === 'syscollector' && agent) {
syscollectorFilters.push(this.filterHandler.managerQuery(agent, true));
syscollectorFilters.push(this.filterHandler.agentQuery(agent));
}
await this.reportingService.startVis2Png(
this.props.section,
agent,
syscollectorFilters.length ? syscollectorFilters : null,
);
this.setState({ loadingReport: false });
}

renderReportButton() {
return (
this.props.section === 'syscollector' && (
<EuiFlexItem grow={false} style={{ marginRight: 4, marginTop: 6 }}>
<EuiButtonEmpty
iconType='document'
isLoading={this.state.loadingReport}
onClick={async () => this.startReport()}
>
Generate report
</EuiButtonEmpty>
</EuiFlexItem>
)
);
}

renderTitle() {
return (
<EuiFlexGroup>
Expand All @@ -123,7 +94,14 @@ export class MainModuleAgent extends Component {
</span>
</EuiFlexItem>
<EuiFlexItem />
{this.renderReportButton()}
{this.props.section === 'syscollector' && (
<EuiFlexItem
grow={false}
style={{ marginRight: 4, marginTop: 6 }}
>
<GenerateSyscollectorReportButton agent={this.props.agent} />
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down Expand Up @@ -253,3 +231,41 @@ export default compose(
}
}),
)(MainModuleAgent);

const GenerateSyscollectorReportButton = ({ agent }) => {
const {
dataSource,
fetchFilters,
isLoading: isDataSourceLoading,
} = useDataSource<tParsedIndexPattern, PatternDataSource>({
repository: new AlertsDataSourceRepository(), // this makes only works with alerts index pattern
DataSource: AlertsDataSource,
});

const action = useAsyncAction(async () => {
const reportingService = new ReportingService();
const agentID =
(agent || store.getState().appStateReducers.currentAgentData || {}).id ||
false;
await reportingService.startVis2Png('syscollector', agentID, {
indexPattern: dataSource.indexPattern,
query: { query: '', language: 'kuery' },
filters: fetchFilters,
time: {
from: 'now-1d/d',
to: 'now',
},
});
}, [dataSource]);

return (
<EuiButtonEmpty
iconType='document'
isLoading={action.running}
isDisabled={isDataSourceLoading}
onClick={action.run}
>
Generate report
</EuiButtonEmpty>
);
};
Expand Up @@ -156,7 +156,7 @@ export const ModulesDefaults = {
id: 'dashboard',
name: 'Dashboard',
component: DashboardGoogleCloud,
buttons: [ButtonModuleExploreAgent],
buttons: [ButtonModuleExploreAgent, ButtonModuleGenerateReport],
},
renderDiscoverTab({
tableColumns: googleCloudColumns,
Expand Down
Expand Up @@ -34,7 +34,7 @@ function parseQueryString() {
/**
* Get the forceNow query parameter
*/
function getForceNow() {
export function getForceNow() {
const forceNow = parseQueryString().forceNow as string;
if (!forceNow) {
return;
Expand Down Expand Up @@ -90,7 +90,7 @@ export const search = async (
gte: dateMath.parse(from).toISOString(),
/* roundUp: true is used to transform the osd dateform to a generic date format
For instance: the "This week" date range in the date picker.
To: now/w
To: now/w
From: now/w
Without the roundUp the to and from date will be the same and the search will return no results or error

Expand Down
Expand Up @@ -24,6 +24,7 @@ import {
} from '../../../common/data-source';
import { DiscoverNoResults } from '../../../common/no-results/no-results';
import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner';
import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context';

const plugins = getPlugins();

Expand Down Expand Up @@ -54,6 +55,18 @@ const DashboardAWSComponents: React.FC = ({}) => {

const { query, dateRangeFrom, dateRangeTo } = searchBarProps;

useReportingCommunicateSearchContext({
isSearching: isDataSourceLoading,
totalResults: results?.hits?.total ?? 0,
indexPattern: dataSource?.indexPattern,
filters: fetchFilters,
query: query,
time: {
from: dateRangeFrom,
to: dateRangeTo,
},
});

useEffect(() => {
if (isDataSourceLoading) {
return;
Expand Down
Expand Up @@ -24,6 +24,7 @@ import {
} from '../../../common/data-source';
import { DiscoverNoResults } from '../../../common/no-results/no-results';
import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner';
import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context';

const plugins = getPlugins();

Expand Down Expand Up @@ -54,6 +55,18 @@ const DashboardDockerComponent: React.FC = ({}) => {

const { query, dateRangeFrom, dateRangeTo } = searchBarProps;

useReportingCommunicateSearchContext({
isSearching: isDataSourceLoading,
totalResults: results?.hits?.total ?? 0,
indexPattern: dataSource?.indexPattern,
filters: fetchFilters,
query: query,
time: {
from: dateRangeFrom,
to: dateRangeTo,
},
});

useEffect(() => {
if (isDataSourceLoading) {
return;
Expand Down
Expand Up @@ -24,6 +24,7 @@ import {
} from '../../../common/data-source';
import { DiscoverNoResults } from '../../../common/no-results/no-results';
import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner';
import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context';

const plugins = getPlugins();

Expand Down Expand Up @@ -54,6 +55,18 @@ const DashboardFIMComponent: React.FC = ({}) => {

const { query, dateRangeFrom, dateRangeTo } = searchBarProps;

useReportingCommunicateSearchContext({
isSearching: isDataSourceLoading,
totalResults: results?.hits?.total ?? 0,
indexPattern: dataSource?.indexPattern,
filters: fetchFilters,
query: query,
time: {
from: dateRangeFrom,
to: dateRangeTo,
},
});

useEffect(() => {
if (isDataSourceLoading) {
return;
Expand Down
Expand Up @@ -23,6 +23,7 @@ import {
import { AlertsGoogleCloudDataSource } from '../../../common/data-source/pattern/alerts/alerts-google-cloud/alerts-google-cloud-data-source';
import { DiscoverNoResults } from '../../../common/no-results/no-results';
import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner';
import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context';

const plugins = getPlugins();

Expand Down Expand Up @@ -53,6 +54,18 @@ const DashboardGoogleCloudComponent: React.FC = () => {

const { query, dateRangeFrom, dateRangeTo } = searchBarProps;

useReportingCommunicateSearchContext({
isSearching: isDataSourceLoading,
totalResults: results?.hits?.total ?? 0,
indexPattern: dataSource?.indexPattern,
filters: fetchFilters,
query: query,
time: {
from: dateRangeFrom,
to: dateRangeTo,
},
});

useEffect(() => {
if (isDataSourceLoading) {
return;
Expand Down Expand Up @@ -101,8 +114,8 @@ const DashboardGoogleCloudComponent: React.FC = () => {
<DiscoverNoResults />
) : null}
{dataSource && results?.hits?.total > 0 ? (
<>
<SampleDataWarning />
<>
<SampleDataWarning />
<div className='google-cloud-dashboard-responsive'>
<DashboardByRenderer
input={{
Expand Down