Skip to content

Commit

Permalink
Created untriaged issue workflow. (opensearch-project#410)
Browse files Browse the repository at this point in the history
Signed-off-by: dblock <[email protected]>
Signed-off-by: Amardeepsingh Siglani <[email protected]>
  • Loading branch information
dblock authored and amsiglan committed Feb 22, 2023
2 parents 881f7bb + 9a62208 commit 82a6308
Show file tree
Hide file tree
Showing 21 changed files with 1,860 additions and 4,080 deletions.
39 changes: 36 additions & 3 deletions cypress/integration/1_detectors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
import sample_index_settings from '../fixtures/sample_index_settings.json';
import dns_rule_data from '../fixtures/integration_tests/rule/create_dns_rule.json';
import sample_dns_settings from '../fixtures/integration_tests/index/create_dns_settings.json';

const testMappings = {
properties: {
Expand All @@ -16,14 +17,15 @@ const testMappings = {
},
};

const cypressDNSRule = 'Cypress DNS Rule';
const cypressDNSRule = dns_rule_data.title;

describe('Detectors', () => {
const indexName = 'cypress-test-dns';
const detectorName = 'test detector';

before(() => {
cy.cleanUpTests();

// Create test index
cy.createIndex(indexName, sample_index_settings).then(() =>
cy
Expand All @@ -41,8 +43,6 @@ describe('Detectors', () => {
);

cy.createRule(dns_rule_data);

cy.contains(detectorName).should('not.exist');
});

beforeEach(() => {
Expand All @@ -57,6 +57,39 @@ describe('Detectors', () => {
});
});

it('...should show mappings warning', () => {
const indexName = 'cypress-index-windows';
const dnsName = 'cypress-index-dns';
cy.createIndex(indexName, sample_index_settings);
cy.createIndex(dnsName, sample_dns_settings);

// Locate Create detector button click to start
cy.get('.euiButton').filter(':contains("Create detector")').click({ force: true });

// Check to ensure process started
cy.waitForPageLoad('create-detector', {
contains: 'Define detector',
});

// Select our pre-seeded data source (check indexName)
cy.get(`[data-test-subj="define-detector-select-data-source"]`)
.find('input')
.focus()
.realType(indexName);

// Select threat detector type (Windows logs)
cy.get(`input[id="dns"]`).click({ force: true });

// Select our pre-seeded data source (check indexName)
cy.get(`[data-test-subj="define-detector-select-data-source"]`)
.find('input')
.focus()
.realType(dnsName)
.realPress('Enter');

cy.get('.euiCallOut').should('be.visible').contains('Detector configuration warning');
});

it('...can be created', () => {
// Locate Create detector button click to start
cy.get('.euiButton').filter(':contains("Create detector")').click({ force: true });
Expand Down
73 changes: 71 additions & 2 deletions public/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ $euiColorGhost: #FFF !default;
$euiColorInk: #000 !default;
$euiTextColor: $euiColorDarkestShade !default;

@import "./components/Charts/ChartContainer.scss";
@import "./pages/Overview/components/Widgets/WidgetContainer.scss";

.selected-radio-panel {
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
border-color: $euiColorPrimary;
Expand Down Expand Up @@ -45,5 +48,71 @@ $euiTextColor: $euiColorDarkestShade !default;
text-decoration: none;
}

@import "./components/Charts/ChartContainer.scss";
@import "./pages/Overview/components/Widgets/WidgetContainer.scss";
.vg-tooltip {
background-color: #404040 !important;
border-radius: 4px !important;
color: #FFFFFF !important;
border: none !important;
padding: 0 !important;
font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol" !important;
font-weight: 600 !important;
font-size: 12px !important;

.vg-tooltip-innerContainer {
.vg-tooltip-header {
padding: 4px;
border-bottom: 1px solid gray;

table tr:nth-child(odd) {
background-color: #535353 !important;
}

td:nth-child(1) {
position: relative;
width: 6px;
padding: 0 0 0 2px !important;

.vg-tooltip-color {
width: 4px;
position: absolute;
top: 2px;
height: calc(100% - 4px);
}
}

td:nth-child(3) {
padding-right: 8px !important;
text-align: right;
}
}

.vg-tooltip-body {
padding: 4px;

table tr:nth-child(even) {
background-color: #535353 !important;
}

td:nth-child(2) {
text-align: right;
}
}

table {
width: 100%;

tbody tr {
td {
height: 28px;
padding: 4px !important;
vertical-align: middle !important;
}

td:nth-child(1) {
padding-right: 10px !important;
font-weight: 400 !important;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
EuiIcon,
EuiInMemoryTable,
EuiText,
EuiToolTip,
} from '@elastic/eui';
import { DEFAULT_EMPTY_DATA } from '../../../../../../utils/constants';
import { STATUS_ICON_PROPS } from '../../utils/constants';
Expand Down Expand Up @@ -140,14 +141,22 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
const { existingMappings: createdMappings, invalidMappingFieldNames } = this.props
.mappingProps as MappingProps[MappingViewType.Edit];
let iconProps = STATUS_ICON_PROPS['unmapped'];
let iconTooltip = 'This field needs to be mapped with a field from your log source.';
if (
createdMappings[entry.ruleFieldName] &&
!invalidMappingFieldNames.includes(entry.ruleFieldName)
) {
iconProps = STATUS_ICON_PROPS['mapped'];
iconTooltip = 'This field has been mapped.';
}

return <EuiIcon {...iconProps} /> || DEFAULT_EMPTY_DATA;
return (
(
<EuiToolTip position="top" content={iconTooltip}>
<EuiIcon {...iconProps} />
</EuiToolTip>
) || DEFAULT_EMPTY_DATA
);
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export default class DetectorDataSource extends Component<
error={isInvalid && (errorMessage || 'Select an input source.')}
>
<EuiComboBox
singleSelection={{ asPlainText: true }}
placeholder={'Select an input source for the detector.'}
isLoading={loading}
options={indexOptions}
Expand All @@ -110,6 +109,7 @@ export default class DetectorDataSource extends Component<
onChange={this.onSelectionChange}
onCreateOption={this.onCreateOption}
isInvalid={!!errorMessage}
isClearable={true}
data-test-subj={'define-detector-select-data-source'}
/>
</EuiFormRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { EuiSpacer, EuiTitle, EuiText } from '@elastic/eui';
import { EuiSpacer, EuiTitle, EuiText, EuiCallOut, EuiTextColor } from '@elastic/eui';
import { Detector, PeriodSchedule } from '../../../../../../models/interfaces';
import DetectorBasicDetailsForm from '../components/DetectorDetails';
import DetectorDataSource from '../components/DetectorDataSource';
import DetectorType from '../components/DetectorType';
import { EuiComboBoxOptionOption } from '@opensearch-project/oui';
import { IndexService } from '../../../../../services';
import { FieldMappingService, IndexService } from '../../../../../services';
import { MIN_NUM_DATA_SOURCES } from '../../../../Detectors/utils/constants';
import { DetectorCreationStep } from '../../../models/types';
import { DetectorSchedule } from '../components/DetectorSchedule/DetectorSchedule';
Expand All @@ -21,11 +21,13 @@ import {
DetectionRules,
} from '../components/DetectionRules/DetectionRules';
import { NotificationsStart } from 'opensearch-dashboards/public';
import _ from 'lodash';

interface DefineDetectorProps extends RouteComponentProps {
detector: Detector;
isEdit: boolean;
indexService: IndexService;
filedMappingService: FieldMappingService;
rulesState: CreateDetectorRulesState;
notifications: NotificationsStart;
loadingRules?: boolean;
Expand All @@ -36,16 +38,66 @@ interface DefineDetectorProps extends RouteComponentProps {
onAllRulesToggle: (enabled: boolean) => void;
}

interface DefineDetectorState {}
interface DefineDetectorState {
message: string[];
}

export default class DefineDetector extends Component<DefineDetectorProps, DefineDetectorState> {
updateDetectorCreationState(detector: Detector) {
const isDataValid =
state = {
message: [],
};

private indicesMappings: any = {};

async updateDetectorCreationState(detector: Detector) {
let isDataValid =
!!detector.name &&
!!detector.detector_type &&
detector.inputs[0].detector_input.indices.length >= MIN_NUM_DATA_SOURCES &&
!!detector.schedule.period.interval;
this.props.changeDetector(detector);

const allIndices = detector.inputs[0].detector_input.indices;
for (let indexName in this.indicesMappings) {
if (allIndices.indexOf(indexName) === -1) {
// cleanup removed indexes
delete this.indicesMappings[indexName];
}
}

for (const indexName of allIndices) {
if (!this.indicesMappings[indexName]) {
const detectorType = this.props.detector.detector_type.toLowerCase();
const result = await this.props.filedMappingService.getMappingsView(
indexName,
detectorType
);
result.ok && (this.indicesMappings[indexName] = result.response.unmapped_field_aliases);
}
}

if (!_.isEmpty(this.indicesMappings)) {
let firstMapping: string[] = [];
let firstMatchMappingIndex: string = '';
let message: string[] = [];
for (let indexName in this.indicesMappings) {
if (this.indicesMappings.hasOwnProperty(indexName)) {
if (!firstMapping.length) firstMapping = this.indicesMappings[indexName];
!firstMatchMappingIndex.length && (firstMatchMappingIndex = indexName);
if (!_.isEqual(firstMapping, this.indicesMappings[indexName])) {
message = [
`The below log sources don't have the same fields, please consider creating separate detectors for them.`,
firstMatchMappingIndex,
indexName,
];
break;
}
}
}

this.setState({ message });
}

this.props.updateDataValidState(DetectorCreationStep.DEFINE_DETECTOR, isDataValid);
}

Expand Down Expand Up @@ -162,6 +214,7 @@ export default class DefineDetector extends Component<DefineDetectorProps, Defin
onPageChange,
onAllRulesToggle,
} = this.props;
const { message } = this.state;
const { name, inputs, detector_type } = this.props.detector;
const { description, indices } = inputs[0].detector_input;

Expand All @@ -177,6 +230,22 @@ export default class DefineDetector extends Component<DefineDetectorProps, Defin
</EuiText>

<EuiSpacer size={'m'} />
{message.length ? (
<>
<EuiCallOut title="Detector configuration warning" color="warning" iconType="alert">
{message.map((messageItem: string, index: number) => (
<EuiTextColor
color={index === 0 ? 'default' : 'warning'}
key={`callout-message-part-${index}`}
>
{messageItem}
<br />
</EuiTextColor>
))}
</EuiCallOut>
<EuiSpacer size={'m'} />
</>
) : null}

<DetectorBasicDetailsForm
{...this.props}
Expand Down
9 changes: 4 additions & 5 deletions public/pages/CreateDetector/containers/CreateDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { createDetectorSteps } from '../utils/constants';
import {
BREADCRUMBS,
EMPTY_DEFAULT_DETECTOR,
OS_NOTIFICATION_PLUGIN,
PLUGIN_NAME,
ROUTES,
OS_NOTIFICATION_PLUGIN,
logTypesWithDashboards,
pendingDashboardCreations,
} from '../../../utils/constants';
Expand All @@ -34,8 +34,8 @@ import {
import { NotificationsStart } from 'opensearch-dashboards/public';
import {
errorNotificationToast,
successNotificationToast,
getPlugins,
successNotificationToast,
} from '../../../utils/helpers';
import { RulesViewModelActor } from '../../Rules/models/RulesViewModelActor';

Expand Down Expand Up @@ -196,14 +196,12 @@ export default class CreateDetector extends Component<CreateDetectorProps, Creat

getRulesOptions(): CreateDetectorRulesOptions {
const enabledRules = this.state.rulesState.allRules.filter((rule) => rule.enabled);
const options: CreateDetectorRulesOptions = enabledRules.map((rule) => ({
return enabledRules.map((rule) => ({
id: rule._id,
name: rule._source.title,
severity: rule._source.level,
tags: rule._source.tags.map((tag: { value: string }) => tag.value),
}));

return options;
}

async setupRulesState() {
Expand Down Expand Up @@ -327,6 +325,7 @@ export default class CreateDetector extends Component<CreateDetectorProps, Creat
{...this.props}
detector={this.state.detector}
indexService={services.indexService}
filedMappingService={services.fieldMappingService}
rulesState={this.state.rulesState}
loadingRules={this.state.loadingRules}
onRuleToggle={this.onRuleToggle}
Expand Down
Loading

0 comments on commit 82a6308

Please sign in to comment.