mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* prototyping * how dis * RAC rules create API * Find rules (in progress) * Finalize find_rules route * A couple more routes, and type error fixes * Fix integration tests? * Fix tests * Fix imports * Add ref * Test fixes * Fix refs * Type fixes * Test fixes * Remove console log * Update rule changes * Test and type fixes * Fix patch rule tests * Fix types * Begin removing namespace as required param * Remove generics * Support RAC everywhere * Tests passing * Types * Keep on passing isRuleRegistryEnabled around * Rewrite install_prepackaged_timelines helper tests
This commit is contained in:
parent
5dea5bbe5f
commit
a9d1b5ce3e
94 changed files with 1060 additions and 546 deletions
|
@ -12,6 +12,9 @@ import { PluginInitializerContext } from 'src/core/server';
|
|||
import { RuleRegistryPlugin } from './plugin';
|
||||
|
||||
export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin';
|
||||
export { RuleDataPluginService } from './rule_data_plugin_service';
|
||||
export { RuleDataClient } from './rule_data_client';
|
||||
export { IRuleDataClient } from './rule_data_client/types';
|
||||
export type {
|
||||
RacRequestHandlerContext,
|
||||
RacApiRequestHandlerContext,
|
||||
|
|
|
@ -187,19 +187,18 @@ export const DEFAULT_TRANSFORMS_SETTING = JSON.stringify(defaultTransformsSettin
|
|||
/**
|
||||
* Id for the signals alerting type
|
||||
*/
|
||||
export const SIGNALS_ID = `siem.signals`;
|
||||
export const SIGNALS_ID = `siem.signals` as const;
|
||||
|
||||
/**
|
||||
* Id's for reference rule types
|
||||
* IDs for RAC rule types
|
||||
*/
|
||||
export const REFERENCE_RULE_ALERT_TYPE_ID = `siem.referenceRule`;
|
||||
export const REFERENCE_RULE_PERSISTENCE_ALERT_TYPE_ID = `siem.referenceRulePersistence`;
|
||||
|
||||
export const QUERY_ALERT_TYPE_ID = `siem.queryRule`;
|
||||
export const EQL_ALERT_TYPE_ID = `siem.eqlRule`;
|
||||
export const INDICATOR_ALERT_TYPE_ID = `siem.indicatorRule`;
|
||||
export const ML_ALERT_TYPE_ID = `siem.mlRule`;
|
||||
export const THRESHOLD_ALERT_TYPE_ID = `siem.thresholdRule`;
|
||||
const RULE_TYPE_PREFIX = `siem` as const;
|
||||
export const EQL_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.eqlRule` as const;
|
||||
export const INDICATOR_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.indicatorRule` as const;
|
||||
export const ML_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.mlRule` as const;
|
||||
export const QUERY_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.queryRule` as const;
|
||||
export const SAVED_QUERY_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.savedQueryRule` as const;
|
||||
export const THRESHOLD_RULE_TYPE_ID = `${RULE_TYPE_PREFIX}.thresholdRule` as const;
|
||||
|
||||
/**
|
||||
* Id for the notifications alerting type
|
||||
|
|
|
@ -59,6 +59,9 @@ export type FileName = t.TypeOf<typeof file_name>;
|
|||
export const exclude_export_details = t.boolean;
|
||||
export type ExcludeExportDetails = t.TypeOf<typeof exclude_export_details>;
|
||||
|
||||
export const namespace = t.string;
|
||||
export type Namespace = t.TypeOf<typeof namespace>;
|
||||
|
||||
/**
|
||||
* TODO: Right now the filters is an "unknown", when it could more than likely
|
||||
* become the actual ESFilter as a type.
|
||||
|
@ -352,6 +355,9 @@ export const timelines_not_updated = PositiveInteger;
|
|||
export const note = t.string;
|
||||
export type Note = t.TypeOf<typeof note>;
|
||||
|
||||
export const namespaceOrUndefined = t.union([namespace, t.undefined]);
|
||||
export type NamespaceOrUndefined = t.TypeOf<typeof namespaceOrUndefined>;
|
||||
|
||||
export const noteOrUndefined = t.union([note, t.undefined]);
|
||||
export type NoteOrUndefined = t.TypeOf<typeof noteOrUndefined>;
|
||||
|
||||
|
|
|
@ -465,6 +465,23 @@ describe('add prepackaged rules schema', () => {
|
|||
expect(message.schema).toEqual(expected);
|
||||
});
|
||||
|
||||
test('You can send in a namespace', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: AddPrepackagedRulesSchemaDecoded = {
|
||||
...getAddPrepackagedRulesSchemaDecodedMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
expect(message.schema).toEqual(expected);
|
||||
});
|
||||
|
||||
test('You can send in an empty array to threat', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
|
@ -71,6 +71,7 @@ import {
|
|||
timestamp_override,
|
||||
Author,
|
||||
event_category_override,
|
||||
namespace,
|
||||
} from '../common/schemas';
|
||||
|
||||
/**
|
||||
|
@ -136,10 +137,10 @@ export const addPrepackagedRulesSchema = t.intersection([
|
|||
threat_indicator_path, // defaults "undefined" if not set during decode
|
||||
concurrent_searches, // defaults to "undefined" if not set during decode
|
||||
items_per_search, // defaults to "undefined" if not set during decode
|
||||
namespace, // defaults to "undefined" if not set during decode
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
||||
export type AddPrepackagedRulesSchema = t.TypeOf<typeof addPrepackagedRulesSchema>;
|
||||
|
||||
// This type is used after a decode since some things are defaults after a decode.
|
||||
|
@ -153,6 +154,7 @@ export type AddPrepackagedRulesSchemaDecoded = Omit<
|
|||
| 'from'
|
||||
| 'interval'
|
||||
| 'max_signals'
|
||||
| 'namespace'
|
||||
| 'risk_score_mapping'
|
||||
| 'severity_mapping'
|
||||
| 'tags'
|
||||
|
@ -176,4 +178,5 @@ export type AddPrepackagedRulesSchemaDecoded = Omit<
|
|||
threat: Threats;
|
||||
throttle: ThrottleOrNull;
|
||||
exceptions_list: ListArray;
|
||||
namespace?: string;
|
||||
};
|
||||
|
|
|
@ -330,6 +330,19 @@ describe('create rules schema', () => {
|
|||
expect(message.schema).toEqual(payload);
|
||||
});
|
||||
|
||||
test('You can send in a namespace', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
});
|
||||
|
||||
test('You can send in an empty array to threat', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
|
|
|
@ -68,6 +68,7 @@ import {
|
|||
last_success_message,
|
||||
last_failure_at,
|
||||
last_failure_message,
|
||||
namespace,
|
||||
} from '../common/schemas';
|
||||
|
||||
export const createSchema = <
|
||||
|
@ -155,6 +156,7 @@ const baseParams = {
|
|||
meta,
|
||||
rule_name_override,
|
||||
timestamp_override,
|
||||
namespace,
|
||||
},
|
||||
defaultable: {
|
||||
tags,
|
||||
|
|
|
@ -187,6 +187,18 @@ describe('update_rules_bulk_schema', () => {
|
|||
expect(output.schema).toEqual({});
|
||||
});
|
||||
|
||||
test('You can set "namespace" to a string', () => {
|
||||
const payload: UpdateRulesBulkSchema = [
|
||||
{ ...getUpdateRulesSchemaMock(), namespace: 'a namespace' },
|
||||
];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
expect(output.schema).toEqual(payload);
|
||||
});
|
||||
|
||||
test('You can set "note" to a string', () => {
|
||||
const payload: UpdateRulesBulkSchema = [
|
||||
{ ...getUpdateRulesSchemaMock(), note: '# test markdown' },
|
||||
|
|
|
@ -404,6 +404,19 @@ describe('rules_schema', () => {
|
|||
expect(message.schema).toEqual(expected);
|
||||
});
|
||||
|
||||
test('it should validate a namespace as string', () => {
|
||||
const payload = {
|
||||
...getRulesSchemaMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
const dependents = getDependents(payload);
|
||||
const decoded = dependents.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should NOT validate invalid_data for the type', () => {
|
||||
const payload: Omit<RulesSchema, 'type'> & { type: string } = getRulesSchemaMock();
|
||||
payload.type = 'invalid_data';
|
||||
|
|
|
@ -75,6 +75,7 @@ import {
|
|||
license,
|
||||
rule_name_override,
|
||||
timestamp_override,
|
||||
namespace,
|
||||
} from '../common/schemas';
|
||||
|
||||
import { typeAndTimelineOnlySchema, TypeAndTimelineOnly } from './type_timeline_only_schema';
|
||||
|
@ -174,6 +175,7 @@ export const partialRulesSchema = t.partial({
|
|||
filters,
|
||||
meta,
|
||||
index,
|
||||
namespace,
|
||||
note,
|
||||
});
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ describe('schedule_throttle_notification_actions', () => {
|
|||
to: 'now',
|
||||
type: 'query',
|
||||
references: ['http://www.example.com'],
|
||||
namespace: 'a namespace',
|
||||
note: '# sample markdown',
|
||||
version: 1,
|
||||
exceptionsList: [],
|
||||
|
|
|
@ -39,6 +39,7 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
import { getPerformBulkActionSchemaMock } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema.mock';
|
||||
import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { FindBulkExecutionLogResponse } from '../../rule_execution_log/types';
|
||||
import { ruleTypeMappings } from '../../signals/utils';
|
||||
|
||||
export const typicalSetStatusSignalByIdsPayload = (): SetSignalsStatusSchemaDecoded => ({
|
||||
signal_ids: ['somefakeid1', 'somefakeid2'],
|
||||
|
@ -179,18 +180,18 @@ export const getEmptyFindResult = (): FindHit => ({
|
|||
data: [],
|
||||
});
|
||||
|
||||
export const getFindResultWithSingleHit = (): FindHit => ({
|
||||
export const getFindResultWithSingleHit = (isRuleRegistryEnabled: boolean): FindHit => ({
|
||||
page: 1,
|
||||
perPage: 1,
|
||||
total: 1,
|
||||
data: [getAlertMock(getQueryRuleParams())],
|
||||
data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())],
|
||||
});
|
||||
|
||||
export const nonRuleFindResult = (): FindHit => ({
|
||||
export const nonRuleFindResult = (isRuleRegistryEnabled: boolean): FindHit => ({
|
||||
page: 1,
|
||||
perPage: 1,
|
||||
total: 1,
|
||||
data: [nonRuleAlert()],
|
||||
data: [nonRuleAlert(isRuleRegistryEnabled)],
|
||||
});
|
||||
|
||||
export const getFindResultWithMultiHits = ({
|
||||
|
@ -348,19 +349,22 @@ export const createActionResult = (): ActionResult => ({
|
|||
isPreconfigured: false,
|
||||
});
|
||||
|
||||
export const nonRuleAlert = () => ({
|
||||
export const nonRuleAlert = (isRuleRegistryEnabled: boolean) => ({
|
||||
// Defaulting to QueryRuleParams because ts doesn't like empty objects
|
||||
...getAlertMock(getQueryRuleParams()),
|
||||
...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bc',
|
||||
name: 'Non-Rule Alert',
|
||||
alertTypeId: 'something',
|
||||
});
|
||||
|
||||
export const getAlertMock = <T extends RuleParams>(params: T): Alert<T> => ({
|
||||
export const getAlertMock = <T extends RuleParams>(
|
||||
isRuleRegistryEnabled: boolean,
|
||||
params: T
|
||||
): Alert<T> => ({
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
name: 'Detect Root/Admin Users',
|
||||
tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`],
|
||||
alertTypeId: 'siem.signals',
|
||||
alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[params.type] : 'siem.signals',
|
||||
consumer: 'siem',
|
||||
params,
|
||||
createdAt: new Date('2019-12-13T16:40:33.400Z'),
|
||||
|
|
|
@ -72,12 +72,16 @@ jest.mock('../../../timeline/routes/prepackaged_timelines/install_prepackaged_ti
|
|||
};
|
||||
});
|
||||
|
||||
describe('add_prepackaged_rules_route', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('add_prepackaged_rules_route - %s', (_, isRuleRegistryEnabled) => {
|
||||
const siemMockClient = siemMock.createClient();
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let securitySetup: SecurityPluginSetup;
|
||||
let mockExceptionsClient: ExceptionListClient;
|
||||
const testif = isRuleRegistryEnabled ? test.skip : test;
|
||||
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
|
@ -91,8 +95,10 @@ describe('add_prepackaged_rules_route', () => {
|
|||
|
||||
mockExceptionsClient = listMock.getExceptionListClient();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
|
||||
(installPrepackagedTimelines as jest.Mock).mockReset();
|
||||
(installPrepackagedTimelines as jest.Mock).mockResolvedValue({
|
||||
|
@ -106,7 +112,7 @@ describe('add_prepackaged_rules_route', () => {
|
|||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
);
|
||||
addPrepackedRulesRoute(server.router, createMockConfig(), securitySetup);
|
||||
addPrepackedRulesRoute(server.router, createMockConfig(), securitySetup, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
|
@ -129,23 +135,25 @@ describe('add_prepackaged_rules_route', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('it returns a 400 if the index does not exist', async () => {
|
||||
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
|
||||
const request = addPrepackagedRulesRequest();
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
);
|
||||
const response = await server.inject(request, context);
|
||||
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.body).toEqual({
|
||||
status_code: 400,
|
||||
message: expect.stringContaining(
|
||||
'Pre-packaged rules cannot be installed until the signals index is created'
|
||||
),
|
||||
});
|
||||
expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400);
|
||||
if (!isRuleRegistryEnabled) {
|
||||
expect(response.body).toEqual({
|
||||
status_code: 400,
|
||||
message: expect.stringContaining(
|
||||
'Pre-packaged rules cannot be installed until the signals index is created'
|
||||
),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('returns 404 if siem client is unavailable', async () => {
|
||||
test('returns 404 if siem client is unavailable', async () => {
|
||||
const { securitySolution, ...contextWithoutSecuritySolution } = context;
|
||||
const response = await server.inject(
|
||||
addPrepackagedRulesRequest(),
|
||||
|
@ -185,16 +193,19 @@ describe('add_prepackaged_rules_route', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('catches errors if payloads cause errors to be thrown', async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createErrorTransportRequestPromise(new Error('Test error'))
|
||||
);
|
||||
const request = addPrepackagedRulesRequest();
|
||||
const response = await server.inject(request, context);
|
||||
testif(
|
||||
'catches errors if signals index does not exist when rule registry not enabled',
|
||||
async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createErrorTransportRequestPromise(new Error('Test error'))
|
||||
);
|
||||
const request = addPrepackagedRulesRequest();
|
||||
const response = await server.inject(request, context);
|
||||
|
||||
expect(response.status).toEqual(500);
|
||||
expect(response.body).toEqual({ message: 'Test error', status_code: 500 });
|
||||
});
|
||||
expect(response.status).toEqual(500);
|
||||
expect(response.body).toEqual({ message: 'Test error', status_code: 500 });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('should install prepackaged timelines', async () => {
|
||||
|
|
|
@ -43,7 +43,8 @@ import { installPrepackagedTimelines } from '../../../timeline/routes/prepackage
|
|||
export const addPrepackedRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
config: ConfigType,
|
||||
security: SetupPlugins['security']
|
||||
security: SetupPlugins['security'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.put(
|
||||
{
|
||||
|
@ -79,7 +80,9 @@ export const addPrepackedRulesRoute = (
|
|||
frameworkRequest,
|
||||
config.maxTimelineImportExportSize,
|
||||
config.prebuiltRulesFromFileSystem,
|
||||
config.prebuiltRulesFromSavedObjects
|
||||
config.prebuiltRulesFromSavedObjects,
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
return response.ok({ body: validated ?? {} });
|
||||
} catch (err) {
|
||||
|
@ -109,7 +112,8 @@ export const createPrepackagedRules = async (
|
|||
maxTimelineImportExportSize: ConfigType['maxTimelineImportExportSize'],
|
||||
prebuiltRulesFromFileSystem: ConfigType['prebuiltRulesFromFileSystem'],
|
||||
prebuiltRulesFromSavedObjects: ConfigType['prebuiltRulesFromSavedObjects'],
|
||||
exceptionsClient?: ExceptionListClient
|
||||
exceptionsClient?: ExceptionListClient,
|
||||
isRuleRegistryEnabled?: boolean | undefined
|
||||
): Promise<PrePackagedRulesAndTimelinesSchema | null> => {
|
||||
const esClient = context.core.elasticsearch.client;
|
||||
const savedObjectsClient = context.core.savedObjects.client;
|
||||
|
@ -131,11 +135,14 @@ export const createPrepackagedRules = async (
|
|||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects
|
||||
);
|
||||
const prepackagedRules = await getExistingPrepackagedRules({ rulesClient });
|
||||
const prepackagedRules = await getExistingPrepackagedRules({
|
||||
rulesClient,
|
||||
isRuleRegistryEnabled: isRuleRegistryEnabled ?? false,
|
||||
});
|
||||
const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules);
|
||||
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules);
|
||||
const signalsIndex = siemClient.getSignalsIndex();
|
||||
if (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0) {
|
||||
if (!isRuleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) {
|
||||
const signalsIndexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex);
|
||||
if (!signalsIndexExists) {
|
||||
throw new PrepackagedRulesError(
|
||||
|
@ -145,7 +152,14 @@ export const createPrepackagedRules = async (
|
|||
}
|
||||
}
|
||||
|
||||
await Promise.all(installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex));
|
||||
await Promise.all(
|
||||
installPrepackagedRules(
|
||||
rulesClient,
|
||||
rulesToInstall,
|
||||
signalsIndex,
|
||||
isRuleRegistryEnabled ?? false
|
||||
)
|
||||
);
|
||||
const timeline = await installPrepackagedTimelines(
|
||||
maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
|
@ -160,7 +174,8 @@ export const createPrepackagedRules = async (
|
|||
context.securitySolution.getSpaceId(),
|
||||
ruleStatusClient,
|
||||
rulesToUpdate,
|
||||
signalsIndex
|
||||
signalsIndex,
|
||||
isRuleRegistryEnabled ?? false
|
||||
);
|
||||
|
||||
const prepackagedRulesOutput: PrePackagedRulesAndTimelinesSchema = {
|
||||
|
|
|
@ -24,7 +24,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('create_rules_bulk', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('create_rules_bulk - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -35,12 +38,14 @@ describe('create_rules_bulk', () => {
|
|||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules
|
||||
clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful creation
|
||||
clients.rulesClient.create.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // successful creation
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
);
|
||||
createRulesBulkRoute(server.router, ml);
|
||||
createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
|
@ -56,7 +61,7 @@ describe('create_rules_bulk', () => {
|
|||
expect(response.body).toEqual({ message: 'Not Found', status_code: 404 });
|
||||
});
|
||||
|
||||
it('returns 404 if siem client is unavailable', async () => {
|
||||
test('returns 404 if siem client is unavailable', async () => {
|
||||
const { securitySolution, ...contextWithoutSecuritySolution } = context;
|
||||
// @ts-expect-error
|
||||
const response = await server.inject(getReadBulkRequest(), contextWithoutSecuritySolution);
|
||||
|
@ -66,7 +71,7 @@ describe('create_rules_bulk', () => {
|
|||
});
|
||||
|
||||
describe('unhappy paths', () => {
|
||||
it('returns a 403 error object if ML Authz fails', async () => {
|
||||
test('returns a 403 error object if ML Authz fails', async () => {
|
||||
(buildMlAuthz as jest.Mock).mockReturnValueOnce({
|
||||
validateRuleType: jest
|
||||
.fn()
|
||||
|
@ -86,26 +91,30 @@ describe('create_rules_bulk', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('returns an error object if the index does not exist', async () => {
|
||||
test('returns an error object if the index does not exist when rule registry not enabled', async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
);
|
||||
const response = await server.inject(getReadBulkRequest(), context);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual([
|
||||
{
|
||||
error: {
|
||||
message: 'To create a rule, the index must exist first. Index undefined does not exist',
|
||||
status_code: 400,
|
||||
|
||||
if (!isRuleRegistryEnabled) {
|
||||
expect(response.body).toEqual([
|
||||
{
|
||||
error: {
|
||||
message:
|
||||
'To create a rule, the index must exist first. Index undefined does not exist',
|
||||
status_code: 400,
|
||||
},
|
||||
rule_id: 'rule-1',
|
||||
},
|
||||
rule_id: 'rule-1',
|
||||
},
|
||||
]);
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
test('returns a duplicate error if rule_id already exists', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const response = await server.inject(getReadBulkRequest(), context);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
@ -136,7 +145,7 @@ describe('create_rules_bulk', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('returns an error object if duplicate rule_ids found in request payload', async () => {
|
||||
test('returns an error object if duplicate rule_ids found in request payload', async () => {
|
||||
const request = requestMock.create({
|
||||
method: 'post',
|
||||
path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`,
|
||||
|
|
|
@ -28,7 +28,8 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'
|
|||
|
||||
export const createRulesBulkRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml']
|
||||
ml: SetupPlugins['ml'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.post(
|
||||
{
|
||||
|
@ -67,9 +68,10 @@ export const createRulesBulkRoute = (
|
|||
.map(async (payloadRule) => {
|
||||
if (payloadRule.rule_id != null) {
|
||||
const rule = await readRules({
|
||||
id: undefined,
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId: payloadRule.rule_id,
|
||||
id: undefined,
|
||||
});
|
||||
if (rule != null) {
|
||||
return createBulkErrorObject({
|
||||
|
@ -79,7 +81,11 @@ export const createRulesBulkRoute = (
|
|||
});
|
||||
}
|
||||
}
|
||||
const internalRule = convertCreateAPIToInternalSchema(payloadRule, siemClient);
|
||||
const internalRule = convertCreateAPIToInternalSchema(
|
||||
payloadRule,
|
||||
siemClient,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
try {
|
||||
const validationErrors = createRuleValidateTypeDependents(payloadRule);
|
||||
if (validationErrors.length) {
|
||||
|
@ -93,7 +99,7 @@ export const createRulesBulkRoute = (
|
|||
throwHttpError(await mlAuthz.validateRuleType(internalRule.params.type));
|
||||
const finalIndex = internalRule.params.outputIndex;
|
||||
const indexExists = await getIndexExists(esClient.asCurrentUser, finalIndex);
|
||||
if (!indexExists) {
|
||||
if (!isRuleRegistryEnabled && !indexExists) {
|
||||
return createBulkErrorObject({
|
||||
ruleId: internalRule.params.ruleId,
|
||||
statusCode: 400,
|
||||
|
@ -112,7 +118,10 @@ export const createRulesBulkRoute = (
|
|||
|
||||
return transformValidateBulkError(internalRule.params.ruleId, createdRule, undefined);
|
||||
} catch (err) {
|
||||
return transformBulkError(internalRule.params.ruleId, err);
|
||||
return transformBulkError(
|
||||
internalRule.params.ruleId,
|
||||
err as Error & { statusCode?: number | undefined }
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
|
@ -24,7 +24,10 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
|
|||
import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('create_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('create_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -35,13 +38,15 @@ describe('create_rules', () => {
|
|||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules
|
||||
clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // creation succeeds
|
||||
clients.rulesClient.create.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // creation succeeds
|
||||
clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); // needed to transform: ;
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
);
|
||||
createRulesRoute(server.router, ml);
|
||||
createRulesRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -57,7 +62,7 @@ describe('create_rules', () => {
|
|||
expect(response.body).toEqual({ message: 'Not Found', status_code: 404 });
|
||||
});
|
||||
|
||||
it('returns 404 if siem client is unavailable', async () => {
|
||||
test('returns 404 if siem client is unavailable', async () => {
|
||||
const { securitySolution, ...contextWithoutSecuritySolution } = context;
|
||||
// @ts-expect-error
|
||||
const response = await server.inject(getCreateRequest(), contextWithoutSecuritySolution);
|
||||
|
@ -65,7 +70,7 @@ describe('create_rules', () => {
|
|||
expect(response.body).toEqual({ message: 'Not Found', status_code: 404 });
|
||||
});
|
||||
|
||||
it('returns 200 if license is not platinum', async () => {
|
||||
test('returns 200 if license is not platinum', async () => {
|
||||
(context.licensing.license.hasAtLeast as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const response = await server.inject(getCreateRequest(), context);
|
||||
|
@ -74,12 +79,12 @@ describe('create_rules', () => {
|
|||
});
|
||||
|
||||
describe('creating an ML Rule', () => {
|
||||
it('is successful', async () => {
|
||||
test('is successful', async () => {
|
||||
const response = await server.inject(createMlRuleRequest(), context);
|
||||
expect(response.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('returns a 403 if ML Authz fails', async () => {
|
||||
test('returns a 403 if ML Authz fails', async () => {
|
||||
(buildMlAuthz as jest.Mock).mockReturnValueOnce({
|
||||
validateRuleType: jest
|
||||
.fn()
|
||||
|
@ -96,21 +101,24 @@ describe('create_rules', () => {
|
|||
});
|
||||
|
||||
describe('unhappy paths', () => {
|
||||
test('it returns a 400 if the index does not exist', async () => {
|
||||
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
);
|
||||
const response = await server.inject(getCreateRequest(), context);
|
||||
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.body).toEqual({
|
||||
message: 'To create a rule, the index must exist first. Index undefined does not exist',
|
||||
status_code: 400,
|
||||
});
|
||||
expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400);
|
||||
|
||||
if (!isRuleRegistryEnabled) {
|
||||
expect(response.body).toEqual({
|
||||
message: 'To create a rule, the index must exist first. Index undefined does not exist',
|
||||
status_code: 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('returns a duplicate error if rule_id already exists', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const response = await server.inject(getCreateRequest(), context);
|
||||
|
||||
expect(response.status).toEqual(409);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
|
||||
import {
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
|
@ -27,7 +26,7 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'
|
|||
export const createRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml'],
|
||||
ruleDataClient?: IRuleDataClient | null // TODO: Use this for RAC (otherwise delete it)
|
||||
isRuleRegistryEnabled: boolean
|
||||
): void => {
|
||||
router.post(
|
||||
{
|
||||
|
@ -57,6 +56,7 @@ export const createRulesRoute = (
|
|||
|
||||
if (request.body.rule_id != null) {
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId: request.body.rule_id,
|
||||
id: undefined,
|
||||
|
@ -69,7 +69,11 @@ export const createRulesRoute = (
|
|||
}
|
||||
}
|
||||
|
||||
const internalRule = convertCreateAPIToInternalSchema(request.body, siemClient);
|
||||
const internalRule = convertCreateAPIToInternalSchema(
|
||||
request.body,
|
||||
siemClient,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
|
||||
const mlAuthz = buildMlAuthz({
|
||||
license: context.licensing.license,
|
||||
|
@ -83,7 +87,7 @@ export const createRulesRoute = (
|
|||
esClient.asCurrentUser,
|
||||
internalRule.params.outputIndex
|
||||
);
|
||||
if (!indexExists) {
|
||||
if (!isRuleRegistryEnabled && !indexExists) {
|
||||
return siemResponse.error({
|
||||
statusCode: 400,
|
||||
body: `To create a rule, the index must exist first. Index ${internalRule.params.outputIndex} does not exist`,
|
||||
|
@ -107,14 +111,18 @@ export const createRulesRoute = (
|
|||
ruleId: createdRule.id,
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
const [validated, errors] = newTransformValidate(createdRule, ruleStatuses[0]);
|
||||
const [validated, errors] = newTransformValidate(
|
||||
createdRule,
|
||||
ruleStatuses[0],
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ statusCode: 500, body: errors });
|
||||
} else {
|
||||
return response.ok({ body: validated ?? {} });
|
||||
}
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
const error = transformError(err as Error);
|
||||
return siemResponse.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
|
|
|
@ -18,7 +18,10 @@ import {
|
|||
import { requestContextMock, serverMock, requestMock } from '../__mocks__';
|
||||
import { deleteRulesBulkRoute } from './delete_rules_bulk_route';
|
||||
|
||||
describe('delete_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('delete_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
|
||||
|
@ -26,11 +29,11 @@ describe('delete_rules', () => {
|
|||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists
|
||||
clients.rulesClient.delete.mockResolvedValue({}); // successful deletion
|
||||
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // rule status request
|
||||
|
||||
deleteRulesBulkRoute(server.router);
|
||||
deleteRulesBulkRoute(server.router, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { validate } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents';
|
||||
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
|
||||
import {
|
||||
|
@ -34,7 +35,10 @@ type Handler = RequestHandler<
|
|||
'delete' | 'post'
|
||||
>;
|
||||
|
||||
export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
export const deleteRulesBulkRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
const config: Config = {
|
||||
validate: {
|
||||
body: buildRouteValidation<typeof queryRulesBulkSchema, QueryRulesBulkSchemaDecoded>(
|
||||
|
@ -71,7 +75,7 @@ export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
}
|
||||
|
||||
try {
|
||||
const rule = await readRules({ rulesClient, id, ruleId });
|
||||
const rule = await readRules({ rulesClient, id, ruleId, isRuleRegistryEnabled });
|
||||
if (!rule) {
|
||||
return getIdBulkError({ id, ruleId });
|
||||
}
|
||||
|
@ -87,7 +91,12 @@ export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
ruleStatuses,
|
||||
id: rule.id,
|
||||
});
|
||||
return transformValidateBulkError(idOrRuleIdOrUnknown, rule, ruleStatuses);
|
||||
return transformValidateBulkError(
|
||||
idOrRuleIdOrUnknown,
|
||||
rule,
|
||||
ruleStatuses,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
} catch (err) {
|
||||
return transformBulkError(idOrRuleIdOrUnknown, err);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@ import { requestContextMock, serverMock, requestMock } from '../__mocks__';
|
|||
import { deleteRulesRoute } from './delete_rules_route';
|
||||
import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
||||
|
||||
describe('delete_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('delete_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
|
||||
|
@ -27,11 +30,11 @@ describe('delete_rules', () => {
|
|||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse());
|
||||
clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses());
|
||||
|
||||
deleteRulesRoute(server.router);
|
||||
deleteRulesRoute(server.router, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -42,7 +45,9 @@ describe('delete_rules', () => {
|
|||
});
|
||||
|
||||
test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => {
|
||||
clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.get.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
const response = await server.inject(getDeleteRequestById(), context);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents';
|
||||
import {
|
||||
queryRulesSchema,
|
||||
|
@ -23,7 +22,7 @@ import { readRules } from '../../rules/read_rules';
|
|||
|
||||
export const deleteRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ruleDataClient?: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.delete(
|
||||
{
|
||||
|
@ -54,7 +53,7 @@ export const deleteRulesRoute = (
|
|||
}
|
||||
|
||||
const ruleStatusClient = context.securitySolution.getExecutionLogClient();
|
||||
const rule = await readRules({ rulesClient, id, ruleId });
|
||||
const rule = await readRules({ isRuleRegistryEnabled, rulesClient, id, ruleId });
|
||||
if (!rule) {
|
||||
const error = getIdError({ id, ruleId });
|
||||
return siemResponse.error({
|
||||
|
@ -74,7 +73,7 @@ export const deleteRulesRoute = (
|
|||
ruleStatuses,
|
||||
id: rule.id,
|
||||
});
|
||||
const transformed = transform(rule, ruleStatuses[0]);
|
||||
const transformed = transform(rule, ruleStatuses[0], isRuleRegistryEnabled);
|
||||
if (transformed == null) {
|
||||
return siemResponse.error({ statusCode: 500, body: 'failed to transform alert' });
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,11 @@ import { getExportByObjectIds } from '../../rules/get_export_by_object_ids';
|
|||
import { getExportAll } from '../../rules/get_export_all';
|
||||
import { buildSiemResponse } from '../utils';
|
||||
|
||||
export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => {
|
||||
export const exportRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
config: ConfigType,
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.post(
|
||||
{
|
||||
path: `${DETECTION_ENGINE_RULES_URL}/_export`,
|
||||
|
@ -53,7 +57,10 @@ export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: C
|
|||
body: `Can't export more than ${exportSizeLimit} rules`,
|
||||
});
|
||||
} else {
|
||||
const nonPackagedRulesCount = await getNonPackagedRulesCount({ rulesClient });
|
||||
const nonPackagedRulesCount = await getNonPackagedRulesCount({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
});
|
||||
if (nonPackagedRulesCount > exportSizeLimit) {
|
||||
return siemResponse.error({
|
||||
statusCode: 400,
|
||||
|
@ -64,8 +71,8 @@ export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: C
|
|||
|
||||
const exported =
|
||||
request.body?.objects != null
|
||||
? await getExportByObjectIds(rulesClient, request.body.objects)
|
||||
: await getExportAll(rulesClient);
|
||||
? await getExportByObjectIds(rulesClient, request.body.objects, isRuleRegistryEnabled)
|
||||
: await getExportAll(rulesClient, isRuleRegistryEnabled);
|
||||
|
||||
const responseBody = request.query.exclude_export_details
|
||||
? exported.rulesNdjson
|
||||
|
|
|
@ -17,7 +17,10 @@ import {
|
|||
} from '../__mocks__/request_responses';
|
||||
import { findRulesRoute } from './find_rules_route';
|
||||
|
||||
describe('find_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('find_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
|
||||
|
@ -25,12 +28,14 @@ describe('find_rules', () => {
|
|||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
clients.rulesClient.get.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse());
|
||||
clients.ruleExecutionLogClient.findBulk.mockResolvedValue(getFindBulkResultStatus());
|
||||
|
||||
findRulesRoute(server.router);
|
||||
findRulesRoute(server.router, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { findRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/find_rules_type_dependents';
|
||||
import {
|
||||
findRulesSchema,
|
||||
|
@ -21,7 +20,7 @@ import { transformFindAlerts } from './utils';
|
|||
|
||||
export const findRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ruleDataClient?: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
|
@ -52,6 +51,7 @@ export const findRulesRoute = (
|
|||
|
||||
const execLogClient = context.securitySolution.getExecutionLogClient();
|
||||
const rules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
perPage: query.per_page,
|
||||
page: query.page,
|
||||
|
|
|
@ -17,7 +17,10 @@ import { RuleStatusResponse } from '../../rules/types';
|
|||
import { AlertExecutionStatusErrorReasons } from '../../../../../../alerting/common';
|
||||
import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
||||
|
||||
describe('find_statuses', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('find_statuses - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
|
||||
|
@ -25,7 +28,9 @@ describe('find_statuses', () => {
|
|||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
clients.ruleExecutionLogClient.findBulk.mockResolvedValue(getFindBulkResultStatus()); // successful status search
|
||||
clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.get.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
findRulesStatusesRoute(server.router);
|
||||
});
|
||||
|
||||
|
@ -57,7 +62,7 @@ describe('find_statuses', () => {
|
|||
test('returns success if rule status client writes an error status', async () => {
|
||||
// 0. task manager tried to run the rule but couldn't, so the alerting framework
|
||||
// wrote an error to the executionStatus.
|
||||
const failingExecutionRule = getAlertMock(getQueryRuleParams());
|
||||
const failingExecutionRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
failingExecutionRule.executionStatus = {
|
||||
status: 'error',
|
||||
lastExecutionDate: failingExecutionRule.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -51,7 +51,10 @@ jest.mock('../../../timeline/utils/check_timelines_status', () => {
|
|||
};
|
||||
});
|
||||
|
||||
describe('get_prepackaged_rule_status_route', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('get_prepackaged_rule_status_route - %s', (_, isRuleRegistryEnabled) => {
|
||||
const mockGetCurrentUser = {
|
||||
user: {
|
||||
username: 'mockUser',
|
||||
|
@ -63,6 +66,7 @@ describe('get_prepackaged_rule_status_route', () => {
|
|||
let securitySetup: SecurityPluginSetup;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
|
||||
|
@ -75,7 +79,18 @@ describe('get_prepackaged_rule_status_route', () => {
|
|||
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult());
|
||||
|
||||
getPrepackagedRulesStatusRoute(server.router, createMockConfig(), securitySetup);
|
||||
(checkTimelinesStatus as jest.Mock).mockResolvedValue({
|
||||
timelinesToInstall: [],
|
||||
timelinesToUpdate: [],
|
||||
prepackagedTimelines: [],
|
||||
});
|
||||
|
||||
getPrepackagedRulesStatusRoute(
|
||||
server.router,
|
||||
createMockConfig(),
|
||||
securitySetup,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -123,7 +138,7 @@ describe('get_prepackaged_rule_status_route', () => {
|
|||
});
|
||||
|
||||
test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const request = getPrepackagedRulesStatusRequest();
|
||||
const response = await server.inject(request, context);
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ import {
|
|||
export const getPrepackagedRulesStatusRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
config: ConfigType,
|
||||
security: SetupPlugins['security']
|
||||
security: SetupPlugins['security'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
|
@ -59,6 +60,7 @@ export const getPrepackagedRulesStatusRoute = (
|
|||
config.prebuiltRulesFromSavedObjects
|
||||
);
|
||||
const customRules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
perPage: 1,
|
||||
page: 1,
|
||||
|
@ -68,7 +70,10 @@ export const getPrepackagedRulesStatusRoute = (
|
|||
fields: undefined,
|
||||
});
|
||||
const frameworkRequest = await buildFrameworkRequest(context, security, request);
|
||||
const prepackagedRules = await getExistingPrepackagedRules({ rulesClient });
|
||||
const prepackagedRules = await getExistingPrepackagedRules({
|
||||
rulesClient,
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
|
||||
const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules);
|
||||
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules);
|
||||
|
|
|
@ -29,7 +29,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('import_rules_route', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('import_rules_route - %s', (_, isRuleRegistryEnabled) => {
|
||||
let config: ReturnType<typeof createMockConfig>;
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let request: ReturnType<typeof requestMock.create>;
|
||||
|
@ -45,11 +48,13 @@ describe('import_rules_route', () => {
|
|||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
);
|
||||
importRulesRoute(server.router, config, ml);
|
||||
importRulesRoute(server.router, config, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
|
@ -60,7 +65,7 @@ describe('import_rules_route', () => {
|
|||
});
|
||||
|
||||
test('returns 500 if more than 10,000 rules are imported', async () => {
|
||||
const ruleIds = new Array(10001).fill(undefined).map((_, index) => `rule-${index}`);
|
||||
const ruleIds = new Array(10001).fill(undefined).map((__, index) => `rule-${index}`);
|
||||
const multiRequest = getImportRulesRequest(buildHapiStream(ruleIdsToNdJsonString(ruleIds)));
|
||||
const response = await server.inject(multiRequest, context);
|
||||
|
||||
|
@ -125,18 +130,20 @@ describe('import_rules_route', () => {
|
|||
transformMock.mockRestore();
|
||||
});
|
||||
|
||||
test('returns an error if the index does not exist', async () => {
|
||||
test('returns an error if the index does not exist when rule registry not enabled', async () => {
|
||||
clients.appClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
);
|
||||
const response = await server.inject(request, context);
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.body).toEqual({
|
||||
message:
|
||||
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
|
||||
status_code: 400,
|
||||
});
|
||||
expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400);
|
||||
if (!isRuleRegistryEnabled) {
|
||||
expect(response.body).toEqual({
|
||||
message:
|
||||
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
|
||||
status_code: 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('returns an error when cluster throws error', async () => {
|
||||
|
@ -166,7 +173,9 @@ describe('import_rules_route', () => {
|
|||
|
||||
describe('single rule import', () => {
|
||||
test('returns 200 if rule imported successfully', async () => {
|
||||
clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.create.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
const response = await server.inject(request, context);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
|
@ -199,7 +208,9 @@ describe('import_rules_route', () => {
|
|||
|
||||
describe('rule with existing rule_id', () => {
|
||||
test('returns with reported conflict if `overwrite` is set to `false`', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule
|
||||
clients.rulesClient.find.mockResolvedValue(
|
||||
getFindResultWithSingleHit(isRuleRegistryEnabled)
|
||||
); // extant rule
|
||||
const response = await server.inject(request, context);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
@ -219,7 +230,9 @@ describe('import_rules_route', () => {
|
|||
});
|
||||
|
||||
test('returns with NO reported conflict if `overwrite` is set to `true`', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule
|
||||
clients.rulesClient.find.mockResolvedValue(
|
||||
getFindResultWithSingleHit(isRuleRegistryEnabled)
|
||||
); // extant rule
|
||||
const overwriteRequest = getImportRulesRequestOverwriteTrue(
|
||||
buildHapiStream(ruleIdsToNdJsonString(['rule-1']))
|
||||
);
|
||||
|
@ -251,7 +264,7 @@ describe('import_rules_route', () => {
|
|||
});
|
||||
|
||||
test('returns 200 if many rules are imported successfully', async () => {
|
||||
const ruleIds = new Array(9999).fill(undefined).map((_, index) => `rule-${index}`);
|
||||
const ruleIds = new Array(9999).fill(undefined).map((__, index) => `rule-${index}`);
|
||||
const multiRequest = getImportRulesRequest(buildHapiStream(ruleIdsToNdJsonString(ruleIds)));
|
||||
const response = await server.inject(multiRequest, context);
|
||||
|
||||
|
@ -339,7 +352,9 @@ describe('import_rules_route', () => {
|
|||
|
||||
describe('rules with existing rule_id', () => {
|
||||
beforeEach(() => {
|
||||
clients.rulesClient.find.mockResolvedValueOnce(getFindResultWithSingleHit()); // extant rule
|
||||
clients.rulesClient.find.mockResolvedValueOnce(
|
||||
getFindResultWithSingleHit(isRuleRegistryEnabled)
|
||||
); // extant rule
|
||||
});
|
||||
|
||||
test('returns with reported conflict if `overwrite` is set to `false`', async () => {
|
||||
|
|
|
@ -53,7 +53,8 @@ const CHUNK_PARSED_OBJECT_SIZE = 50;
|
|||
export const importRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
config: ConfigType,
|
||||
ml: SetupPlugins['ml']
|
||||
ml: SetupPlugins['ml'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.post(
|
||||
{
|
||||
|
@ -103,7 +104,7 @@ export const importRulesRoute = (
|
|||
}
|
||||
const signalsIndex = siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex);
|
||||
if (!indexExists) {
|
||||
if (!isRuleRegistryEnabled && !indexExists) {
|
||||
return siemResponse.error({
|
||||
statusCode: 400,
|
||||
body: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`,
|
||||
|
@ -205,6 +206,7 @@ export const importRulesRoute = (
|
|||
const filters: PartialFilter[] | undefined = filtersRest as PartialFilter[];
|
||||
throwHttpError(await mlAuthz.validateRuleType(type));
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId,
|
||||
id: undefined,
|
||||
|
@ -212,6 +214,7 @@ export const importRulesRoute = (
|
|||
|
||||
if (rule == null) {
|
||||
await createRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
anomalyThreshold,
|
||||
author,
|
||||
|
|
|
@ -22,7 +22,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('patch_rules_bulk', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('patch_rules_bulk - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -32,10 +35,12 @@ describe('patch_rules_bulk', () => {
|
|||
({ clients, context } = requestContextMock.createTools());
|
||||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // update succeeds
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // update succeeds
|
||||
|
||||
patchRulesBulkRoute(server.router, ml);
|
||||
patchRulesBulkRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
|
|
@ -27,7 +27,8 @@ import { PartialFilter } from '../../types';
|
|||
|
||||
export const patchRulesBulkRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml']
|
||||
ml: SetupPlugins['ml'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.patch(
|
||||
{
|
||||
|
@ -121,7 +122,12 @@ export const patchRulesBulkRoute = (
|
|||
throwHttpError(await mlAuthz.validateRuleType(type));
|
||||
}
|
||||
|
||||
const existingRule = await readRules({ rulesClient, ruleId, id });
|
||||
const existingRule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId,
|
||||
id,
|
||||
});
|
||||
if (existingRule?.params.type) {
|
||||
// reject an unauthorized modification of an ML rule
|
||||
throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type));
|
||||
|
@ -185,7 +191,7 @@ export const patchRulesBulkRoute = (
|
|||
ruleId: rule.id,
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
return transformValidateBulkError(rule.id, rule, ruleStatuses);
|
||||
return transformValidateBulkError(rule.id, rule, ruleStatuses, isRuleRegistryEnabled);
|
||||
} else {
|
||||
return getIdBulkError({ id, ruleId });
|
||||
}
|
||||
|
|
|
@ -25,7 +25,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('patch_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('patch_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -35,14 +38,18 @@ describe('patch_rules', () => {
|
|||
({ clients, context } = requestContextMock.createTools());
|
||||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update
|
||||
clients.rulesClient.get.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // existing rule
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // successful update
|
||||
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // successful transform
|
||||
clients.savedObjectsClient.create.mockResolvedValue(getRuleExecutionStatuses()[0]); // successful transform
|
||||
clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses());
|
||||
|
||||
patchRulesRoute(server.router, ml);
|
||||
patchRulesRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -69,7 +76,7 @@ describe('patch_rules', () => {
|
|||
});
|
||||
|
||||
test('returns error if requesting a non-rule', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult());
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled));
|
||||
const response = await server.inject(getPatchRequest(), context);
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.body).toEqual({
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { RuleAlertAction } from '../../../../../common/detection_engine/types';
|
||||
import { patchRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/patch_rules_type_dependents';
|
||||
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
|
||||
|
@ -30,7 +29,7 @@ import { PartialFilter } from '../../types';
|
|||
export const patchRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml'],
|
||||
ruleDataClient?: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.patch(
|
||||
{
|
||||
|
@ -124,7 +123,12 @@ export const patchRulesRoute = (
|
|||
throwHttpError(await mlAuthz.validateRuleType(type));
|
||||
}
|
||||
|
||||
const existingRule = await readRules({ rulesClient, ruleId, id });
|
||||
const existingRule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId,
|
||||
id,
|
||||
});
|
||||
if (existingRule?.params.type) {
|
||||
// reject an unauthorized modification of an ML rule
|
||||
throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type));
|
||||
|
@ -189,7 +193,11 @@ export const patchRulesRoute = (
|
|||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
|
||||
const [validated, errors] = transformValidate(rule, ruleStatuses[0]);
|
||||
const [validated, errors] = transformValidate(
|
||||
rule,
|
||||
ruleStatuses[0],
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ statusCode: 500, body: errors });
|
||||
} else {
|
||||
|
|
|
@ -20,7 +20,10 @@ import { getPerformBulkActionSchemaMock } from '../../../../../common/detection_
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('perform_bulk_action', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('perform_bulk_action - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -30,9 +33,9 @@ describe('perform_bulk_action', () => {
|
|||
({ clients, context } = requestContextMock.createTools());
|
||||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
performBulkActionRoute(server.router, ml);
|
||||
performBulkActionRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
|
|
|
@ -25,7 +25,8 @@ const BULK_ACTION_RULES_LIMIT = 10000;
|
|||
|
||||
export const performBulkActionRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml']
|
||||
ml: SetupPlugins['ml'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.post(
|
||||
{
|
||||
|
@ -58,6 +59,7 @@ export const performBulkActionRoute = (
|
|||
}
|
||||
|
||||
const rules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
perPage: BULK_ACTION_RULES_LIMIT,
|
||||
filter: body.query !== '' ? body.query : undefined,
|
||||
|
@ -131,7 +133,8 @@ export const performBulkActionRoute = (
|
|||
case BulkAction.export:
|
||||
const exported = await getExportByObjectIds(
|
||||
rulesClient,
|
||||
rules.data.map(({ params }) => ({ rule_id: params.ruleId }))
|
||||
rules.data.map(({ params }) => ({ rule_id: params.ruleId })),
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
|
||||
const responseBody = `${exported.rulesNdjson}${exported.exportDetails}`;
|
||||
|
|
|
@ -16,7 +16,10 @@ import {
|
|||
} from '../__mocks__/request_responses';
|
||||
import { requestMock, requestContextMock, serverMock } from '../__mocks__';
|
||||
|
||||
describe('read_signals', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('read_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
|
||||
|
@ -24,11 +27,11 @@ describe('read_signals', () => {
|
|||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists
|
||||
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // successful transform
|
||||
clients.ruleExecutionLogClient.find.mockResolvedValue([]);
|
||||
|
||||
readRulesRoute(server.router);
|
||||
readRulesRoute(server.router, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -45,7 +48,7 @@ describe('read_signals', () => {
|
|||
});
|
||||
|
||||
test('returns error if requesting a non-rule', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult());
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled));
|
||||
const response = await server.inject(getReadRequest(), context);
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.body).toEqual({
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents';
|
||||
import {
|
||||
queryRulesSchema,
|
||||
|
@ -23,7 +22,7 @@ import { RuleExecutionStatus } from '../../../../../common/detection_engine/sche
|
|||
|
||||
export const readRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ruleDataClient?: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
|
@ -55,8 +54,9 @@ export const readRulesRoute = (
|
|||
|
||||
const ruleStatusClient = context.securitySolution.getExecutionLogClient();
|
||||
const rule = await readRules({
|
||||
rulesClient,
|
||||
id,
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId,
|
||||
});
|
||||
if (rule != null) {
|
||||
|
@ -72,7 +72,7 @@ export const readRulesRoute = (
|
|||
currentStatus.attributes.statusDate = rule.executionStatus.lastExecutionDate.toISOString();
|
||||
currentStatus.attributes.status = RuleExecutionStatus.failed;
|
||||
}
|
||||
const transformed = transform(rule, currentStatus);
|
||||
const transformed = transform(rule, currentStatus, isRuleRegistryEnabled);
|
||||
if (transformed == null) {
|
||||
return siemResponse.error({ statusCode: 500, body: 'Internal error transforming' });
|
||||
} else {
|
||||
|
|
|
@ -23,7 +23,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('update_rules_bulk', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('update_rules_bulk - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -33,10 +36,12 @@ describe('update_rules_bulk', () => {
|
|||
({ clients, context } = requestContextMock.createTools());
|
||||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
|
||||
updateRulesBulkRoute(server.router, ml);
|
||||
updateRulesBulkRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
|
|
@ -22,7 +22,8 @@ import { updateRules } from '../../rules/update_rules';
|
|||
|
||||
export const updateRulesBulkRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml']
|
||||
ml: SetupPlugins['ml'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.put(
|
||||
{
|
||||
|
@ -74,6 +75,7 @@ export const updateRulesBulkRoute = (
|
|||
ruleStatusClient,
|
||||
defaultOutputIndex: siemClient.getSignalsIndex(),
|
||||
ruleUpdate: payloadRule,
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
if (rule != null) {
|
||||
const ruleStatuses = await ruleStatusClient.find({
|
||||
|
@ -81,7 +83,7 @@ export const updateRulesBulkRoute = (
|
|||
ruleId: rule.id,
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
return transformValidateBulkError(rule.id, rule, ruleStatuses);
|
||||
return transformValidateBulkError(rule.id, rule, ruleStatuses, isRuleRegistryEnabled);
|
||||
} else {
|
||||
return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id });
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock';
|
|||
|
||||
jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create());
|
||||
|
||||
describe('update_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('update_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let ml: ReturnType<typeof mlServicesMock.createSetupContract>;
|
||||
|
@ -33,12 +36,16 @@ describe('update_rules', () => {
|
|||
({ clients, context } = requestContextMock.createTools());
|
||||
ml = mlServicesMock.createSetupContract();
|
||||
|
||||
clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update
|
||||
clients.rulesClient.get.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
); // successful update
|
||||
clients.ruleExecutionLogClient.find.mockResolvedValue([]); // successful transform: ;
|
||||
|
||||
updateRulesRoute(server.router, ml);
|
||||
updateRulesRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
||||
describe('status codes with actionClient and alertClient', () => {
|
||||
|
@ -75,7 +82,7 @@ describe('update_rules', () => {
|
|||
});
|
||||
|
||||
test('returns error when updating non-rule', async () => {
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult());
|
||||
clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled));
|
||||
const response = await server.inject(getUpdateRequest(), context);
|
||||
|
||||
expect(response.status).toEqual(404);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { IRuleDataClient } from '../../../../../../rule_registry/server';
|
||||
import { updateRulesSchema } from '../../../../../common/detection_engine/schemas/request';
|
||||
import { updateRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/update_rules_type_dependents';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
|
@ -24,7 +23,7 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v
|
|||
export const updateRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ml: SetupPlugins['ml'],
|
||||
ruleDataClient?: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.put(
|
||||
{
|
||||
|
@ -61,11 +60,12 @@ export const updateRulesRoute = (
|
|||
|
||||
const ruleStatusClient = context.securitySolution.getExecutionLogClient();
|
||||
const rule = await updateRules({
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
defaultOutputIndex: siemClient.getSignalsIndex(),
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleStatusClient,
|
||||
defaultOutputIndex: siemClient.getSignalsIndex(),
|
||||
ruleUpdate: request.body,
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
|
||||
if (rule != null) {
|
||||
|
@ -74,7 +74,11 @@ export const updateRulesRoute = (
|
|||
ruleId: rule.id,
|
||||
spaceId: context.securitySolution.getSpaceId(),
|
||||
});
|
||||
const [validated, errors] = transformValidate(rule, ruleStatuses[0]);
|
||||
const [validated, errors] = transformValidate(
|
||||
rule,
|
||||
ruleStatuses[0],
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ statusCode: 500, body: errors });
|
||||
} else {
|
||||
|
|
|
@ -41,16 +41,19 @@ import {
|
|||
|
||||
type PromiseFromStreams = ImportRulesSchemaDecoded | Error;
|
||||
|
||||
describe('utils', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('utils - %s', (_, isRuleRegistryEnabled) => {
|
||||
describe('transformAlertToRule', () => {
|
||||
test('should work with a full data set', () => {
|
||||
const fullRule = getAlertMock(getQueryRuleParams());
|
||||
const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const rule = transformAlertToRule(fullRule);
|
||||
expect(rule).toEqual(getOutputRuleAlertForRest());
|
||||
});
|
||||
|
||||
test('should omit note if note is undefined', () => {
|
||||
const fullRule = getAlertMock(getQueryRuleParams());
|
||||
const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
fullRule.params.note = undefined;
|
||||
const rule = transformAlertToRule(fullRule);
|
||||
const { note, ...expectedWithoutNote } = getOutputRuleAlertForRest();
|
||||
|
@ -58,7 +61,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('should return enabled is equal to false', () => {
|
||||
const fullRule = getAlertMock(getQueryRuleParams());
|
||||
const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
fullRule.enabled = false;
|
||||
const ruleWithEnabledFalse = transformAlertToRule(fullRule);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
|
@ -67,7 +70,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('should return immutable is equal to false', () => {
|
||||
const fullRule = getAlertMock(getQueryRuleParams());
|
||||
const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
fullRule.params.immutable = false;
|
||||
const ruleWithEnabledFalse = transformAlertToRule(fullRule);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
|
@ -75,7 +78,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('should work with tags but filter out any internal tags', () => {
|
||||
const fullRule = getAlertMock(getQueryRuleParams());
|
||||
const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
fullRule.tags = ['tag 1', 'tag 2', `${INTERNAL_IDENTIFIER}_some_other_value`];
|
||||
const rule = transformAlertToRule(fullRule);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
|
@ -84,7 +87,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('transforms ML Rule fields', () => {
|
||||
const mlRule = getAlertMock(getMlRuleParams());
|
||||
const mlRule = getAlertMock(isRuleRegistryEnabled, getMlRuleParams());
|
||||
mlRule.params.anomalyThreshold = 55;
|
||||
mlRule.params.machineLearningJobId = ['some_job_id'];
|
||||
mlRule.params.type = 'machine_learning';
|
||||
|
@ -100,7 +103,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('transforms threat_matching fields', () => {
|
||||
const threatRule = getAlertMock(getThreatRuleParams());
|
||||
const threatRule = getAlertMock(isRuleRegistryEnabled, getThreatRuleParams());
|
||||
const threatFilters: PartialFilter[] = [
|
||||
{
|
||||
query: {
|
||||
|
@ -153,7 +156,7 @@ describe('utils', () => {
|
|||
test('does not leak a lists structure in the transform which would cause validation issues', () => {
|
||||
const result: RuleAlertType & { lists: [] } = {
|
||||
lists: [],
|
||||
...getAlertMock(getQueryRuleParams()),
|
||||
...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
};
|
||||
const rule = transformAlertToRule(result);
|
||||
expect(rule).toEqual(
|
||||
|
@ -168,7 +171,7 @@ describe('utils', () => {
|
|||
test('does not leak an exceptions_list structure in the transform which would cause validation issues', () => {
|
||||
const result: RuleAlertType & { exceptions_list: [] } = {
|
||||
exceptions_list: [],
|
||||
...getAlertMock(getQueryRuleParams()),
|
||||
...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
};
|
||||
const rule = transformAlertToRule(result);
|
||||
expect(rule).toEqual(
|
||||
|
@ -265,7 +268,7 @@ describe('utils', () => {
|
|||
page: 1,
|
||||
perPage: 0,
|
||||
total: 0,
|
||||
data: [getAlertMock(getQueryRuleParams())],
|
||||
data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())],
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
@ -281,14 +284,18 @@ describe('utils', () => {
|
|||
|
||||
describe('transform', () => {
|
||||
test('outputs 200 if the data is of type siem alert', () => {
|
||||
const output = transform(getAlertMock(getQueryRuleParams()));
|
||||
const output = transform(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
test('returns 500 if the data is not of type siem alert', () => {
|
||||
const unsafeCast = ({ data: [{ random: 1 }] } as unknown) as PartialAlert;
|
||||
const output = transform(unsafeCast);
|
||||
const output = transform(unsafeCast, undefined, isRuleRegistryEnabled);
|
||||
expect(output).toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -396,24 +403,34 @@ describe('utils', () => {
|
|||
|
||||
describe('transformOrBulkError', () => {
|
||||
test('outputs 200 if the data is of type siem alert', () => {
|
||||
const output = transformOrBulkError('rule-1', getAlertMock(getQueryRuleParams()), {
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
actions: [],
|
||||
ruleThrottle: 'no_actions',
|
||||
alertThrottle: null,
|
||||
});
|
||||
const output = transformOrBulkError(
|
||||
'rule-1',
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
{
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
actions: [],
|
||||
ruleThrottle: 'no_actions',
|
||||
alertThrottle: null,
|
||||
},
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
test('returns 500 if the data is not of type siem alert', () => {
|
||||
const unsafeCast = ({ name: 'something else' } as unknown) as PartialAlert;
|
||||
const output = transformOrBulkError('rule-1', unsafeCast, {
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
actions: [],
|
||||
ruleThrottle: 'no_actions',
|
||||
alertThrottle: null,
|
||||
});
|
||||
const output = transformOrBulkError(
|
||||
'rule-1',
|
||||
unsafeCast,
|
||||
{
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
actions: [],
|
||||
ruleThrottle: 'no_actions',
|
||||
alertThrottle: null,
|
||||
},
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: BulkError = {
|
||||
rule_id: 'rule-1',
|
||||
error: { message: 'Internal error transforming', status_code: 500 },
|
||||
|
@ -428,15 +445,15 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('given single alert will return the alert transformed', () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const transformed = transformAlertsToRules([result1]);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
expect(transformed).toEqual([expected]);
|
||||
});
|
||||
|
||||
test('given two alerts will return the two alerts transformed', () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = 'some other id';
|
||||
result2.params.ruleId = 'some other id';
|
||||
|
||||
|
@ -451,11 +468,16 @@ describe('utils', () => {
|
|||
|
||||
describe('transformOrImportError', () => {
|
||||
test('returns 1 given success if the alert is an alert type and the existing success count is 0', () => {
|
||||
const output = transformOrImportError('rule-1', getAlertMock(getQueryRuleParams()), {
|
||||
success: true,
|
||||
success_count: 0,
|
||||
errors: [],
|
||||
});
|
||||
const output = transformOrImportError(
|
||||
'rule-1',
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
{
|
||||
success: true,
|
||||
success_count: 0,
|
||||
errors: [],
|
||||
},
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: ImportSuccessError = {
|
||||
success: true,
|
||||
errors: [],
|
||||
|
@ -465,11 +487,16 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
test('returns 2 given successes if the alert is an alert type and the existing success count is 1', () => {
|
||||
const output = transformOrImportError('rule-1', getAlertMock(getQueryRuleParams()), {
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
});
|
||||
const output = transformOrImportError(
|
||||
'rule-1',
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
{
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
},
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: ImportSuccessError = {
|
||||
success: true,
|
||||
errors: [],
|
||||
|
@ -480,11 +507,16 @@ describe('utils', () => {
|
|||
|
||||
test('returns 1 error and success of false if the data is not of type siem alert', () => {
|
||||
const unsafeCast = ({ name: 'something else' } as unknown) as PartialAlert;
|
||||
const output = transformOrImportError('rule-1', unsafeCast, {
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
});
|
||||
const output = transformOrImportError(
|
||||
'rule-1',
|
||||
unsafeCast,
|
||||
{
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
},
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: ImportSuccessError = {
|
||||
success: false,
|
||||
errors: [
|
||||
|
|
|
@ -135,9 +135,10 @@ export const transformFindAlerts = (
|
|||
|
||||
export const transform = (
|
||||
alert: PartialAlert<RuleParams>,
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>,
|
||||
isRuleRegistryEnabled?: boolean
|
||||
): Partial<RulesSchema> | null => {
|
||||
if (isAlertType(alert)) {
|
||||
if (isAlertType(isRuleRegistryEnabled ?? false, alert)) {
|
||||
return transformAlertToRule(
|
||||
alert,
|
||||
isRuleStatusSavedObjectType(ruleStatus) ? ruleStatus : undefined
|
||||
|
@ -150,9 +151,10 @@ export const transform = (
|
|||
export const transformOrBulkError = (
|
||||
ruleId: string,
|
||||
alert: PartialAlert<RuleParams>,
|
||||
ruleStatus?: unknown
|
||||
ruleStatus?: unknown,
|
||||
isRuleRegistryEnabled?: boolean
|
||||
): Partial<RulesSchema> | BulkError => {
|
||||
if (isAlertType(alert)) {
|
||||
if (isAlertType(isRuleRegistryEnabled ?? false, alert)) {
|
||||
if (isRuleStatusFindType(ruleStatus) && ruleStatus?.saved_objects.length > 0) {
|
||||
return transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus);
|
||||
} else {
|
||||
|
@ -170,9 +172,10 @@ export const transformOrBulkError = (
|
|||
export const transformOrImportError = (
|
||||
ruleId: string,
|
||||
alert: PartialAlert<RuleParams>,
|
||||
existingImportSuccessError: ImportSuccessError
|
||||
existingImportSuccessError: ImportSuccessError,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): ImportSuccessError => {
|
||||
if (isAlertType(alert)) {
|
||||
if (isAlertType(isRuleRegistryEnabled, alert)) {
|
||||
return createSuccessObject(existingImportSuccessError);
|
||||
} else {
|
||||
return createImportErrorObject({
|
||||
|
|
|
@ -66,20 +66,23 @@ export const ruleOutput = (): RulesSchema => ({
|
|||
timeline_id: 'some-timeline-id',
|
||||
});
|
||||
|
||||
describe('validate', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('validate - %s', (_, isRuleRegistryEnabled) => {
|
||||
describe('transformValidate', () => {
|
||||
test('it should do a validation correctly of a partial alert', () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const [validated, errors] = transformValidate(ruleAlert);
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const [validated, errors] = transformValidate(ruleAlert, undefined, isRuleRegistryEnabled);
|
||||
expect(validated).toEqual(ruleOutput());
|
||||
expect(errors).toEqual(null);
|
||||
});
|
||||
|
||||
test('it should do an in-validation correctly of a partial alert', () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
// @ts-expect-error
|
||||
delete ruleAlert.name;
|
||||
const [validated, errors] = transformValidate(ruleAlert);
|
||||
const [validated, errors] = transformValidate(ruleAlert, undefined, isRuleRegistryEnabled);
|
||||
expect(validated).toEqual(null);
|
||||
expect(errors).toEqual('Invalid value "undefined" supplied to "name"');
|
||||
});
|
||||
|
@ -87,16 +90,26 @@ describe('validate', () => {
|
|||
|
||||
describe('transformValidateBulkError', () => {
|
||||
test('it should do a validation correctly of a rule id', () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const validatedOrError = transformValidateBulkError('rule-1', ruleAlert);
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const validatedOrError = transformValidateBulkError(
|
||||
'rule-1',
|
||||
ruleAlert,
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
expect(validatedOrError).toEqual(ruleOutput());
|
||||
});
|
||||
|
||||
test('it should do an in-validation correctly of a rule id', () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
// @ts-expect-error
|
||||
delete ruleAlert.name;
|
||||
const validatedOrError = transformValidateBulkError('rule-1', ruleAlert);
|
||||
const validatedOrError = transformValidateBulkError(
|
||||
'rule-1',
|
||||
ruleAlert,
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: BulkError = {
|
||||
error: {
|
||||
message: 'Invalid value "undefined" supplied to "name"',
|
||||
|
@ -109,8 +122,13 @@ describe('validate', () => {
|
|||
|
||||
test('it should do a validation correctly of a rule id with ruleStatus passed in', () => {
|
||||
const ruleStatuses = getRuleExecutionStatuses();
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const validatedOrError = transformValidateBulkError('rule-1', ruleAlert, ruleStatuses);
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
const validatedOrError = transformValidateBulkError(
|
||||
'rule-1',
|
||||
ruleAlert,
|
||||
ruleStatuses,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: RulesSchema = {
|
||||
...ruleOutput(),
|
||||
status: RuleExecutionStatus.succeeded,
|
||||
|
@ -122,10 +140,15 @@ describe('validate', () => {
|
|||
});
|
||||
|
||||
test('it should return error object if "alert" is not expected alert type', () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
// @ts-expect-error
|
||||
delete ruleAlert.alertTypeId;
|
||||
const validatedOrError = transformValidateBulkError('rule-1', ruleAlert);
|
||||
const validatedOrError = transformValidateBulkError(
|
||||
'rule-1',
|
||||
ruleAlert,
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const expected: BulkError = {
|
||||
error: {
|
||||
message: 'Internal error transforming',
|
||||
|
|
|
@ -29,9 +29,10 @@ import { RuleParams } from '../../schemas/rule_schemas';
|
|||
|
||||
export const transformValidate = (
|
||||
alert: PartialAlert<RuleParams>,
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>,
|
||||
isRuleRegistryEnabled?: boolean
|
||||
): [RulesSchema | null, string | null] => {
|
||||
const transformed = transform(alert, ruleStatus);
|
||||
const transformed = transform(alert, ruleStatus, isRuleRegistryEnabled);
|
||||
if (transformed == null) {
|
||||
return [null, 'Internal error transforming'];
|
||||
} else {
|
||||
|
@ -41,9 +42,10 @@ export const transformValidate = (
|
|||
|
||||
export const newTransformValidate = (
|
||||
alert: PartialAlert<RuleParams>,
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>
|
||||
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>,
|
||||
isRuleRegistryEnabled?: boolean
|
||||
): [FullResponseSchema | null, string | null] => {
|
||||
const transformed = transform(alert, ruleStatus);
|
||||
const transformed = transform(alert, ruleStatus, isRuleRegistryEnabled);
|
||||
if (transformed == null) {
|
||||
return [null, 'Internal error transforming'];
|
||||
} else {
|
||||
|
@ -54,9 +56,10 @@ export const newTransformValidate = (
|
|||
export const transformValidateBulkError = (
|
||||
ruleId: string,
|
||||
alert: PartialAlert<RuleParams>,
|
||||
ruleStatus?: Array<SavedObjectsFindResult<IRuleStatusSOAttributes>>
|
||||
ruleStatus?: Array<SavedObjectsFindResult<IRuleStatusSOAttributes>>,
|
||||
isRuleRegistryEnabled?: boolean
|
||||
): RulesSchema | BulkError => {
|
||||
if (isAlertType(alert)) {
|
||||
if (isAlertType(isRuleRegistryEnabled ?? false, alert)) {
|
||||
if (ruleStatus && ruleStatus?.length > 0 && isRuleStatusSavedObjectType(ruleStatus[0])) {
|
||||
const transformed = transformAlertToRule(alert, ruleStatus[0]);
|
||||
const [validated, errors] = validateNonExact(transformed, rulesSchema);
|
||||
|
|
|
@ -12,7 +12,10 @@ import { buildSiemResponse } from '../utils';
|
|||
|
||||
import { readTags } from '../../tags/read_tags';
|
||||
|
||||
export const readTagsRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
export const readTagsRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
path: DETECTION_ENGINE_TAGS_URL,
|
||||
|
@ -31,6 +34,7 @@ export const readTagsRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
|
||||
try {
|
||||
const tags = await readTags({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
});
|
||||
return response.ok({ body: tags });
|
||||
|
|
|
@ -33,7 +33,10 @@ import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas
|
|||
|
||||
let rulesClient: ReturnType<typeof rulesClientMock.create>;
|
||||
|
||||
describe('utils', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('utils - %s', (_, isRuleRegistryEnabled) => {
|
||||
describe('transformBulkError', () => {
|
||||
test('returns transformed object if it is a boom object', () => {
|
||||
const boom = new Boom.Boom('some boom message', { statusCode: 400 });
|
||||
|
@ -390,12 +393,12 @@ describe('utils', () => {
|
|||
rulesClient = rulesClientMock.create();
|
||||
});
|
||||
it('getFailingRules finds no failing rules', async () => {
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
const res = await getFailingRules(['my-fake-id'], rulesClient);
|
||||
expect(res).toEqual({});
|
||||
});
|
||||
it('getFailingRules finds a failing rule', async () => {
|
||||
const foundRule = getAlertMock(getQueryRuleParams());
|
||||
const foundRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
foundRule.executionStatus = {
|
||||
status: 'error',
|
||||
lastExecutionDate: foundRule.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -328,6 +328,6 @@ export const getFailingRules = async (
|
|||
if (Boom.isBoom(exc)) {
|
||||
throw exc;
|
||||
}
|
||||
throw new Error(`Failed to get executionStatus with RulesClient: ${exc.message}`);
|
||||
throw new Error(`Failed to get executionStatus with RulesClient: ${(exc as Error).message}`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { PersistenceServices } from '../../../../../../rule_registry/server';
|
||||
import { INDICATOR_ALERT_TYPE_ID } from '../../../../../common/constants';
|
||||
import { INDICATOR_RULE_TYPE_ID } from '../../../../../common/constants';
|
||||
import { threatRuleParams, ThreatRuleParams } from '../../schemas/rule_schemas';
|
||||
import { threatMatchExecutor } from '../../signals/executors/threat_match';
|
||||
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
|
||||
|
@ -33,7 +33,7 @@ export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions)
|
|||
ruleDataService,
|
||||
});
|
||||
return createSecurityRuleType<ThreatRuleParams, {}, PersistenceServices, {}>({
|
||||
id: INDICATOR_ALERT_TYPE_ID,
|
||||
id: INDICATOR_RULE_TYPE_ID,
|
||||
name: 'Indicator Match Rule',
|
||||
validate: {
|
||||
params: {
|
||||
|
|
|
@ -14,7 +14,7 @@ import { SavedObject } from 'src/core/types';
|
|||
import { buildEsQuery, IIndexPattern } from '../../../../../../../src/plugins/data/common';
|
||||
|
||||
import { createPersistenceRuleTypeFactory } from '../../../../../rule_registry/server';
|
||||
import { ML_ALERT_TYPE_ID } from '../../../../common/constants';
|
||||
import { ML_RULE_TYPE_ID } from '../../../../common/constants';
|
||||
import { SecurityRuleRegistry } from '../../../plugin';
|
||||
|
||||
const createSecurityMlRuleType = createPersistenceRuleTypeFactory<SecurityRuleRegistry>();
|
||||
|
@ -38,7 +38,7 @@ import { MachineLearningRuleAttributes } from '../signals/types';
|
|||
import { createErrorsFromShard, createSearchAfterReturnType, mergeReturns } from '../signals/utils';
|
||||
|
||||
export const mlAlertType = createSecurityMlRuleType({
|
||||
id: ML_ALERT_TYPE_ID,
|
||||
id: ML_RULE_TYPE_ID,
|
||||
name: 'Machine Learning Rule',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { PersistenceServices } from '../../../../../../rule_registry/server';
|
||||
import { ML_ALERT_TYPE_ID } from '../../../../../common/constants';
|
||||
import { ML_RULE_TYPE_ID } from '../../../../../common/constants';
|
||||
import { machineLearningRuleParams, MachineLearningRuleParams } from '../../schemas/rule_schemas';
|
||||
import { mlExecutor } from '../../signals/executors/ml';
|
||||
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
|
||||
|
@ -32,7 +32,7 @@ export const createMlAlertType = (createOptions: CreateRuleOptions) => {
|
|||
ruleDataService,
|
||||
});
|
||||
return createSecurityRuleType<MachineLearningRuleParams, {}, PersistenceServices, {}>({
|
||||
id: ML_ALERT_TYPE_ID,
|
||||
id: ML_RULE_TYPE_ID,
|
||||
name: 'Machine Learning Rule',
|
||||
validate: {
|
||||
params: {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { PersistenceServices } from '../../../../../../rule_registry/server';
|
||||
import { QUERY_ALERT_TYPE_ID } from '../../../../../common/constants';
|
||||
import { QUERY_RULE_TYPE_ID } from '../../../../../common/constants';
|
||||
import { queryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas';
|
||||
import { queryExecutor } from '../../signals/executors/query';
|
||||
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
|
||||
|
@ -33,7 +33,7 @@ export const createQueryAlertType = (createOptions: CreateRuleOptions) => {
|
|||
ruleDataService,
|
||||
});
|
||||
return createSecurityRuleType<QueryRuleParams, {}, PersistenceServices, {}>({
|
||||
id: QUERY_ALERT_TYPE_ID,
|
||||
id: QUERY_RULE_TYPE_ID,
|
||||
name: 'Custom Query Rule',
|
||||
validate: {
|
||||
params: {
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { CreateRulesOptions } from './types';
|
||||
import { rulesClientMock } from '../../../../../alerting/server/mocks';
|
||||
|
||||
export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({
|
||||
export const getCreateRulesOptionsMock = (isRuleRegistryEnabled: boolean): CreateRulesOptions => ({
|
||||
isRuleRegistryEnabled,
|
||||
author: ['Elastic'],
|
||||
buildingBlockType: undefined,
|
||||
rulesClient: rulesClientMock.create(),
|
||||
|
@ -61,7 +62,10 @@ export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({
|
|||
actions: [],
|
||||
});
|
||||
|
||||
export const getCreateMlRulesOptionsMock = (): CreateRulesOptions => ({
|
||||
export const getCreateMlRulesOptionsMock = (
|
||||
isRuleRegistryEnabled: boolean
|
||||
): CreateRulesOptions => ({
|
||||
isRuleRegistryEnabled,
|
||||
author: ['Elastic'],
|
||||
buildingBlockType: undefined,
|
||||
rulesClient: rulesClientMock.create(),
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
import { createRules } from './create_rules';
|
||||
import { getCreateMlRulesOptionsMock } from './create_rules.mock';
|
||||
|
||||
describe('createRules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('createRules - %s', (_, isRuleRegistryEnabled) => {
|
||||
it('calls the rulesClient with legacy ML params', async () => {
|
||||
const ruleOptions = getCreateMlRulesOptionsMock();
|
||||
const ruleOptions = getCreateMlRulesOptionsMock(isRuleRegistryEnabled);
|
||||
await createRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
@ -26,7 +29,7 @@ describe('createRules', () => {
|
|||
|
||||
it('calls the rulesClient with ML params', async () => {
|
||||
const ruleOptions = {
|
||||
...getCreateMlRulesOptionsMock(),
|
||||
...getCreateMlRulesOptionsMock(isRuleRegistryEnabled),
|
||||
machineLearningJobId: ['new_job_1', 'new_job_2'],
|
||||
};
|
||||
await createRules(ruleOptions);
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CreateRulesOptions } from './types';
|
|||
import { addTags } from './add_tags';
|
||||
import { PartialFilter, RuleTypeParams } from '../types';
|
||||
import { transformToAlertThrottle, transformToNotifyWhen } from './utils';
|
||||
import { ruleTypeMappings } from '../signals/utils';
|
||||
|
||||
export const createRules = async ({
|
||||
rulesClient,
|
||||
|
@ -68,16 +69,18 @@ export const createRules = async ({
|
|||
to,
|
||||
type,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version,
|
||||
exceptionsList,
|
||||
actions,
|
||||
isRuleRegistryEnabled,
|
||||
}: CreateRulesOptions): Promise<SanitizedAlert<RuleTypeParams>> => {
|
||||
const rule = await rulesClient.create<RuleTypeParams>({
|
||||
data: {
|
||||
name,
|
||||
tags: addTags(tags, ruleId, immutable),
|
||||
alertTypeId: SIGNALS_ID,
|
||||
alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[type] : SIGNALS_ID,
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
anomalyThreshold,
|
||||
|
@ -125,6 +128,7 @@ export const createRules = async ({
|
|||
to,
|
||||
type,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version,
|
||||
exceptionsList,
|
||||
|
|
|
@ -52,6 +52,7 @@ describe('duplicateRule', () => {
|
|||
query: 'process.args : "chmod"',
|
||||
filters: [],
|
||||
buildingBlockType: undefined,
|
||||
namespace: undefined,
|
||||
note: undefined,
|
||||
timelineId: undefined,
|
||||
timelineTitle: undefined,
|
||||
|
@ -99,6 +100,7 @@ describe('duplicateRule', () => {
|
|||
"license": "",
|
||||
"maxSignals": 100,
|
||||
"meta": undefined,
|
||||
"namespace": undefined,
|
||||
"note": undefined,
|
||||
"outputIndex": ".siem-signals-default",
|
||||
"query": "process.args : \\"chmod\\"",
|
||||
|
|
|
@ -6,16 +6,38 @@
|
|||
*/
|
||||
|
||||
import { getFilter } from './find_rules';
|
||||
import { SIGNALS_ID } from '../../../../common/constants';
|
||||
import {
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
SIGNALS_ID,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
const allAlertTypeIds = `(alert.attributes.alertTypeId: ${ML_RULE_TYPE_ID}
|
||||
OR alert.attributes.alertTypeId: ${QUERY_RULE_TYPE_ID}
|
||||
OR alert.attributes.alertTypeId: ${INDICATOR_RULE_TYPE_ID})`.replace(/[\n\r]/g, '');
|
||||
|
||||
describe('find_rules', () => {
|
||||
test('it returns a full filter with an AND if sent down', () => {
|
||||
expect(getFilter('alert.attributes.enabled: true')).toEqual(
|
||||
`alert.attributes.alertTypeId: ${SIGNALS_ID} AND alert.attributes.enabled: true`
|
||||
);
|
||||
});
|
||||
const fullFilterTestCases: Array<[boolean, string]> = [
|
||||
[false, `alert.attributes.alertTypeId: ${SIGNALS_ID} AND alert.attributes.enabled: true`],
|
||||
[true, `${allAlertTypeIds} AND alert.attributes.enabled: true`],
|
||||
];
|
||||
const nullFilterTestCases: Array<[boolean, string]> = [
|
||||
[false, `alert.attributes.alertTypeId: ${SIGNALS_ID}`],
|
||||
[true, allAlertTypeIds],
|
||||
];
|
||||
|
||||
test('it returns existing filter with no AND when not set', () => {
|
||||
expect(getFilter(null)).toEqual(`alert.attributes.alertTypeId: ${SIGNALS_ID}`);
|
||||
});
|
||||
test.each(fullFilterTestCases)(
|
||||
'it returns a full filter with an AND if sent down [rule registry enabled: %p]',
|
||||
(isRuleRegistryEnabled, expected) => {
|
||||
expect(getFilter('alert.attributes.enabled: true', isRuleRegistryEnabled)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
|
||||
test.each(nullFilterTestCases)(
|
||||
'it returns existing filter with no AND when not set [rule registry enabled: %p]',
|
||||
(isRuleRegistryEnabled, expected) => {
|
||||
expect(getFilter(null, isRuleRegistryEnabled)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -8,13 +8,23 @@
|
|||
import { FindResult } from '../../../../../alerting/server';
|
||||
import { SIGNALS_ID } from '../../../../common/constants';
|
||||
import { RuleParams } from '../schemas/rule_schemas';
|
||||
import { ruleTypeMappings } from '../signals/utils';
|
||||
import { FindRuleOptions } from './types';
|
||||
|
||||
export const getFilter = (filter: string | null | undefined) => {
|
||||
export const getFilter = (
|
||||
filter: string | null | undefined,
|
||||
isRuleRegistryEnabled: boolean = false
|
||||
) => {
|
||||
const alertTypeFilter = isRuleRegistryEnabled
|
||||
? `(${Object.values(ruleTypeMappings)
|
||||
.map((type) => (type !== SIGNALS_ID ? `alert.attributes.alertTypeId: ${type}` : undefined))
|
||||
.filter((type) => type != null)
|
||||
.join(' OR ')})`
|
||||
: `alert.attributes.alertTypeId: ${SIGNALS_ID}`;
|
||||
if (filter == null) {
|
||||
return `alert.attributes.alertTypeId: ${SIGNALS_ID}`;
|
||||
return alertTypeFilter;
|
||||
} else {
|
||||
return `alert.attributes.alertTypeId: ${SIGNALS_ID} AND ${filter}`;
|
||||
return `${alertTypeFilter} AND ${filter}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,13 +36,14 @@ export const findRules = ({
|
|||
filter,
|
||||
sortField,
|
||||
sortOrder,
|
||||
isRuleRegistryEnabled,
|
||||
}: FindRuleOptions): Promise<FindResult<RuleParams>> => {
|
||||
return rulesClient.find({
|
||||
options: {
|
||||
fields,
|
||||
page,
|
||||
perPage,
|
||||
filter: getFilter(filter),
|
||||
filter: getFilter(filter, isRuleRegistryEnabled),
|
||||
sortOrder,
|
||||
sortField,
|
||||
},
|
||||
|
|
|
@ -20,7 +20,10 @@ import {
|
|||
getNonPackagedRulesCount,
|
||||
} from './get_existing_prepackaged_rules';
|
||||
|
||||
describe('get_existing_prepackaged_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('get_existing_prepackaged_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
@ -28,23 +31,23 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
describe('getExistingPrepackagedRules', () => {
|
||||
test('should return a single item in a single page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
const rules = await getExistingPrepackagedRules({ rulesClient });
|
||||
expect(rules).toEqual([getAlertMock(getQueryRuleParams())]);
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const rules = await getExistingPrepackagedRules({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]);
|
||||
});
|
||||
|
||||
test('should return 3 items over 1 page with all on one page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.params.immutable = true;
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.params.immutable = true;
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result3 = getAlertMock(getQueryRuleParams());
|
||||
const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result3.params.immutable = true;
|
||||
result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a';
|
||||
|
||||
|
@ -68,7 +71,7 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const rules = await getExistingPrepackagedRules({ rulesClient });
|
||||
const rules = await getExistingPrepackagedRules({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual([result1, result2, result3]);
|
||||
});
|
||||
});
|
||||
|
@ -76,18 +79,18 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
describe('getNonPackagedRules', () => {
|
||||
test('should return a single item in a single page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
const rules = await getNonPackagedRules({ rulesClient });
|
||||
expect(rules).toEqual([getAlertMock(getQueryRuleParams())]);
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]);
|
||||
});
|
||||
|
||||
test('should return 2 items over 1 page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
// first result mock which is for returning the total
|
||||
|
@ -105,20 +108,20 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 })
|
||||
);
|
||||
|
||||
const rules = await getNonPackagedRules({ rulesClient });
|
||||
const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual([result1, result2]);
|
||||
});
|
||||
|
||||
test('should return 3 items over 1 page with all on one page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result3 = getAlertMock(getQueryRuleParams());
|
||||
const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a';
|
||||
|
||||
// first result mock which is for returning the total
|
||||
|
@ -141,7 +144,7 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const rules = await getNonPackagedRules({ rulesClient });
|
||||
const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual([result1, result2, result3]);
|
||||
});
|
||||
});
|
||||
|
@ -149,18 +152,18 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
describe('getRules', () => {
|
||||
test('should return a single item in a single page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
const rules = await getRules({ rulesClient, filter: '' });
|
||||
expect(rules).toEqual([getAlertMock(getQueryRuleParams())]);
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const rules = await getRules({ isRuleRegistryEnabled, rulesClient, filter: '' });
|
||||
expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]);
|
||||
});
|
||||
|
||||
test('should return 2 items over two pages, one per page', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
|
||||
// first result mock which is for returning the total
|
||||
|
@ -178,7 +181,7 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 })
|
||||
);
|
||||
|
||||
const rules = await getRules({ rulesClient, filter: '' });
|
||||
const rules = await getRules({ isRuleRegistryEnabled, rulesClient, filter: '' });
|
||||
expect(rules).toEqual([result1, result2]);
|
||||
});
|
||||
});
|
||||
|
@ -186,8 +189,8 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
describe('getRulesCount', () => {
|
||||
test('it returns a count', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
const rules = await getRulesCount({ rulesClient, filter: '' });
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const rules = await getRulesCount({ isRuleRegistryEnabled, rulesClient, filter: '' });
|
||||
expect(rules).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
@ -195,8 +198,8 @@ describe('get_existing_prepackaged_rules', () => {
|
|||
describe('getNonPackagedRulesCount', () => {
|
||||
test('it returns a count', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
const rules = await getNonPackagedRulesCount({ rulesClient });
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
const rules = await getNonPackagedRulesCount({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(rules).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,21 +14,26 @@ export const FILTER_NON_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IM
|
|||
export const FILTER_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`;
|
||||
|
||||
export const getNonPackagedRulesCount = async ({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
}: {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
rulesClient: RulesClient;
|
||||
}): Promise<number> => {
|
||||
return getRulesCount({ rulesClient, filter: FILTER_NON_PREPACKED_RULES });
|
||||
return getRulesCount({ isRuleRegistryEnabled, rulesClient, filter: FILTER_NON_PREPACKED_RULES });
|
||||
};
|
||||
|
||||
export const getRulesCount = async ({
|
||||
rulesClient,
|
||||
filter,
|
||||
isRuleRegistryEnabled,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
filter: string;
|
||||
isRuleRegistryEnabled: boolean;
|
||||
}): Promise<number> => {
|
||||
const firstRule = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
filter,
|
||||
perPage: 1,
|
||||
|
@ -43,12 +48,15 @@ export const getRulesCount = async ({
|
|||
export const getRules = async ({
|
||||
rulesClient,
|
||||
filter,
|
||||
isRuleRegistryEnabled,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
filter: string;
|
||||
}): Promise<RuleAlertType[]> => {
|
||||
const count = await getRulesCount({ rulesClient, filter });
|
||||
isRuleRegistryEnabled: boolean;
|
||||
}) => {
|
||||
const count = await getRulesCount({ rulesClient, filter, isRuleRegistryEnabled });
|
||||
const rules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
filter,
|
||||
perPage: count,
|
||||
|
@ -58,7 +66,7 @@ export const getRules = async ({
|
|||
fields: undefined,
|
||||
});
|
||||
|
||||
if (isAlertTypes(rules.data)) {
|
||||
if (isAlertTypes(isRuleRegistryEnabled, rules.data)) {
|
||||
return rules.data;
|
||||
} else {
|
||||
// If this was ever true, you have a really messed up system.
|
||||
|
@ -69,22 +77,28 @@ export const getRules = async ({
|
|||
|
||||
export const getNonPackagedRules = async ({
|
||||
rulesClient,
|
||||
isRuleRegistryEnabled,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
isRuleRegistryEnabled: boolean;
|
||||
}): Promise<RuleAlertType[]> => {
|
||||
return getRules({
|
||||
rulesClient,
|
||||
filter: FILTER_NON_PREPACKED_RULES,
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
};
|
||||
|
||||
export const getExistingPrepackagedRules = async ({
|
||||
rulesClient,
|
||||
isRuleRegistryEnabled,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
isRuleRegistryEnabled: boolean;
|
||||
}): Promise<RuleAlertType[]> => {
|
||||
return getRules({
|
||||
rulesClient,
|
||||
filter: FILTER_PREPACKED_RULES,
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -16,11 +16,14 @@ import { getListArrayMock } from '../../../../common/detection_engine/schemas/ty
|
|||
import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('getExportAll', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('getExportAll - %s', (_, isRuleRegistryEnabled) => {
|
||||
test('it exports everything from the alerts client', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
const result = getFindResultWithSingleHit();
|
||||
const alert = getAlertMock(getQueryRuleParams());
|
||||
const result = getFindResultWithSingleHit(isRuleRegistryEnabled);
|
||||
const alert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
alert.params = {
|
||||
...alert.params,
|
||||
filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }],
|
||||
|
@ -32,7 +35,7 @@ describe('getExportAll', () => {
|
|||
result.data = [alert];
|
||||
rulesClient.find.mockResolvedValue(result);
|
||||
|
||||
const exports = await getExportAll(rulesClient);
|
||||
const exports = await getExportAll(rulesClient, isRuleRegistryEnabled);
|
||||
const rulesJson = JSON.parse(exports.rulesNdjson);
|
||||
const detailsJson = JSON.parse(exports.exportDetails);
|
||||
expect(rulesJson).toEqual({
|
||||
|
@ -94,7 +97,7 @@ describe('getExportAll', () => {
|
|||
|
||||
rulesClient.find.mockResolvedValue(findResult);
|
||||
|
||||
const exports = await getExportAll(rulesClient);
|
||||
const exports = await getExportAll(rulesClient, isRuleRegistryEnabled);
|
||||
expect(exports).toEqual({
|
||||
rulesNdjson: '',
|
||||
exportDetails: '{"exported_count":0,"missing_rules":[],"missing_rules_count":0}\n',
|
||||
|
|
|
@ -12,12 +12,13 @@ import { transformAlertsToRules } from '../routes/rules/utils';
|
|||
import { transformDataToNdjson } from '../../../utils/read_stream/create_stream_from_ndjson';
|
||||
|
||||
export const getExportAll = async (
|
||||
rulesClient: RulesClient
|
||||
rulesClient: RulesClient,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Promise<{
|
||||
rulesNdjson: string;
|
||||
exportDetails: string;
|
||||
}> => {
|
||||
const ruleAlertTypes = await getNonPackagedRules({ rulesClient });
|
||||
const ruleAlertTypes = await getNonPackagedRules({ rulesClient, isRuleRegistryEnabled });
|
||||
const rules = transformAlertsToRules(ruleAlertTypes);
|
||||
// We do not support importing/exporting actions. When we do, delete this line of code
|
||||
const rulesWithoutActions = rules.map((rule) => ({ ...rule, actions: [] }));
|
||||
|
|
|
@ -16,7 +16,10 @@ import { getListArrayMock } from '../../../../common/detection_engine/schemas/ty
|
|||
import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('get_export_by_object_ids', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('get_export_by_object_ids - %s', (_, isRuleRegistryEnabled) => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
|
@ -25,10 +28,10 @@ describe('get_export_by_object_ids', () => {
|
|||
describe('getExportByObjectIds', () => {
|
||||
test('it exports object ids into an expected string with new line characters', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
const objects = [{ rule_id: 'rule-1' }];
|
||||
const exports = await getExportByObjectIds(rulesClient, objects);
|
||||
const exports = await getExportByObjectIds(rulesClient, objects, isRuleRegistryEnabled);
|
||||
const exportsObj = {
|
||||
rulesNdjson: JSON.parse(exports.rulesNdjson),
|
||||
exportDetails: JSON.parse(exports.exportDetails),
|
||||
|
@ -85,7 +88,7 @@ describe('get_export_by_object_ids', () => {
|
|||
|
||||
test('it does not export immutable rules', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
const result = getAlertMock(getQueryRuleParams());
|
||||
const result = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result.params.immutable = true;
|
||||
|
||||
const findResult: FindHit = {
|
||||
|
@ -95,11 +98,11 @@ describe('get_export_by_object_ids', () => {
|
|||
data: [result],
|
||||
};
|
||||
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(findResult);
|
||||
|
||||
const objects = [{ rule_id: 'rule-1' }];
|
||||
const exports = await getExportByObjectIds(rulesClient, objects);
|
||||
const exports = await getExportByObjectIds(rulesClient, objects, isRuleRegistryEnabled);
|
||||
expect(exports).toEqual({
|
||||
rulesNdjson: '',
|
||||
exportDetails:
|
||||
|
@ -111,10 +114,10 @@ describe('get_export_by_object_ids', () => {
|
|||
describe('getRulesFromObjects', () => {
|
||||
test('it returns transformed rules from objects sent in', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
const objects = [{ rule_id: 'rule-1' }];
|
||||
const exports = await getRulesFromObjects(rulesClient, objects);
|
||||
const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled);
|
||||
const expected: RulesErrors = {
|
||||
exportedCount: 1,
|
||||
missingRules: [],
|
||||
|
@ -175,7 +178,7 @@ describe('get_export_by_object_ids', () => {
|
|||
|
||||
test('it does not transform the rule if the rule is an immutable rule and designates it as a missing rule', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
const result = getAlertMock(getQueryRuleParams());
|
||||
const result = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result.params.immutable = true;
|
||||
|
||||
const findResult: FindHit = {
|
||||
|
@ -189,7 +192,7 @@ describe('get_export_by_object_ids', () => {
|
|||
rulesClient.find.mockResolvedValue(findResult);
|
||||
|
||||
const objects = [{ rule_id: 'rule-1' }];
|
||||
const exports = await getRulesFromObjects(rulesClient, objects);
|
||||
const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled);
|
||||
const expected: RulesErrors = {
|
||||
exportedCount: 0,
|
||||
missingRules: [{ rule_id: 'rule-1' }],
|
||||
|
@ -212,7 +215,7 @@ describe('get_export_by_object_ids', () => {
|
|||
rulesClient.find.mockResolvedValue(findResult);
|
||||
|
||||
const objects = [{ rule_id: 'rule-1' }];
|
||||
const exports = await getRulesFromObjects(rulesClient, objects);
|
||||
const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled);
|
||||
const expected: RulesErrors = {
|
||||
exportedCount: 0,
|
||||
missingRules: [{ rule_id: 'rule-1' }],
|
||||
|
|
|
@ -34,12 +34,13 @@ export interface RulesErrors {
|
|||
|
||||
export const getExportByObjectIds = async (
|
||||
rulesClient: RulesClient,
|
||||
objects: Array<{ rule_id: string }>
|
||||
objects: Array<{ rule_id: string }>,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Promise<{
|
||||
rulesNdjson: string;
|
||||
exportDetails: string;
|
||||
}> => {
|
||||
const rulesAndErrors = await getRulesFromObjects(rulesClient, objects);
|
||||
const rulesAndErrors = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled);
|
||||
// We do not support importing/exporting actions. When we do, delete this line of code
|
||||
const rulesWithoutActions = rulesAndErrors.rules.map((rule) => ({ ...rule, actions: [] }));
|
||||
const rulesNdjson = transformDataToNdjson(rulesWithoutActions);
|
||||
|
@ -49,7 +50,8 @@ export const getExportByObjectIds = async (
|
|||
|
||||
export const getRulesFromObjects = async (
|
||||
rulesClient: RulesClient,
|
||||
objects: Array<{ rule_id: string }>
|
||||
objects: Array<{ rule_id: string }>,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Promise<RulesErrors> => {
|
||||
// If we put more than 1024 ids in one block like "alert.attributes.tags: (id1 OR id2 OR ... OR id1100)"
|
||||
// then the KQL -> ES DSL query generator still puts them all in the same "should" array, but ES defaults
|
||||
|
@ -67,6 +69,7 @@ export const getRulesFromObjects = async (
|
|||
})
|
||||
.join(' OR ');
|
||||
const rules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
filter,
|
||||
page: 1,
|
||||
|
@ -79,7 +82,7 @@ export const getRulesFromObjects = async (
|
|||
const matchingRule = rules.data.find((rule) => rule.params.ruleId === ruleId);
|
||||
if (
|
||||
matchingRule != null &&
|
||||
isAlertType(matchingRule) &&
|
||||
isAlertType(isRuleRegistryEnabled, matchingRule) &&
|
||||
matchingRule.params.immutable !== true
|
||||
) {
|
||||
return {
|
||||
|
|
|
@ -9,57 +9,61 @@ import { getRulesToInstall } from './get_rules_to_install';
|
|||
import { getAlertMock } from '../routes/__mocks__/request_responses';
|
||||
import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request';
|
||||
|
||||
describe('get_rules_to_install', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('get_rules_to_install - %s', (_, isRuleRegistryEnabled) => {
|
||||
test('should return empty array if both rule sets are empty', () => {
|
||||
const update = getRulesToInstall([], []);
|
||||
expect(update).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return empty array if the two rule ids match', () => {
|
||||
const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
const update = getRulesToInstall([ruleFromFileSystem], [installedRule]);
|
||||
expect(update).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return the rule to install if the id of the two rules do not match', () => {
|
||||
const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-2';
|
||||
const update = getRulesToInstall([ruleFromFileSystem], [installedRule]);
|
||||
expect(update).toEqual([ruleFromFileSystem]);
|
||||
});
|
||||
|
||||
test('should return two rules to install if both the ids of the two rules do not match', () => {
|
||||
const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
|
||||
const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem2.rule_id = 'rule-2';
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-3';
|
||||
const update = getRulesToInstall([ruleFromFileSystem1, ruleFromFileSystem2], [installedRule]);
|
||||
expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]);
|
||||
});
|
||||
|
||||
test('should return two rules of three to install if both the ids of the two rules do not match but the third does', () => {
|
||||
const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
|
||||
const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem2.rule_id = 'rule-2';
|
||||
|
||||
const ruleFromFileSystem3 = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
const ruleFromFileSystem3 = getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded;
|
||||
ruleFromFileSystem3.rule_id = 'rule-3';
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-3';
|
||||
const update = getRulesToInstall(
|
||||
[ruleFromFileSystem1, ruleFromFileSystem2, ruleFromFileSystem3],
|
||||
|
|
|
@ -11,7 +11,7 @@ import { RuleAlertType } from './types';
|
|||
export const getRulesToInstall = (
|
||||
rulesFromFileSystem: AddPrepackagedRulesSchemaDecoded[],
|
||||
installedRules: RuleAlertType[]
|
||||
): AddPrepackagedRulesSchemaDecoded[] => {
|
||||
) => {
|
||||
return rulesFromFileSystem.filter(
|
||||
(rule) => !installedRules.some((installedRule) => installedRule.params.ruleId === rule.rule_id)
|
||||
);
|
||||
|
|
|
@ -10,7 +10,10 @@ import { getAlertMock } from '../routes/__mocks__/request_responses';
|
|||
import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('get_rules_to_update', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('get_rules_to_update - %s', (_, isRuleRegistryEnabled) => {
|
||||
describe('get_rules_to_update', () => {
|
||||
test('should return empty array if both rule sets are empty', () => {
|
||||
const update = getRulesToUpdate([], []);
|
||||
|
@ -22,7 +25,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 2;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-2';
|
||||
installedRule.params.version = 1;
|
||||
const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]);
|
||||
|
@ -34,7 +37,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 1;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 2;
|
||||
const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]);
|
||||
|
@ -46,7 +49,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 1;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 1;
|
||||
const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]);
|
||||
|
@ -58,7 +61,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 2;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 1;
|
||||
installedRule.params.exceptionsList = [];
|
||||
|
@ -72,12 +75,12 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [];
|
||||
|
||||
const installedRule2 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule2.params.ruleId = 'rule-2';
|
||||
installedRule2.params.version = 1;
|
||||
installedRule2.params.exceptionsList = [];
|
||||
|
@ -95,12 +98,12 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem2.rule_id = 'rule-2';
|
||||
ruleFromFileSystem2.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [];
|
||||
|
||||
const installedRule2 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule2.params.ruleId = 'rule-2';
|
||||
installedRule2.params.version = 1;
|
||||
installedRule2.params.exceptionsList = [];
|
||||
|
@ -125,7 +128,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [];
|
||||
|
@ -147,7 +150,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -179,7 +182,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -201,7 +204,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -228,7 +231,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem2.rule_id = 'rule-2';
|
||||
ruleFromFileSystem2.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -239,7 +242,7 @@ describe('get_rules_to_update', () => {
|
|||
type: 'endpoint',
|
||||
},
|
||||
];
|
||||
const installedRule2 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule2.params.ruleId = 'rule-2';
|
||||
installedRule2.params.version = 1;
|
||||
installedRule2.params.exceptionsList = [
|
||||
|
@ -278,7 +281,7 @@ describe('get_rules_to_update', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -290,7 +293,7 @@ describe('get_rules_to_update', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const installedRule2 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule2.params.ruleId = 'rule-2';
|
||||
installedRule2.params.version = 1;
|
||||
installedRule2.params.exceptionsList = [
|
||||
|
@ -320,7 +323,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 2;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-2';
|
||||
installedRule.params.version = 1;
|
||||
const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]);
|
||||
|
@ -332,7 +335,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 1;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 2;
|
||||
const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]);
|
||||
|
@ -344,7 +347,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 1;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 1;
|
||||
const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]);
|
||||
|
@ -356,7 +359,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem.rule_id = 'rule-1';
|
||||
ruleFromFileSystem.version = 2;
|
||||
|
||||
const installedRule = getAlertMock(getQueryRuleParams());
|
||||
const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule.params.ruleId = 'rule-1';
|
||||
installedRule.params.version = 1;
|
||||
installedRule.params.exceptionsList = [];
|
||||
|
@ -380,7 +383,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [];
|
||||
|
@ -402,7 +405,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -434,7 +437,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
@ -456,7 +459,7 @@ describe('get_rules_to_update', () => {
|
|||
ruleFromFileSystem1.rule_id = 'rule-1';
|
||||
ruleFromFileSystem1.version = 2;
|
||||
|
||||
const installedRule1 = getAlertMock(getQueryRuleParams());
|
||||
const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
installedRule1.params.ruleId = 'rule-1';
|
||||
installedRule1.params.version = 1;
|
||||
installedRule1.params.exceptionsList = [
|
||||
|
|
|
@ -18,7 +18,7 @@ import { RuleAlertType } from './types';
|
|||
export const getRulesToUpdate = (
|
||||
rulesFromFileSystem: AddPrepackagedRulesSchemaDecoded[],
|
||||
installedRules: RuleAlertType[]
|
||||
): AddPrepackagedRulesSchemaDecoded[] => {
|
||||
) => {
|
||||
return rulesFromFileSystem
|
||||
.filter((ruleFromFileSystem) => filterInstalledRules(ruleFromFileSystem, installedRules))
|
||||
.map((ruleFromFileSystem) => mergeExceptionLists(ruleFromFileSystem, installedRules));
|
||||
|
|
|
@ -14,7 +14,8 @@ import { PartialFilter } from '../types';
|
|||
export const installPrepackagedRules = (
|
||||
rulesClient: RulesClient,
|
||||
rules: AddPrepackagedRulesSchemaDecoded[],
|
||||
outputIndex: string
|
||||
outputIndex: string,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Array<Promise<SanitizedAlert<AlertTypeParams>>> =>
|
||||
rules.reduce<Array<Promise<SanitizedAlert<AlertTypeParams>>>>((acc, rule) => {
|
||||
const {
|
||||
|
@ -60,6 +61,7 @@ export const installPrepackagedRules = (
|
|||
threshold,
|
||||
timestamp_override: timestampOverride,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version,
|
||||
exceptions_list: exceptionsList,
|
||||
|
@ -70,6 +72,7 @@ export const installPrepackagedRules = (
|
|||
return [
|
||||
...acc,
|
||||
createRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
anomalyThreshold,
|
||||
author,
|
||||
|
@ -116,6 +119,7 @@ export const installPrepackagedRules = (
|
|||
throttle: null, // At this time there is no pre-packaged actions
|
||||
timestampOverride,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version,
|
||||
exceptionsList,
|
||||
|
|
|
@ -11,7 +11,7 @@ import { getAlertMock } from '../routes/__mocks__/request_responses';
|
|||
import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client';
|
||||
|
||||
export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({
|
||||
export const getPatchRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchRulesOptions => ({
|
||||
author: ['Elastic'],
|
||||
buildingBlockType: undefined,
|
||||
rulesClient: rulesClientMock.create(),
|
||||
|
@ -61,10 +61,10 @@ export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({
|
|||
version: 1,
|
||||
exceptionsList: [],
|
||||
actions: [],
|
||||
rule: getAlertMock(getQueryRuleParams()),
|
||||
rule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
});
|
||||
|
||||
export const getPatchMlRulesOptionsMock = (): PatchRulesOptions => ({
|
||||
export const getPatchMlRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchRulesOptions => ({
|
||||
author: ['Elastic'],
|
||||
buildingBlockType: undefined,
|
||||
rulesClient: rulesClientMock.create(),
|
||||
|
@ -114,5 +114,5 @@ export const getPatchMlRulesOptionsMock = (): PatchRulesOptions => ({
|
|||
version: 1,
|
||||
exceptionsList: [],
|
||||
actions: [],
|
||||
rule: getAlertMock(getMlRuleParams()),
|
||||
rule: getAlertMock(isRuleRegistryEnabled, getMlRuleParams()),
|
||||
});
|
||||
|
|
|
@ -12,15 +12,18 @@ import { RulesClientMock } from '../../../../../alerting/server/rules_client.moc
|
|||
import { getAlertMock } from '../routes/__mocks__/request_responses';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('patchRules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('patchRules - %s', (_, isRuleRegistryEnabled) => {
|
||||
it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => {
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock();
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled);
|
||||
const ruleOptions: PatchRulesOptions = {
|
||||
...rulesOptionsMock,
|
||||
enabled: false,
|
||||
};
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.disable).toHaveBeenCalledWith(
|
||||
|
@ -31,7 +34,7 @@ describe('patchRules', () => {
|
|||
});
|
||||
|
||||
it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => {
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock();
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled);
|
||||
const ruleOptions: PatchRulesOptions = {
|
||||
...rulesOptionsMock,
|
||||
enabled: true,
|
||||
|
@ -40,7 +43,7 @@ describe('patchRules', () => {
|
|||
ruleOptions.rule.enabled = false;
|
||||
}
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.enable).toHaveBeenCalledWith(
|
||||
|
@ -51,7 +54,7 @@ describe('patchRules', () => {
|
|||
});
|
||||
|
||||
it('calls the rulesClient with legacy ML params', async () => {
|
||||
const rulesOptionsMock = getPatchMlRulesOptionsMock();
|
||||
const rulesOptionsMock = getPatchMlRulesOptionsMock(isRuleRegistryEnabled);
|
||||
const ruleOptions: PatchRulesOptions = {
|
||||
...rulesOptionsMock,
|
||||
enabled: true,
|
||||
|
@ -60,7 +63,7 @@ describe('patchRules', () => {
|
|||
ruleOptions.rule.enabled = false;
|
||||
}
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith(
|
||||
|
@ -76,7 +79,7 @@ describe('patchRules', () => {
|
|||
});
|
||||
|
||||
it('calls the rulesClient with new ML params', async () => {
|
||||
const rulesOptionsMock = getPatchMlRulesOptionsMock();
|
||||
const rulesOptionsMock = getPatchMlRulesOptionsMock(isRuleRegistryEnabled);
|
||||
const ruleOptions: PatchRulesOptions = {
|
||||
...rulesOptionsMock,
|
||||
machineLearningJobId: ['new_job_1', 'new_job_2'],
|
||||
|
@ -86,7 +89,7 @@ describe('patchRules', () => {
|
|||
ruleOptions.rule.enabled = false;
|
||||
}
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith(
|
||||
|
@ -103,7 +106,7 @@ describe('patchRules', () => {
|
|||
|
||||
describe('regression tests', () => {
|
||||
it("updates the rule's actions if provided", async () => {
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock();
|
||||
const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled);
|
||||
const ruleOptions: PatchRulesOptions = {
|
||||
...rulesOptionsMock,
|
||||
actions: [
|
||||
|
@ -118,7 +121,7 @@ describe('patchRules', () => {
|
|||
],
|
||||
};
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith(
|
||||
|
@ -140,7 +143,7 @@ describe('patchRules', () => {
|
|||
});
|
||||
|
||||
it('does not update actions if none are specified', async () => {
|
||||
const ruleOptions = getPatchRulesOptionsMock();
|
||||
const ruleOptions = getPatchRulesOptionsMock(isRuleRegistryEnabled);
|
||||
delete ruleOptions.actions;
|
||||
if (ruleOptions.rule != null) {
|
||||
ruleOptions.rule.actions = [
|
||||
|
@ -155,7 +158,7 @@ describe('patchRules', () => {
|
|||
];
|
||||
}
|
||||
((ruleOptions.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
await patchRules(ruleOptions);
|
||||
expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith(
|
||||
|
|
|
@ -80,6 +80,7 @@ export const patchRules = async ({
|
|||
to,
|
||||
type,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version,
|
||||
exceptionsList,
|
||||
|
@ -131,6 +132,7 @@ export const patchRules = async ({
|
|||
type,
|
||||
references,
|
||||
version,
|
||||
namespace,
|
||||
note,
|
||||
exceptionsList,
|
||||
anomalyThreshold,
|
||||
|
@ -176,6 +178,7 @@ export const patchRules = async ({
|
|||
to,
|
||||
type,
|
||||
references,
|
||||
namespace,
|
||||
note,
|
||||
version: calculatedVersion,
|
||||
exceptionsList,
|
||||
|
|
|
@ -21,7 +21,10 @@ export class TestError extends Error {
|
|||
public output: { statusCode: number };
|
||||
}
|
||||
|
||||
describe('read_rules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('read_rules - %s', (_, isRuleRegistryEnabled) => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
|
@ -30,23 +33,25 @@ describe('read_rules', () => {
|
|||
describe('readRules', () => {
|
||||
test('should return the output from rulesClient if id is set but ruleId is undefined', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
ruleId: undefined,
|
||||
});
|
||||
expect(rule).toEqual(getAlertMock(getQueryRuleParams()));
|
||||
expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
});
|
||||
test('should return null if saved object found by alerts client given id is not alert type', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
const result = getAlertMock(getQueryRuleParams());
|
||||
const result = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
// @ts-expect-error
|
||||
delete result.alertTypeId;
|
||||
rulesClient.get.mockResolvedValue(result);
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
ruleId: undefined,
|
||||
|
@ -61,6 +66,7 @@ describe('read_rules', () => {
|
|||
});
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
ruleId: undefined,
|
||||
|
@ -75,6 +81,7 @@ describe('read_rules', () => {
|
|||
});
|
||||
try {
|
||||
await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
ruleId: undefined,
|
||||
|
@ -86,23 +93,25 @@ describe('read_rules', () => {
|
|||
|
||||
test('should return the output from rulesClient if id is undefined but ruleId is set', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: undefined,
|
||||
ruleId: 'rule-1',
|
||||
});
|
||||
expect(rule).toEqual(getAlertMock(getQueryRuleParams()));
|
||||
expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
});
|
||||
|
||||
test('should return null if the output from rulesClient with ruleId set is empty', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 });
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: undefined,
|
||||
ruleId: 'rule-1',
|
||||
|
@ -112,23 +121,25 @@ describe('read_rules', () => {
|
|||
|
||||
test('should return the output from rulesClient if id is null but ruleId is set', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: undefined,
|
||||
ruleId: 'rule-1',
|
||||
});
|
||||
expect(rule).toEqual(getAlertMock(getQueryRuleParams()));
|
||||
expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
});
|
||||
|
||||
test('should return null if id and ruleId are undefined', async () => {
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
const rule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id: undefined,
|
||||
ruleId: undefined,
|
||||
|
|
|
@ -20,6 +20,7 @@ import { isAlertType, ReadRuleOptions } from './types';
|
|||
* a filter query against the tags using `alert.attributes.tags: "__internal:${ruleId}"]`
|
||||
*/
|
||||
export const readRules = async ({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
id,
|
||||
ruleId,
|
||||
|
@ -27,7 +28,7 @@ export const readRules = async ({
|
|||
if (id != null) {
|
||||
try {
|
||||
const rule = await rulesClient.get({ id });
|
||||
if (isAlertType(rule)) {
|
||||
if (isAlertType(isRuleRegistryEnabled, rule)) {
|
||||
return rule;
|
||||
} else {
|
||||
return null;
|
||||
|
@ -42,6 +43,7 @@ export const readRules = async ({
|
|||
}
|
||||
} else if (ruleId != null) {
|
||||
const ruleFromFind = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
filter: `alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`,
|
||||
page: 1,
|
||||
|
@ -50,7 +52,10 @@ export const readRules = async ({
|
|||
sortField: undefined,
|
||||
sortOrder: undefined,
|
||||
});
|
||||
if (ruleFromFind.data.length === 0 || !isAlertType(ruleFromFind.data[0])) {
|
||||
if (
|
||||
ruleFromFind.data.length === 0 ||
|
||||
!isAlertType(isRuleRegistryEnabled, ruleFromFind.data[0])
|
||||
) {
|
||||
return null;
|
||||
} else {
|
||||
return ruleFromFind.data[0];
|
||||
|
|
|
@ -101,6 +101,7 @@ import {
|
|||
BuildingBlockTypeOrUndefined,
|
||||
RuleNameOverrideOrUndefined,
|
||||
EventCategoryOverrideOrUndefined,
|
||||
NamespaceOrUndefined,
|
||||
} from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
|
||||
import { RulesClient, PartialAlert } from '../../../../../alerting/server';
|
||||
|
@ -109,6 +110,7 @@ import { SIGNALS_ID } from '../../../../common/constants';
|
|||
import { PartialFilter } from '../types';
|
||||
import { RuleParams } from '../schemas/rule_schemas';
|
||||
import { IRuleExecutionLogClient } from '../rule_execution_log/types';
|
||||
import { ruleTypeMappings } from '../signals/utils';
|
||||
|
||||
export type RuleAlertType = Alert<RuleParams>;
|
||||
|
||||
|
@ -192,15 +194,20 @@ export interface Clients {
|
|||
}
|
||||
|
||||
export const isAlertTypes = (
|
||||
isRuleRegistryEnabled: boolean,
|
||||
partialAlert: Array<PartialAlert<RuleParams>>
|
||||
): partialAlert is RuleAlertType[] => {
|
||||
return partialAlert.every((rule) => isAlertType(rule));
|
||||
return partialAlert.every((rule) => isAlertType(isRuleRegistryEnabled, rule));
|
||||
};
|
||||
|
||||
export const isAlertType = (
|
||||
isRuleRegistryEnabled: boolean,
|
||||
partialAlert: PartialAlert<RuleParams>
|
||||
): partialAlert is RuleAlertType => {
|
||||
return partialAlert.alertTypeId === SIGNALS_ID;
|
||||
const ruleTypeValues = (Object.values(ruleTypeMappings) as unknown) as string[];
|
||||
return isRuleRegistryEnabled
|
||||
? ruleTypeValues.includes(partialAlert.alertTypeId as string)
|
||||
: partialAlert.alertTypeId === SIGNALS_ID;
|
||||
};
|
||||
|
||||
export const isRuleStatusSavedObjectType = (
|
||||
|
@ -266,9 +273,12 @@ export interface CreateRulesOptions {
|
|||
version: Version;
|
||||
exceptionsList: ListArray;
|
||||
actions: RuleAlertAction[];
|
||||
isRuleRegistryEnabled: boolean;
|
||||
namespace?: NamespaceOrUndefined;
|
||||
}
|
||||
|
||||
export interface UpdateRulesOptions {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
spaceId: string;
|
||||
ruleStatusClient: IRuleExecutionLogClient;
|
||||
rulesClient: RulesClient;
|
||||
|
@ -327,9 +337,11 @@ export interface PatchRulesOptions {
|
|||
exceptionsList: ListArrayOrUndefined;
|
||||
actions: RuleAlertAction[] | undefined;
|
||||
rule: SanitizedAlert<RuleParams> | null;
|
||||
namespace?: NamespaceOrUndefined;
|
||||
}
|
||||
|
||||
export interface ReadRuleOptions {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
rulesClient: RulesClient;
|
||||
id: IdOrUndefined;
|
||||
ruleId: RuleIdOrUndefined;
|
||||
|
@ -343,6 +355,7 @@ export interface DeleteRuleOptions {
|
|||
}
|
||||
|
||||
export interface FindRuleOptions {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
rulesClient: RulesClient;
|
||||
perPage: PerPageOrUndefined;
|
||||
page: PageOrUndefined;
|
||||
|
|
|
@ -13,7 +13,10 @@ import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/dete
|
|||
import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client';
|
||||
jest.mock('./patch_rules');
|
||||
|
||||
describe('updatePrepackagedRules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('updatePrepackagedRules - %s', (_, isRuleRegistryEnabled) => {
|
||||
let rulesClient: ReturnType<typeof rulesClientMock.create>;
|
||||
let ruleStatusClient: ReturnType<typeof ruleExecutionLogClientMock.create>;
|
||||
|
||||
|
@ -33,14 +36,15 @@ describe('updatePrepackagedRules', () => {
|
|||
];
|
||||
const outputIndex = 'outputIndex';
|
||||
const prepackagedRule = getAddPrepackagedRulesSchemaDecodedMock();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
await updatePrepackagedRules(
|
||||
rulesClient,
|
||||
'default',
|
||||
ruleStatusClient,
|
||||
[{ ...prepackagedRule, actions }],
|
||||
outputIndex
|
||||
outputIndex,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
|
||||
expect(patchRules).toHaveBeenCalledWith(
|
||||
|
|
|
@ -54,7 +54,8 @@ export const updatePrepackagedRules = async (
|
|||
spaceId: string,
|
||||
ruleStatusClient: IRuleExecutionLogClient,
|
||||
rules: AddPrepackagedRulesSchemaDecoded[],
|
||||
outputIndex: string
|
||||
outputIndex: string,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Promise<void> => {
|
||||
const ruleChunks = chunk(UPDATE_CHUNK_SIZE, rules);
|
||||
for (const ruleChunk of ruleChunks) {
|
||||
|
@ -63,7 +64,8 @@ export const updatePrepackagedRules = async (
|
|||
spaceId,
|
||||
ruleStatusClient,
|
||||
ruleChunk,
|
||||
outputIndex
|
||||
outputIndex,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
await Promise.all(rulePromises);
|
||||
}
|
||||
|
@ -83,7 +85,8 @@ export const createPromises = (
|
|||
spaceId: string,
|
||||
ruleStatusClient: IRuleExecutionLogClient,
|
||||
rules: AddPrepackagedRulesSchemaDecoded[],
|
||||
outputIndex: string
|
||||
outputIndex: string,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): Array<Promise<PartialAlert<RuleParams> | null>> => {
|
||||
return rules.map(async (rule) => {
|
||||
const {
|
||||
|
@ -133,7 +136,12 @@ export const createPromises = (
|
|||
exceptions_list: exceptionsList,
|
||||
} = rule;
|
||||
|
||||
const existingRule = await readRules({ rulesClient, ruleId, id: undefined });
|
||||
const existingRule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId,
|
||||
id: undefined,
|
||||
});
|
||||
|
||||
// TODO: Fix these either with an is conversion or by better typing them within io-ts
|
||||
const filters: PartialFilter[] | undefined = filtersObject as PartialFilter[];
|
||||
|
|
|
@ -11,20 +11,21 @@ import {
|
|||
getUpdateRulesSchemaMock,
|
||||
} from '../../../../common/detection_engine/schemas/request/rule_schemas.mock';
|
||||
import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client';
|
||||
import { UpdateRulesOptions } from './types';
|
||||
|
||||
export const getUpdateRulesOptionsMock = (): UpdateRulesOptions => ({
|
||||
export const getUpdateRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ({
|
||||
spaceId: 'default',
|
||||
rulesClient: rulesClientMock.create(),
|
||||
ruleStatusClient: ruleExecutionLogClientMock.create(),
|
||||
defaultOutputIndex: '.siem-signals-default',
|
||||
ruleUpdate: getUpdateRulesSchemaMock(),
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
|
||||
export const getUpdateMlRulesOptionsMock = (): UpdateRulesOptions => ({
|
||||
export const getUpdateMlRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ({
|
||||
spaceId: 'default',
|
||||
rulesClient: rulesClientMock.create(),
|
||||
ruleStatusClient: ruleExecutionLogClientMock.create(),
|
||||
defaultOutputIndex: '.siem-signals-default',
|
||||
ruleUpdate: getUpdateMachineLearningSchemaMock(),
|
||||
isRuleRegistryEnabled,
|
||||
});
|
||||
|
|
|
@ -11,15 +11,18 @@ import { getUpdateRulesOptionsMock, getUpdateMlRulesOptionsMock } from './update
|
|||
import { RulesClientMock } from '../../../../../alerting/server/rules_client.mock';
|
||||
import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('updateRules', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('updateRules - %s', (_, isRuleRegistryEnabled) => {
|
||||
it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => {
|
||||
const rulesOptionsMock = getUpdateRulesOptionsMock();
|
||||
const rulesOptionsMock = getUpdateRulesOptionsMock(isRuleRegistryEnabled);
|
||||
rulesOptionsMock.ruleUpdate.enabled = false;
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
|
||||
await updateRules(rulesOptionsMock);
|
||||
|
@ -32,15 +35,15 @@ describe('updateRules', () => {
|
|||
});
|
||||
|
||||
it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => {
|
||||
const rulesOptionsMock = getUpdateRulesOptionsMock();
|
||||
const rulesOptionsMock = getUpdateRulesOptionsMock(isRuleRegistryEnabled);
|
||||
rulesOptionsMock.ruleUpdate.enabled = true;
|
||||
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue({
|
||||
...getAlertMock(getQueryRuleParams()),
|
||||
...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()),
|
||||
enabled: false,
|
||||
});
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getQueryRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
|
||||
await updateRules(rulesOptionsMock);
|
||||
|
@ -53,15 +56,15 @@ describe('updateRules', () => {
|
|||
});
|
||||
|
||||
it('calls the rulesClient with params', async () => {
|
||||
const rulesOptionsMock = getUpdateMlRulesOptionsMock();
|
||||
const rulesOptionsMock = getUpdateMlRulesOptionsMock(isRuleRegistryEnabled);
|
||||
rulesOptionsMock.ruleUpdate.enabled = true;
|
||||
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).update.mockResolvedValue(
|
||||
getAlertMock(getMlRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getMlRuleParams())
|
||||
);
|
||||
|
||||
((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue(
|
||||
getAlertMock(getMlRuleParams())
|
||||
getAlertMock(isRuleRegistryEnabled, getMlRuleParams())
|
||||
);
|
||||
|
||||
await updateRules(rulesOptionsMock);
|
||||
|
|
|
@ -14,11 +14,12 @@ import { readRules } from './read_rules';
|
|||
import { UpdateRulesOptions } from './types';
|
||||
import { addTags } from './add_tags';
|
||||
import { typeSpecificSnakeToCamel } from '../schemas/rule_converters';
|
||||
import { InternalRuleUpdate, RuleParams } from '../schemas/rule_schemas';
|
||||
import { RuleParams } from '../schemas/rule_schemas';
|
||||
import { enableRule } from './enable_rule';
|
||||
import { maybeMute, transformToAlertThrottle, transformToNotifyWhen } from './utils';
|
||||
|
||||
export const updateRules = async ({
|
||||
isRuleRegistryEnabled,
|
||||
spaceId,
|
||||
rulesClient,
|
||||
ruleStatusClient,
|
||||
|
@ -26,6 +27,7 @@ export const updateRules = async ({
|
|||
ruleUpdate,
|
||||
}: UpdateRulesOptions): Promise<PartialAlert<RuleParams> | null> => {
|
||||
const existingRule = await readRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
ruleId: ruleUpdate.rule_id,
|
||||
id: ruleUpdate.id,
|
||||
|
@ -36,7 +38,7 @@ export const updateRules = async ({
|
|||
|
||||
const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate);
|
||||
const enabled = ruleUpdate.enabled ?? true;
|
||||
const newInternalRule: InternalRuleUpdate = {
|
||||
const newInternalRule = {
|
||||
name: ruleUpdate.name,
|
||||
tags: addTags(ruleUpdate.tags ?? [], existingRule.params.ruleId, existingRule.params.immutable),
|
||||
params: {
|
||||
|
@ -63,6 +65,7 @@ export const updateRules = async ({
|
|||
timestampOverride: ruleUpdate.timestamp_override,
|
||||
to: ruleUpdate.to ?? 'now',
|
||||
references: ruleUpdate.references ?? [],
|
||||
namespace: ruleUpdate.namespace,
|
||||
note: ruleUpdate.note,
|
||||
// Always use the version from the request if specified. If it isn't specified, leave immutable rules alone and
|
||||
// increment the version of mutable rules by 1.
|
||||
|
|
|
@ -81,6 +81,7 @@ describe('utils', () => {
|
|||
type: undefined,
|
||||
references: undefined,
|
||||
version: undefined,
|
||||
namespace: undefined,
|
||||
note: undefined,
|
||||
anomalyThreshold: undefined,
|
||||
machineLearningJobId: undefined,
|
||||
|
@ -131,6 +132,7 @@ describe('utils', () => {
|
|||
type: undefined,
|
||||
references: undefined,
|
||||
version: undefined,
|
||||
namespace: undefined,
|
||||
note: undefined,
|
||||
anomalyThreshold: undefined,
|
||||
machineLearningJobId: undefined,
|
||||
|
@ -181,6 +183,7 @@ describe('utils', () => {
|
|||
type: undefined,
|
||||
references: undefined,
|
||||
version: undefined,
|
||||
namespace: undefined,
|
||||
note: undefined,
|
||||
anomalyThreshold: undefined,
|
||||
machineLearningJobId: undefined,
|
||||
|
|
|
@ -52,6 +52,7 @@ import {
|
|||
RuleNameOverrideOrUndefined,
|
||||
TimestampOverrideOrUndefined,
|
||||
EventCategoryOverrideOrUndefined,
|
||||
NamespaceOrUndefined,
|
||||
} from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { PartialFilter } from '../types';
|
||||
import { RuleParams } from '../schemas/rule_schemas';
|
||||
|
@ -118,6 +119,7 @@ export interface UpdateProperties {
|
|||
version: VersionOrUndefined;
|
||||
exceptionsList: ListArrayOrUndefined;
|
||||
anomalyThreshold: AnomalyThresholdOrUndefined;
|
||||
namespace: NamespaceOrUndefined;
|
||||
}
|
||||
|
||||
export const calculateVersion = (
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
transformToAlertThrottle,
|
||||
transformToNotifyWhen,
|
||||
} from '../rules/utils';
|
||||
import { ruleTypeMappings } from '../signals/utils';
|
||||
|
||||
// These functions provide conversions from the request API schema to the internal rule schema and from the internal rule schema
|
||||
// to the response API schema. This provides static type-check assurances that the internal schema is in sync with the API schema for
|
||||
|
@ -121,14 +122,15 @@ export const typeSpecificSnakeToCamel = (params: CreateTypeSpecific): TypeSpecif
|
|||
|
||||
export const convertCreateAPIToInternalSchema = (
|
||||
input: CreateRulesSchema,
|
||||
siemClient: AppClient
|
||||
siemClient: AppClient,
|
||||
isRuleRegistryEnabled: boolean
|
||||
): InternalRuleCreate => {
|
||||
const typeSpecificParams = typeSpecificSnakeToCamel(input);
|
||||
const newRuleId = input.rule_id ?? uuid.v4();
|
||||
return {
|
||||
name: input.name,
|
||||
tags: addTags(input.tags ?? [], newRuleId, false),
|
||||
alertTypeId: SIGNALS_ID,
|
||||
alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[input.type] : SIGNALS_ID,
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
author: input.author ?? [],
|
||||
|
@ -153,6 +155,7 @@ export const convertCreateAPIToInternalSchema = (
|
|||
timestampOverride: input.timestamp_override,
|
||||
to: input.to ?? 'now',
|
||||
references: input.references ?? [],
|
||||
namespace: input.namespace,
|
||||
note: input.note,
|
||||
version: input.version ?? 1,
|
||||
exceptionsList: input.exceptions_list ?? [],
|
||||
|
@ -249,6 +252,7 @@ export const commonParamsCamelToSnake = (params: BaseRuleParams) => {
|
|||
risk_score: params.riskScore,
|
||||
severity: params.severity,
|
||||
building_block_type: params.buildingBlockType,
|
||||
namespace: params.namespace,
|
||||
note: params.note,
|
||||
license: params.license,
|
||||
output_index: params.outputIndex,
|
||||
|
|
|
@ -36,6 +36,7 @@ const getBaseRuleParams = (): BaseRuleParams => {
|
|||
riskScoreMapping: [],
|
||||
ruleNameOverride: undefined,
|
||||
maxSignals: 10000,
|
||||
namespace: undefined,
|
||||
note: '# Investigative notes',
|
||||
timelineId: 'some-timeline-id',
|
||||
timelineTitle: 'some-timeline-title',
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
buildingBlockTypeOrUndefined,
|
||||
description,
|
||||
enabled,
|
||||
namespaceOrUndefined,
|
||||
noteOrUndefined,
|
||||
false_positives,
|
||||
rule_id,
|
||||
|
@ -62,7 +63,13 @@ import {
|
|||
updated_at,
|
||||
} from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
|
||||
import { SIGNALS_ID, SERVER_APP_ID } from '../../../../common/constants';
|
||||
import {
|
||||
SIGNALS_ID,
|
||||
SERVER_APP_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
const nonEqlLanguages = t.keyof({ kuery: null, lucene: null });
|
||||
export const baseRuleParams = t.exact(
|
||||
|
@ -70,6 +77,7 @@ export const baseRuleParams = t.exact(
|
|||
author,
|
||||
buildingBlockType: buildingBlockTypeOrUndefined,
|
||||
description,
|
||||
namespace: namespaceOrUndefined,
|
||||
note: noteOrUndefined,
|
||||
falsePositives: false_positives,
|
||||
from,
|
||||
|
@ -196,10 +204,21 @@ export const notifyWhen = t.union([
|
|||
t.null,
|
||||
]);
|
||||
|
||||
export const allRuleTypes = t.union([
|
||||
t.literal(SIGNALS_ID),
|
||||
// t.literal(EQL_RULE_TYPE_ID),
|
||||
t.literal(ML_RULE_TYPE_ID),
|
||||
t.literal(QUERY_RULE_TYPE_ID),
|
||||
// t.literal(SAVED_QUERY_RULE_TYPE_ID),
|
||||
t.literal(INDICATOR_RULE_TYPE_ID),
|
||||
// t.literal(THRESHOLD_RULE_TYPE_ID),
|
||||
]);
|
||||
export type AllRuleTypes = t.TypeOf<typeof allRuleTypes>;
|
||||
|
||||
export const internalRuleCreate = t.type({
|
||||
name,
|
||||
tags,
|
||||
alertTypeId: t.literal(SIGNALS_ID),
|
||||
alertTypeId: allRuleTypes,
|
||||
consumer: t.literal(SERVER_APP_ID),
|
||||
schedule: t.type({
|
||||
interval: t.string,
|
||||
|
|
|
@ -182,7 +182,7 @@ export const searchAfterAndBulkCreate = async ({
|
|||
break;
|
||||
}
|
||||
} catch (exc: unknown) {
|
||||
logger.error(buildRuleMessage(`[-] search_after and bulk threw an error ${exc}`));
|
||||
logger.error(buildRuleMessage(`[-] search_after_bulk_create threw an error ${exc}`));
|
||||
return mergeReturns([
|
||||
toReturn,
|
||||
createSearchAfterReturnType({
|
||||
|
|
|
@ -177,7 +177,7 @@ describe('signal_rule_alert_type', () => {
|
|||
alertServices.scopedClusterClient.asCurrentUser.fieldCaps.mockResolvedValue(
|
||||
value as ApiResponse<estypes.FieldCapsResponse>
|
||||
);
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
alertServices.savedObjectsClient.get.mockResolvedValue({
|
||||
id: 'id',
|
||||
type: 'type',
|
||||
|
@ -245,7 +245,7 @@ describe('signal_rule_alert_type', () => {
|
|||
},
|
||||
application: {},
|
||||
});
|
||||
const newRuleAlert = getAlertMock(getQueryRuleParams());
|
||||
const newRuleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
newRuleAlert.params.index = ['some*', 'myfa*', 'anotherindex*'];
|
||||
payload = getPayload(newRuleAlert, alertServices) as jest.Mocked<RuleExecutorOptions>;
|
||||
|
||||
|
@ -274,7 +274,7 @@ describe('signal_rule_alert_type', () => {
|
|||
},
|
||||
application: {},
|
||||
});
|
||||
const newRuleAlert = getAlertMock(getQueryRuleParams());
|
||||
const newRuleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
newRuleAlert.params.index = ['some*', 'myfa*', 'anotherindex*'];
|
||||
payload = getPayload(newRuleAlert, alertServices) as jest.Mocked<RuleExecutorOptions>;
|
||||
|
||||
|
@ -309,7 +309,7 @@ describe('signal_rule_alert_type', () => {
|
|||
});
|
||||
|
||||
it('should call scheduleActions if signalsCount was greater than 0 and rule has actions defined', async () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
ruleAlert.actions = [
|
||||
{
|
||||
actionTypeId: '.slack',
|
||||
|
@ -333,7 +333,7 @@ describe('signal_rule_alert_type', () => {
|
|||
});
|
||||
|
||||
it('should resolve results_link when meta is an empty object to use "/app/security"', async () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
ruleAlert.params.meta = {};
|
||||
ruleAlert.actions = [
|
||||
{
|
||||
|
@ -366,7 +366,7 @@ describe('signal_rule_alert_type', () => {
|
|||
});
|
||||
|
||||
it('should resolve results_link when meta is undefined use "/app/security"', async () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
delete ruleAlert.params.meta;
|
||||
ruleAlert.actions = [
|
||||
{
|
||||
|
@ -399,7 +399,7 @@ describe('signal_rule_alert_type', () => {
|
|||
});
|
||||
|
||||
it('should resolve results_link with a custom link', async () => {
|
||||
const ruleAlert = getAlertMock(getQueryRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getQueryRuleParams());
|
||||
ruleAlert.params.meta = { kibana_siem_app_url: 'http://localhost' };
|
||||
ruleAlert.actions = [
|
||||
{
|
||||
|
@ -433,7 +433,7 @@ describe('signal_rule_alert_type', () => {
|
|||
|
||||
describe('ML rule', () => {
|
||||
it('should not call checkPrivileges if ML rule', async () => {
|
||||
const ruleAlert = getAlertMock(getMlRuleParams());
|
||||
const ruleAlert = getAlertMock(false, getMlRuleParams());
|
||||
alertServices.savedObjectsClient.get.mockResolvedValue({
|
||||
id: 'id',
|
||||
type: 'type',
|
||||
|
|
|
@ -62,6 +62,12 @@ import {
|
|||
import { WrappedRACAlert } from '../rule_types/types';
|
||||
import { SearchTypes } from '../../../../common/detection_engine/types';
|
||||
import { IRuleExecutionLogClient } from '../rule_execution_log/types';
|
||||
import {
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
SIGNALS_ID,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
interface SortExceptionsReturn {
|
||||
exceptionsWithValueLists: ExceptionListItemSchema[];
|
||||
|
@ -999,3 +1005,15 @@ export const getField = <T extends SearchTypes>(event: SimpleHit, field: string)
|
|||
return get(event._source, field) as T;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps legacy rule types to RAC rule type IDs.
|
||||
*/
|
||||
export const ruleTypeMappings = {
|
||||
eql: SIGNALS_ID,
|
||||
machine_learning: ML_RULE_TYPE_ID,
|
||||
query: QUERY_RULE_TYPE_ID,
|
||||
saved_query: SIGNALS_ID,
|
||||
threat_match: INDICATOR_RULE_TYPE_ID,
|
||||
threshold: SIGNALS_ID,
|
||||
};
|
||||
|
|
|
@ -11,19 +11,22 @@ import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/co
|
|||
import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('read_tags', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('read_tags - %s', (_, isRuleRegistryEnabled) => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('readRawTags', () => {
|
||||
test('it should return the intersection of tags to where none are repeating', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -31,17 +34,17 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should return the intersection of tags to where some are repeating values', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -49,17 +52,17 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should work with no tags defined between two results', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = [];
|
||||
|
@ -67,12 +70,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has repeating values in it', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2'];
|
||||
|
@ -80,12 +83,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2']);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has empty tags', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
@ -93,19 +96,19 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readTags', () => {
|
||||
test('it should return the intersection of tags to where none are repeating', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -113,17 +116,17 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should return the intersection of tags to where some are repeating values', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -131,17 +134,17 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should work with no tags defined between two results', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = [];
|
||||
|
@ -149,12 +152,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has repeating values in it', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2'];
|
||||
|
@ -162,12 +165,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2']);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has empty tags', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
@ -175,12 +178,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should filter out any __internal tags for things such as alert_id', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [
|
||||
|
@ -192,12 +195,12 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1']);
|
||||
});
|
||||
|
||||
test('it should filter out any __internal tags with two different results', async () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [
|
||||
|
@ -210,7 +213,7 @@ describe('read_tags', () => {
|
|||
'tag 5',
|
||||
];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = [
|
||||
|
@ -225,19 +228,19 @@ describe('read_tags', () => {
|
|||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
const tags = await readTags({ isRuleRegistryEnabled, rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertTagsToSet', () => {
|
||||
test('it should convert the intersection of two tag systems without duplicates', () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -255,12 +258,12 @@ describe('read_tags', () => {
|
|||
|
||||
describe('convertToTags', () => {
|
||||
test('it should convert the two tag systems together with duplicates', () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
@ -281,18 +284,18 @@ describe('read_tags', () => {
|
|||
});
|
||||
|
||||
test('it should filter out anything that is not a tag', () => {
|
||||
const result1 = getAlertMock(getQueryRuleParams());
|
||||
const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getAlertMock(getQueryRuleParams());
|
||||
const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result2.id = '99979e67-19a7-455f-b452-8eded6135716';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
// @ts-expect-error
|
||||
delete result2.tags;
|
||||
|
||||
const result3 = getAlertMock(getQueryRuleParams());
|
||||
const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
|
||||
result3.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result3.params.ruleId = 'rule-2';
|
||||
result3.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
|
|
@ -40,22 +40,27 @@ export const convertTagsToSet = (tagObjects: object[]): Set<string> => {
|
|||
// then this should be replaced with a an aggregation call.
|
||||
// Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html
|
||||
export const readTags = async ({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
}: {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
rulesClient: RulesClient;
|
||||
}): Promise<string[]> => {
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient });
|
||||
return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER));
|
||||
};
|
||||
|
||||
export const readRawTags = async ({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
}: {
|
||||
isRuleRegistryEnabled: boolean;
|
||||
rulesClient: RulesClient;
|
||||
perPage?: number;
|
||||
}): Promise<string[]> => {
|
||||
// Get just one record so we can get the total count
|
||||
const firstTags = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
fields: ['tags'],
|
||||
perPage: 1,
|
||||
|
@ -66,6 +71,7 @@ export const readRawTags = async ({
|
|||
});
|
||||
// Get all the rules to aggregate over all the tags of the rules
|
||||
const rules = await findRules({
|
||||
isRuleRegistryEnabled,
|
||||
rulesClient,
|
||||
fields: ['tags'],
|
||||
perPage: firstTags.total,
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { join, resolve } from 'path';
|
||||
|
||||
import { createPromiseFromStreams } from '@kbn/utils';
|
||||
import { SecurityPluginSetup } from '../../../../../../../security/server';
|
||||
|
||||
|
@ -21,17 +19,20 @@ import {
|
|||
getFindResultWithSingleHit,
|
||||
} from '../../../../detection_engine/routes/__mocks__/request_responses';
|
||||
|
||||
import * as lib from './helpers';
|
||||
import { importTimelines } from '../../timelines/import_timelines';
|
||||
import * as helpers from './helpers';
|
||||
import { importTimelines } from '../../timelines/import_timelines/helpers';
|
||||
import { buildFrameworkRequest } from '../../../utils/common';
|
||||
import { ImportTimelineResultSchema } from '../../../../../../common/types/timeline';
|
||||
|
||||
jest.mock('../../timelines/import_timelines');
|
||||
jest.mock('../../timelines/import_timelines/helpers');
|
||||
|
||||
describe('installPrepackagedTimelines', () => {
|
||||
describe.each([
|
||||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('installPrepackagedTimelines - %s', (_, isRuleRegistryEnabled) => {
|
||||
let securitySetup: SecurityPluginSetup;
|
||||
let frameworkRequest: FrameworkRequest;
|
||||
const spyInstallPrepackagedTimelines = jest.spyOn(lib, 'installPrepackagedTimelines');
|
||||
const spyInstallPrepackagedTimelines = jest.spyOn(helpers, 'installPrepackagedTimelines');
|
||||
|
||||
const { clients, context } = requestContextMock.createTools();
|
||||
const config = createMockConfig();
|
||||
|
@ -46,11 +47,11 @@ describe('installPrepackagedTimelines', () => {
|
|||
authz: {},
|
||||
} as unknown) as SecurityPluginSetup;
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
|
||||
jest.doMock('./helpers', () => {
|
||||
return {
|
||||
...lib,
|
||||
...helpers,
|
||||
installPrepackagedTimelines: spyInstallPrepackagedTimelines,
|
||||
};
|
||||
});
|
||||
|
@ -60,7 +61,7 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
spyInstallPrepackagedTimelines.mockClear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
@ -68,10 +69,10 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
test('should call importTimelines', async () => {
|
||||
await lib.installPrepackagedTimelines(
|
||||
await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
false,
|
||||
mockFilePath,
|
||||
mockFileName
|
||||
);
|
||||
|
@ -80,14 +81,12 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
test('should call importTimelines with Readables', async () => {
|
||||
const dir = resolve(join(__dirname, mockFilePath));
|
||||
const file = mockFileName;
|
||||
await lib.installPrepackagedTimelines(
|
||||
await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
dir,
|
||||
file
|
||||
mockFilePath,
|
||||
mockFileName
|
||||
);
|
||||
const args = await createPromiseFromStreams([(importTimelines as jest.Mock).mock.calls[0][0]]);
|
||||
const expected = JSON.stringify({
|
||||
|
@ -194,14 +193,12 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
test('should call importTimelines with maxTimelineImportExportSize', async () => {
|
||||
const dir = resolve(join(__dirname, mockFilePath));
|
||||
const file = mockFileName;
|
||||
await lib.installPrepackagedTimelines(
|
||||
await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
dir,
|
||||
file
|
||||
mockFilePath,
|
||||
mockFileName
|
||||
);
|
||||
|
||||
expect((importTimelines as jest.Mock).mock.calls[0][1]).toEqual(
|
||||
|
@ -210,14 +207,12 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
test('should call importTimelines with frameworkRequest', async () => {
|
||||
const dir = resolve(join(__dirname, mockFilePath));
|
||||
const file = mockFileName;
|
||||
await lib.installPrepackagedTimelines(
|
||||
await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
dir,
|
||||
file
|
||||
mockFilePath,
|
||||
mockFileName
|
||||
);
|
||||
|
||||
expect(JSON.stringify((importTimelines as jest.Mock).mock.calls[0][2])).toEqual(
|
||||
|
@ -226,21 +221,19 @@ describe('installPrepackagedTimelines', () => {
|
|||
});
|
||||
|
||||
test('should call importTimelines with immutable', async () => {
|
||||
const dir = resolve(join(__dirname, mockFilePath));
|
||||
const file = mockFileName;
|
||||
await lib.installPrepackagedTimelines(
|
||||
await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
dir,
|
||||
file
|
||||
mockFilePath,
|
||||
mockFileName
|
||||
);
|
||||
|
||||
expect((importTimelines as jest.Mock).mock.calls[0][3]).toEqual(true);
|
||||
});
|
||||
|
||||
test('should handle errors from getReadables', async () => {
|
||||
const result = await lib.installPrepackagedTimelines(
|
||||
const result = await helpers.installPrepackagedTimelines(
|
||||
config.maxTimelineImportExportSize,
|
||||
frameworkRequest,
|
||||
true,
|
||||
|
|
|
@ -63,10 +63,10 @@ import {
|
|||
SERVER_APP_ID,
|
||||
SIGNALS_ID,
|
||||
NOTIFICATIONS_ID,
|
||||
QUERY_ALERT_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
DEFAULT_SPACE_ID,
|
||||
INDICATOR_ALERT_TYPE_ID,
|
||||
ML_ALERT_TYPE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
} from '../common/constants';
|
||||
import { registerEndpointRoutes } from './endpoint/routes/metadata';
|
||||
import { registerLimitedConcurrencyRoutes } from './endpoint/routes/limited_concurrency';
|
||||
|
@ -272,7 +272,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins.security,
|
||||
plugins.ml,
|
||||
ruleDataService,
|
||||
ruleDataClient
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
registerEndpointRoutes(router, endpointContext);
|
||||
registerLimitedConcurrencyRoutes(core);
|
||||
|
@ -281,7 +281,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
registerTrustedAppsRoutes(router, endpointContext);
|
||||
registerActionRoutes(router, endpointContext);
|
||||
|
||||
const racRuleTypes = [QUERY_ALERT_TYPE_ID, INDICATOR_ALERT_TYPE_ID, ML_ALERT_TYPE_ID];
|
||||
const racRuleTypes = [QUERY_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, ML_RULE_TYPE_ID];
|
||||
const ruleTypes = [
|
||||
SIGNALS_ID,
|
||||
NOTIFICATIONS_ID,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IRuleDataClient, RuleDataPluginService } from '../../../rule_registry/server';
|
||||
import { RuleDataPluginService } from '../../../rule_registry/server';
|
||||
|
||||
import { SecuritySolutionPluginRouter } from '../types';
|
||||
|
||||
|
@ -64,31 +64,31 @@ export const initRoutes = (
|
|||
security: SetupPlugins['security'],
|
||||
ml: SetupPlugins['ml'],
|
||||
ruleDataService: RuleDataPluginService,
|
||||
ruleDataClient: IRuleDataClient | null
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
// Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules
|
||||
// All REST rule creation, deletion, updating, etc......
|
||||
createRulesRoute(router, ml, ruleDataClient);
|
||||
readRulesRoute(router, ruleDataClient);
|
||||
updateRulesRoute(router, ml, ruleDataClient);
|
||||
patchRulesRoute(router, ml, ruleDataClient);
|
||||
deleteRulesRoute(router, ruleDataClient);
|
||||
findRulesRoute(router, ruleDataClient);
|
||||
createRulesRoute(router, ml, isRuleRegistryEnabled);
|
||||
readRulesRoute(router, isRuleRegistryEnabled);
|
||||
updateRulesRoute(router, ml, isRuleRegistryEnabled);
|
||||
patchRulesRoute(router, ml, isRuleRegistryEnabled);
|
||||
deleteRulesRoute(router, isRuleRegistryEnabled);
|
||||
findRulesRoute(router, isRuleRegistryEnabled);
|
||||
|
||||
// TODO: pass ruleDataClient to all relevant routes
|
||||
// TODO: pass isRuleRegistryEnabled to all relevant routes
|
||||
|
||||
addPrepackedRulesRoute(router, config, security);
|
||||
getPrepackagedRulesStatusRoute(router, config, security);
|
||||
createRulesBulkRoute(router, ml);
|
||||
updateRulesBulkRoute(router, ml);
|
||||
patchRulesBulkRoute(router, ml);
|
||||
deleteRulesBulkRoute(router);
|
||||
performBulkActionRoute(router, ml);
|
||||
addPrepackedRulesRoute(router, config, security, isRuleRegistryEnabled);
|
||||
getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled);
|
||||
createRulesBulkRoute(router, ml, isRuleRegistryEnabled);
|
||||
updateRulesBulkRoute(router, ml, isRuleRegistryEnabled);
|
||||
patchRulesBulkRoute(router, ml, isRuleRegistryEnabled);
|
||||
deleteRulesBulkRoute(router, isRuleRegistryEnabled);
|
||||
performBulkActionRoute(router, ml, isRuleRegistryEnabled);
|
||||
|
||||
createTimelinesRoute(router, config, security);
|
||||
patchTimelinesRoute(router, config, security);
|
||||
importRulesRoute(router, config, ml);
|
||||
exportRulesRoute(router, config);
|
||||
importRulesRoute(router, config, ml, isRuleRegistryEnabled);
|
||||
exportRulesRoute(router, config, isRuleRegistryEnabled);
|
||||
|
||||
importTimelinesRoute(router, config, security);
|
||||
exportTimelinesRoute(router, config, security);
|
||||
|
@ -123,7 +123,7 @@ export const initRoutes = (
|
|||
deleteIndexRoute(router);
|
||||
|
||||
// Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags
|
||||
readTagsRoute(router);
|
||||
readTagsRoute(router, isRuleRegistryEnabled);
|
||||
|
||||
// Privileges API to get the generic user privileges
|
||||
readPrivilegesRoute(router, hasEncryptionKey);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue