Skip to content

Commit

Permalink
Scopes ruleDataService to security-solution and updates timeline to q…
Browse files Browse the repository at this point in the history
…uery alerts as data index if configured
  • Loading branch information
spong committed May 28, 2021
1 parent 91bede9 commit 5201667
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 76 deletions.
4 changes: 2 additions & 2 deletions x-pack/plugins/rule_registry/server/rule_data_client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export class RuleDataClient implements IRuleDataClient {
return clusterClient.bulk(requestWithDefaultParameters).then((response) => {
if (response.body.errors) {
if (
response.body.items.length === 1 &&
response.body.items[0]?.index?.error?.type === 'index_not_found_exception'
response.body.items.length > 0 &&
response.body.items?.[0]?.index?.error?.type === 'index_not_found_exception'
) {
return this.createOrUpdateWriteTarget({ namespace }).then(() => {
return clusterClient.bulk(requestWithDefaultParameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const createPersistenceRuleTypeFactory: CreatePersistenceRuleTypeFactory
...event,
'event.kind': 'signal',
'kibana.rac.alert.id': '???',
'kibana.rac.alert.status': 'open',
'kibana.rac.alert.uuid': v4(),
'kibana.rac.alert.ancestors': isAlert
? ((event['kibana.rac.alert.ancestors'] as string[]) ?? []).concat([
Expand All @@ -101,12 +102,7 @@ export const createPersistenceRuleTypeFactory: CreatePersistenceRuleTypeFactory

if (ruleDataClient && numAlerts) {
await ruleDataClient.getWriter().bulk({
body: currentAlerts.map((alert) => ({
create: {
_index: {},
},
alert,
})),
body: currentAlerts.flatMap((event) => [{ index: {} }, event]),
});
}

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const DEFAULT_TIME_RANGE = 'timepicker:timeDefaults';
export const DEFAULT_REFRESH_RATE_INTERVAL = 'timepicker:refreshIntervalDefaults';
export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults';
export const DEFAULT_APP_REFRESH_INTERVAL = 'securitySolution:refreshIntervalDefaults';
export const DEFAULT_ALERTS_INDEX = '*alerts-security-solution*';
export const DEFAULT_ALERTS_INDEX = '.alerts-security-solution';
export const DEFAULT_SIGNALS_INDEX = '.siem-signals';
export const DEFAULT_LISTS_INDEX = '.lists';
export const DEFAULT_ITEMS_INDEX = '.items';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,30 +167,30 @@ export const buildShowBuildingBlockFilterRuleRegistry = (
},
];

export const requiredFieldsForActionsRuleRegistry = [
'alert.id',
'@timestamp',
'event.kind',
'alert.start',
'alert.uuid',
'event.action',
'alert.status',
'alert.duration.us',
'rule.uuid',
'rule.id',
'rule.name',
'rule.category',
'producer',
'tags',
];
export const requiredFieldMappingsForActionsRuleRegistry = {
'@timestamp': '@timestamp',
'alert.id': 'kibana.rac.alert.id',
'event.kind': 'event.kind',
'alert.start': 'kibana.rac.alert.start',
'alert.uuid': 'kibana.rac.alert.uuid',
'event.action': 'event.action',
'alert.status': 'kibana.rac.alert.status',
'alert.duration.us': 'kibana.rac.alert.duration.us',
'rule.uuid': 'rule.uuid',
'rule.id': 'rule.id',
'rule.name': 'rule.name',
'rule.category': 'rule.category',
producer: 'kibana.rac.alert.producer',
tags: 'tags',
};

export const alertsHeadersRuleRegistry: ColumnHeaderOptions[] = requiredFieldsForActionsRuleRegistry.map<ColumnHeaderOptions>(
(field) => ({
columnHeaderType: defaultColumnHeaderType,
id: field,
width: 120,
})
);
export const alertsHeadersRuleRegistry: ColumnHeaderOptions[] = Object.entries(
requiredFieldMappingsForActionsRuleRegistry
).map<ColumnHeaderOptions>(([alias, field]) => ({
columnHeaderType: defaultColumnHeaderType,
displayAsText: alias,
id: field,
}));

export const alertsDefaultModelRuleRegistry: SubsetTimelineModel = {
...timelineDefaults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@
* 2.0.
*/

import { DEFAULT_ALERTS_INDEX } from '../../../common/constants';
import { TimelineId } from '../../../common/types/timeline';

export const detectionsTimelineIds = [
TimelineId.detectionsPage,
TimelineId.detectionsRulesDetailsPage,
];

export const skipQueryForDetectionsPage = (id: string, defaultIndex: string[]) =>
// TODO: Once we are past experimental phase `useRuleRegistry` should be removed
export const skipQueryForDetectionsPage = (
id: string,
defaultIndex: string[],
useRuleRegistry = false
) =>
id != null &&
detectionsTimelineIds.some((timelineId) => timelineId === id) &&
!defaultIndex.some((di) => di.toLowerCase().startsWith('.siem-signals'));
!defaultIndex.some((di) =>
di.toLowerCase().startsWith(useRuleRegistry ? DEFAULT_ALERTS_INDEX : '.siem-signals')
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Subscription } from 'rxjs';

import { ESQuery } from '../../../common/typed_json';
import { isCompleteResponse, isErrorResponse } from '../../../../../../src/plugins/data/public';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { inputsModel, KueryFilterQueryKind } from '../../common/store';
import { useKibana } from '../../common/lib/kibana';
import { createFilter } from '../../common/containers/helpers';
Expand Down Expand Up @@ -197,6 +198,9 @@ export const useTimelineEvents = ({
});
const { addError, addWarning } = useAppToasts();

// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');

const timelineSearch = useCallback(
(request: TimelineRequest<typeof language> | null) => {
if (request == null || pageName === '' || skip) {
Expand Down Expand Up @@ -305,7 +309,10 @@ export const useTimelineEvents = ({
);

useEffect(() => {
if (skipQueryForDetectionsPage(id, indexNames) || indexNames.length === 0) {
if (
skipQueryForDetectionsPage(id, indexNames, ruleRegistryEnabled) ||
indexNames.length === 0
) {
return;
}

Expand Down Expand Up @@ -364,7 +371,10 @@ export const useTimelineEvents = ({
activeTimeline.setActivePage(newActivePage);
}
}
if (!skipQueryForDetectionsPage(id, indexNames) && !deepEqual(prevRequest, currentRequest)) {
if (
!skipQueryForDetectionsPage(id, indexNames, ruleRegistryEnabled) &&
!deepEqual(prevRequest, currentRequest)
) {
return currentRequest;
}
return prevRequest;
Expand All @@ -380,6 +390,7 @@ export const useTimelineEvents = ({
id,
language,
limit,
ruleRegistryEnabled,
startDate,
sort,
fields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,41 @@ export const createQueryAlertType = (ruleDataClient: RuleDataClient, logger: Log
services: { alertWithPersistence, findAlerts },
params: { indexPatterns, customQuery },
}) {
const indexPattern: IIndexPattern = {
fields: [],
title: indexPatterns.join(),
};
try {
const indexPattern: IIndexPattern = {
fields: [],
title: indexPatterns.join(),
};

// TODO: kql or lucene?
// TODO: kql or lucene?

const esQuery = buildEsQuery(
indexPattern,
{ query: customQuery, language: 'kuery' },
[]
) as QueryContainer;
const query: ESSearchRequest = {
body: {
query: esQuery,
fields: ['*'],
sort: {
'@timestamp': 'asc' as const,
const esQuery = buildEsQuery(
indexPattern,
{ query: customQuery, language: 'kuery' },
[]
) as QueryContainer;
const query: ESSearchRequest = {
body: {
query: esQuery,
fields: ['*'],
sort: {
'@timestamp': 'asc' as const,
},
},
},
};
};

const alerts = await findAlerts(query);
alertWithPersistence(alerts).forEach((alert) => {
alert.scheduleActions('default', { server: 'server-test' });
});
const alerts = await findAlerts(query);
// console.log('alerts', alerts);
alertWithPersistence(alerts).forEach((alert) => {
alert.scheduleActions('default', { server: 'server-test' });
});

return {
lastChecked: new Date(),
};
return {
lastChecked: new Date(),
};
} catch (error) {
logger.error(error);
}
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
*/

import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils';
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
import { ConfigType } from '../../../../config';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants';
import { DEFAULT_ALERTS_INDEX, DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants';

import { buildSiemResponse } from '../utils';
import { SIGNALS_TEMPLATE_VERSION } from './get_signals_template';
import { getIndexVersion } from './get_index_version';
import { isOutdated } from '../../migrations/helpers';

export const readIndexRoute = (router: SecuritySolutionPluginRouter) => {
export const readIndexRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => {
router.get(
{
path: DETECTION_ENGINE_INDEX_URL,
Expand All @@ -34,8 +36,16 @@ export const readIndexRoute = (router: SecuritySolutionPluginRouter) => {
return siemResponse.error({ statusCode: 404 });
}

// TODO: Once we are past experimental phase this code should be removed
const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental);
if (ruleRegistryEnabled) {
return response.ok({
body: { name: DEFAULT_ALERTS_INDEX, index_mapping_outdated: false },
});
}

const index = siemClient.getSignalsIndex();
const indexExists = await getIndexExists(esClient, index);
const indexExists = ruleRegistryEnabled ? true : await getIndexExists(esClient, index);

if (indexExists) {
let mappingOutdated: boolean | null = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
getSignalsAggsAndQueryRequest,
getEmptySignalsResponse,
} from '../__mocks__/request_responses';
import { requestContextMock, serverMock, requestMock } from '../__mocks__';
import { requestContextMock, serverMock, requestMock, createMockConfig } from '../__mocks__';
import { querySignalsRoute } from './query_signals_route';

describe('query for signal', () => {
Expand All @@ -27,7 +27,7 @@ describe('query for signal', () => {

clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptySignalsResponse());

querySignalsRoute(server.router);
querySignalsRoute(server.router, createMockConfig());
});

describe('query and agg on signals index', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
*/

import { transformError } from '@kbn/securitysolution-es-utils';
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
import { ConfigType } from '../../../../config';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants';
import {
DEFAULT_ALERTS_INDEX,
DETECTION_ENGINE_QUERY_SIGNALS_URL,
} from '../../../../../common/constants';
import { buildSiemResponse } from '../utils';
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';

Expand All @@ -16,7 +21,7 @@ import {
QuerySignalsSchemaDecoded,
} from '../../../../../common/detection_engine/schemas/request/query_signals_index_schema';

export const querySignalsRoute = (router: SecuritySolutionPluginRouter) => {
export const querySignalsRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => {
router.post(
{
path: DETECTION_ENGINE_QUERY_SIGNALS_URL,
Expand Down Expand Up @@ -48,9 +53,12 @@ export const querySignalsRoute = (router: SecuritySolutionPluginRouter) => {
const clusterClient = context.core.elasticsearch.legacy.client;
const siemClient = context.securitySolution!.getAppClient();

// TODO: Once we are past experimental phase this code should be removed
const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental);

try {
const result = await clusterClient.callAsCurrentUser('search', {
index: siemClient.getSignalsIndex(),
index: ruleRegistryEnabled ? DEFAULT_ALERTS_INDEX : siemClient.getSignalsIndex(),
body: { query, aggs, _source, track_total_hits, size },
ignoreUnavailable: true,
});
Expand Down
Loading

0 comments on commit 5201667

Please sign in to comment.