Skip to content

Commit

Permalink
Add correlation rule details into the finding details flyout opensear…
Browse files Browse the repository at this point in the history
…ch-project#563 (opensearch-project#565)

* Add correlation rule details into the finding details flyout opensearch-project#563

Signed-off-by: Jovan Cvetkovic <[email protected]>

* Add correlation rule details into the finding details flyout opensearch-project#563

Signed-off-by: Jovan Cvetkovic <[email protected]>

* fix tests

Signed-off-by: Jovan Cvetkovic <[email protected]>

* Add correlation rule details into the finding details flyout opensearch-project#563 opensearch-project#565

Signed-off-by: Jovan Cvetkovic <[email protected]>

* Add correlation rule details into the finding details flyout opensearch-project#563 opensearch-project#565

Signed-off-by: Jovan Cvetkovic <[email protected]>

* Add correlation rule details into the finding details flyout opensearch-project#563 opensearch-project#565

Signed-off-by: Jovan Cvetkovic <[email protected]>

---------

Signed-off-by: Jovan Cvetkovic <[email protected]>
  • Loading branch information
jovancvetkovic3006 authored May 4, 2023
1 parent 0e38a1b commit d9aa818
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 136 deletions.
7 changes: 6 additions & 1 deletion cypress/support/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,12 @@ Cypress.Commands.add(
cy.get($tr).within(($tr) => {
data.map((rowData) => {
rowData.forEach((tdData) => {
tdData && cy.get($tr).find('td').contains(`${tdData}`);
if (typeof tdData === 'string') {
tdData && cy.get($tr).find('td').contains(`${tdData}`);
} else {
// if rule is an object then use path
tdData && cy.get($tr).find('td').contains(`${tdData.path}`);
}
});
});
});
Expand Down
3 changes: 2 additions & 1 deletion public/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ $euiTextColor: $euiColorDarkestShade !default;
@import "./pages/Overview/components/Widgets/WidgetContainer.scss";
@import "./pages/Main/components/Callout.scss";
@import "./pages/Detectors/components/ReviewFieldMappings/ReviewFieldMappings.scss";
@import "./pages/Correlations//Correlations.scss";
@import "./pages/Correlations/Correlations.scss";
@import "./pages/Findings/components/CorrelationsTable/CorrelationsTable.scss";

.selected-radio-panel {
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
Expand Down
25 changes: 14 additions & 11 deletions public/pages/Correlations/containers/CorrelationsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -391,17 +391,20 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
<EuiSpacer />
{findingCardsData.correlatedFindings.map((finding, index) => {
return (
<FindingCard
key={index}
id={finding.id}
logType={finding.logType}
timestamp={finding.timestamp}
detectionRule={finding.detectionRule}
correlationData={{
score: finding.correlationScore || 0,
onInspect: this.onFindingInspect,
}}
/>
<>
<FindingCard
key={index}
id={finding.id}
logType={finding.logType}
timestamp={finding.timestamp}
detectionRule={finding.detectionRule}
correlationData={{
score: finding.correlationScore || 0,
onInspect: this.onFindingInspect,
}}
/>
<EuiSpacer size="m" />
</>
);
})}
</EuiFlyoutBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.correlations-table-details-row {
.correlations-table-details-row-value {
font-weight: 600;
color: $euiColorDarkestShade;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState } from 'react';
import { CorrelationFinding } from '../../../../../types';
import { ruleTypes } from '../../../Rules/utils/constants';
import { DEFAULT_EMPTY_DATA, ROUTES } from '../../../../utils/constants';
import { getSeverityBadge } from '../../../Rules/utils/helpers';
import {
EuiButton,
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiTitle,
EuiText,
EuiPanel,
EuiInMemoryTable,
EuiBasicTableColumn,
} from '@elastic/eui';
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
import { FindingItemType } from '../../containers/Findings/Findings';
import { RouteComponentProps } from 'react-router-dom';

export interface CorrelationsTableProps {
finding: FindingItemType;
correlatedFindings: CorrelationFinding[];
history: RouteComponentProps['history'];
isLoading: boolean;
}

export const CorrelationsTable: React.FC<CorrelationsTableProps> = ({
correlatedFindings,
finding,
history,
isLoading,
}) => {
const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{
[key: string]: JSX.Element;
}>({});

const toggleCorrelationDetails = (item: any) => {
const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
if (itemIdToExpandedRowMapValues[item.id]) {
delete itemIdToExpandedRowMapValues[item.id];
} else {
itemIdToExpandedRowMapValues[item.id] = (
<EuiPanel color="subdued" className={'correlations-table-details-row'}>
<EuiFlexGroup justifyContent="flexStart">
<EuiFlexItem grow={false} style={{ minWidth: 200 }}>
<EuiText size={'xs'}>Finding ID</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiText size={'xs'} className={'correlations-table-details-row-value'}>
{item.id}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>

<EuiFlexGroup justifyContent="flexStart">
<EuiFlexItem grow={false} style={{ minWidth: 200 }}>
<EuiText size={'xs'}>Threat detector</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiText size={'xs'} className={'correlations-table-details-row-value'}>
{item.detectorName}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>

<EuiFlexGroup justifyContent="flexStart">
<EuiFlexItem grow={false} style={{ minWidth: 200 }}>
<EuiText size={'xs'}>Detection rule</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiText size={'xs'} className={'correlations-table-details-row-value'}>
{item.detectionRule?.name || '-'}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
}

setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
};

const columns: EuiBasicTableColumn<CorrelationFinding>[] = [
{
field: 'timestamp',
name: 'Time',
sortable: true,
},
{
name: 'Correlated rule',
truncateText: true,
render: (item: CorrelationFinding) => item?.correlationRule.name || DEFAULT_EMPTY_DATA,
},
{
field: 'logType',
name: 'Log type',
sortable: true,
render: (category: string) =>
// TODO: This formatting may need some refactoring depending on the response payload
ruleTypes.find((ruleType) => ruleType.value === category)?.label || DEFAULT_EMPTY_DATA,
},
{
name: 'Rule severity',
truncateText: true,
align: 'center',
render: (item: CorrelationFinding) => getSeverityBadge(item.detectionRule.severity),
},
{
field: 'correlationScore',
name: 'Score',
sortable: true,
},
{
align: RIGHT_ALIGNMENT,
width: '40px',
isExpander: true,
render: (item: any) => (
<EuiButtonIcon
onClick={() => toggleCorrelationDetails(item)}
aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'}
iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'}
/>
),
},
];

const goToCorrelationsPage = () => {
history.push({
pathname: `${ROUTES.CORRELATIONS}`,
state: {
finding: finding,
correlatedFindings: correlatedFindings,
},
});
};

return (
<>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="s">
<h3>Correlated findings</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => goToCorrelationsPage()}
disabled={correlatedFindings.length === 0}
>
View correlations graph
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiInMemoryTable
columns={columns}
items={correlatedFindings}
itemId="id"
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
isExpandable={true}
hasActions={true}
pagination={true}
search={true}
sorting={true}
loading={isLoading}
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
};
Loading

0 comments on commit d9aa818

Please sign in to comment.