mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution][Platform] - Update rule exported counts to include total object count (#116338)
### Summary Addresses #116330.
This commit is contained in:
parent
2e9d0c0ee7
commit
2f88776eac
22 changed files with 378 additions and 37 deletions
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ExportExceptionDetails } from '.';
|
||||
|
||||
export interface ExportExceptionDetailsMock {
|
||||
listCount?: number;
|
||||
missingListsCount?: number;
|
||||
missingLists?: Array<Record<'list_id', string>>;
|
||||
itemCount?: number;
|
||||
missingItemCount?: number;
|
||||
missingItems?: Array<Record<'item_id', string>>;
|
||||
}
|
||||
|
||||
export const getExceptionExportDetailsMock = (
|
||||
details?: ExportExceptionDetailsMock
|
||||
): ExportExceptionDetails => ({
|
||||
exported_exception_list_count: details?.listCount ?? 0,
|
||||
exported_exception_list_item_count: details?.itemCount ?? 0,
|
||||
missing_exception_list_item_count: details?.missingItemCount ?? 0,
|
||||
missing_exception_list_items: details?.missingItems ?? [],
|
||||
missing_exception_lists: details?.missingLists ?? [],
|
||||
missing_exception_lists_count: details?.missingListsCount ?? 0,
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { getExceptionExportDetailsMock } from './index.mock';
|
||||
import { exportExceptionDetailsSchema, ExportExceptionDetails } from '.';
|
||||
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('exportExceptionDetails', () => {
|
||||
test('it should validate export meta', () => {
|
||||
const payload = getExceptionExportDetailsMock();
|
||||
const decoded = exportExceptionDetailsSchema.decode(payload);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should strip out extra keys', () => {
|
||||
const payload: ExportExceptionDetails & {
|
||||
extraKey?: string;
|
||||
} = getExceptionExportDetailsMock();
|
||||
payload.extraKey = 'some extra key';
|
||||
const decoded = exportExceptionDetailsSchema.decode(payload);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(getExceptionExportDetailsMock());
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
export const exportExceptionDetails = {
|
||||
exported_exception_list_count: t.number,
|
||||
exported_exception_list_item_count: t.number,
|
||||
missing_exception_list_item_count: t.number,
|
||||
missing_exception_list_items: t.array(
|
||||
t.exact(
|
||||
t.type({
|
||||
item_id: NonEmptyString,
|
||||
})
|
||||
)
|
||||
),
|
||||
missing_exception_lists: t.array(
|
||||
t.exact(
|
||||
t.type({
|
||||
list_id: NonEmptyString,
|
||||
})
|
||||
)
|
||||
),
|
||||
missing_exception_lists_count: t.number,
|
||||
};
|
||||
|
||||
export const exportExceptionDetailsSchema = t.exact(t.type(exportExceptionDetails));
|
||||
|
||||
export type ExportExceptionDetails = t.TypeOf<typeof exportExceptionDetailsSchema>;
|
|
@ -23,6 +23,7 @@ export * from './entry_match';
|
|||
export * from './entry_match_any';
|
||||
export * from './entry_match_wildcard';
|
||||
export * from './entry_nested';
|
||||
export * from './exception_export_details';
|
||||
export * from './exception_list';
|
||||
export * from './exception_list_item_type';
|
||||
export * from './filter';
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ExportExceptionDetails } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
export interface ExportExceptionDetailsMock {
|
||||
listCount?: number;
|
||||
missingListsCount?: number;
|
||||
missingLists?: Array<Record<'list_id', string>>;
|
||||
itemCount?: number;
|
||||
missingItemCount?: number;
|
||||
missingItems?: Array<Record<'item_id', string>>;
|
||||
}
|
||||
|
||||
export const getExceptionExportDetailsMock = (
|
||||
details?: ExportExceptionDetailsMock
|
||||
): ExportExceptionDetails => ({
|
||||
exported_exception_list_count: details?.listCount ?? 0,
|
||||
exported_exception_list_item_count: details?.itemCount ?? 0,
|
||||
missing_exception_list_item_count: details?.missingItemCount ?? 0,
|
||||
missing_exception_list_items: details?.missingItems ?? [],
|
||||
missing_exception_lists: details?.missingLists ?? [],
|
||||
missing_exception_lists_count: details?.missingListsCount ?? 0,
|
||||
});
|
|
@ -15,6 +15,7 @@ import type {
|
|||
ExceptionListItemTypeOrUndefined,
|
||||
ExceptionListType,
|
||||
ExceptionListTypeOrUndefined,
|
||||
ExportExceptionDetails,
|
||||
FilterOrUndefined,
|
||||
Id,
|
||||
IdOrUndefined,
|
||||
|
@ -229,12 +230,5 @@ export interface ExportExceptionListAndItemsOptions {
|
|||
|
||||
export interface ExportExceptionListAndItemsReturn {
|
||||
exportData: string;
|
||||
exportDetails: {
|
||||
exported_exception_list_count: number;
|
||||
exported_exception_list_item_count: number;
|
||||
missing_exception_list_item_count: number;
|
||||
missing_exception_list_items: string[];
|
||||
missing_exception_lists: string[];
|
||||
missing_exception_lists_count: number;
|
||||
};
|
||||
exportDetails: ExportExceptionDetails;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
ExportExceptionDetails,
|
||||
IdOrUndefined,
|
||||
ListIdOrUndefined,
|
||||
NamespaceType,
|
||||
|
@ -25,14 +26,7 @@ interface ExportExceptionListAndItemsOptions {
|
|||
|
||||
export interface ExportExceptionListAndItemsReturn {
|
||||
exportData: string;
|
||||
exportDetails: {
|
||||
exported_exception_list_count: number;
|
||||
exported_exception_list_item_count: number;
|
||||
missing_exception_list_item_count: number;
|
||||
missing_exception_list_items: string[];
|
||||
missing_exception_lists: string[];
|
||||
missing_exception_lists_count: number;
|
||||
};
|
||||
exportDetails: ExportExceptionDetails;
|
||||
}
|
||||
|
||||
export const exportExceptionListAndItems = async ({
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ExportRulesDetails } from './export_rules_details_schema';
|
||||
import {
|
||||
ExportExceptionDetailsMock,
|
||||
getExceptionExportDetailsMock,
|
||||
} from '../../../../../lists/common/schemas/response/exception_export_details_schema.mock';
|
||||
|
||||
interface RuleDetailsMock {
|
||||
totalCount?: number;
|
||||
rulesCount?: number;
|
||||
missingCount?: number;
|
||||
missingRules?: Array<Record<'rule_id', string>>;
|
||||
}
|
||||
|
||||
export const getOutputDetailsSample = (ruleDetails?: RuleDetailsMock): ExportRulesDetails => ({
|
||||
exported_count: ruleDetails?.totalCount ?? 0,
|
||||
exported_rules_count: ruleDetails?.rulesCount ?? 0,
|
||||
missing_rules: ruleDetails?.missingRules ?? [],
|
||||
missing_rules_count: ruleDetails?.missingCount ?? 0,
|
||||
});
|
||||
|
||||
export const getOutputDetailsSampleWithExceptions = (
|
||||
ruleDetails?: RuleDetailsMock,
|
||||
exceptionDetails?: ExportExceptionDetailsMock
|
||||
): ExportRulesDetails => ({
|
||||
...getOutputDetailsSample(ruleDetails),
|
||||
...getExceptionExportDetailsMock(exceptionDetails),
|
||||
});
|
||||
|
||||
export const getSampleDetailsAsNdjson = (sample: ExportRulesDetails): string => {
|
||||
return `${JSON.stringify(sample)}\n`;
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import {
|
||||
getOutputDetailsSample,
|
||||
getOutputDetailsSampleWithExceptions,
|
||||
} from './export_rules_details_schema.mock';
|
||||
import {
|
||||
ExportRulesDetails,
|
||||
exportRulesDetailsWithExceptionsSchema,
|
||||
} from './export_rules_details_schema';
|
||||
|
||||
describe('exportRulesDetailsWithExceptionsSchema', () => {
|
||||
test('it should validate export details response', () => {
|
||||
const payload = getOutputDetailsSample();
|
||||
const decoded = exportRulesDetailsWithExceptionsSchema.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 validate export details with exceptions details response', () => {
|
||||
const payload = getOutputDetailsSampleWithExceptions();
|
||||
const decoded = exportRulesDetailsWithExceptionsSchema.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 strip out extra keys', () => {
|
||||
const payload: ExportRulesDetails & {
|
||||
extraKey?: string;
|
||||
} = getOutputDetailsSample();
|
||||
payload.extraKey = 'some extra key';
|
||||
const decoded = exportRulesDetailsWithExceptionsSchema.decode(payload);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(getOutputDetailsSample());
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { exportExceptionDetails } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
const createSchema = <Required extends t.Props, Optional extends t.Props>(
|
||||
requiredFields: Required,
|
||||
optionalFields: Optional
|
||||
) => {
|
||||
return t.intersection([t.exact(t.type(requiredFields)), t.exact(t.partial(optionalFields))]);
|
||||
};
|
||||
|
||||
export const exportRulesDetails = {
|
||||
exported_count: t.number,
|
||||
exported_rules_count: t.number,
|
||||
missing_rules: t.array(
|
||||
t.exact(
|
||||
t.type({
|
||||
rule_id: NonEmptyString,
|
||||
})
|
||||
)
|
||||
),
|
||||
missing_rules_count: t.number,
|
||||
};
|
||||
|
||||
const exportRulesDetailsSchema = t.exact(t.type(exportRulesDetails));
|
||||
export type ExportRulesDetailsSchema = t.TypeOf<typeof exportRulesDetailsSchema>;
|
||||
|
||||
// With exceptions
|
||||
export const exportRulesDetailsWithExceptionsSchema = createSchema(
|
||||
exportRulesDetails,
|
||||
exportExceptionDetails
|
||||
);
|
||||
|
||||
export type ExportRulesDetails = t.TypeOf<typeof exportRulesDetailsWithExceptionsSchema>;
|
|
@ -419,7 +419,63 @@ export const getEditedRule = (): CustomRule => ({
|
|||
});
|
||||
|
||||
export const expectedExportedRule = (ruleResponse: Cypress.Response<RulesSchema>): string => {
|
||||
const jsonrule = ruleResponse.body;
|
||||
const {
|
||||
id,
|
||||
updated_at: updatedAt,
|
||||
updated_by: updatedBy,
|
||||
created_at: createdAt,
|
||||
description,
|
||||
name,
|
||||
risk_score: riskScore,
|
||||
severity,
|
||||
query,
|
||||
} = ruleResponse.body;
|
||||
const rule = {
|
||||
id,
|
||||
updated_at: updatedAt,
|
||||
updated_by: updatedBy,
|
||||
created_at: createdAt,
|
||||
created_by: 'elastic',
|
||||
name,
|
||||
tags: [],
|
||||
interval: '100m',
|
||||
enabled: false,
|
||||
description,
|
||||
risk_score: riskScore,
|
||||
severity,
|
||||
output_index: '.siem-signals-default',
|
||||
author: [],
|
||||
false_positives: [],
|
||||
from: 'now-50000h',
|
||||
rule_id: 'rule_testing',
|
||||
max_signals: 100,
|
||||
risk_score_mapping: [],
|
||||
severity_mapping: [],
|
||||
threat: [],
|
||||
to: 'now',
|
||||
references: [],
|
||||
version: 1,
|
||||
exceptions_list: [],
|
||||
immutable: false,
|
||||
type: 'query',
|
||||
language: 'kuery',
|
||||
index: ['exceptions-*'],
|
||||
query,
|
||||
throttle: 'no_actions',
|
||||
actions: [],
|
||||
};
|
||||
const details = {
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_rules: [],
|
||||
missing_rules_count: 0,
|
||||
exported_exception_list_count: 0,
|
||||
exported_exception_list_item_count: 0,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
missing_exception_lists: [],
|
||||
missing_exception_lists_count: 0,
|
||||
};
|
||||
|
||||
return `{"id":"${jsonrule.id}","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","created_at":"${jsonrule.created_at}","created_by":"elastic","name":"${jsonrule.name}","tags":[],"interval":"100m","enabled":false,"description":"${jsonrule.description}","risk_score":${jsonrule.risk_score},"severity":"${jsonrule.severity}","output_index":".siem-signals-default","author":[],"false_positives":[],"from":"now-50000h","rule_id":"rule_testing","max_signals":100,"risk_score_mapping":[],"severity_mapping":[],"threat":[],"to":"now","references":[],"version":1,"exceptions_list":[],"immutable":false,"type":"query","language":"kuery","index":["exceptions-*"],"query":"${jsonrule.query}","throttle":"no_actions","actions":[]}\n{"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n`;
|
||||
return `${JSON.stringify(rule)}\n${JSON.stringify(details)}\n`;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,10 @@ import { createPromiseFromStreams } from '@kbn/utils';
|
|||
import { createRulesStreamFromNdJson } from './create_rules_stream_from_ndjson';
|
||||
import { BadRequestError } from '@kbn/securitysolution-es-utils';
|
||||
import { ImportRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/import_rules_schema';
|
||||
import {
|
||||
getOutputDetailsSample,
|
||||
getSampleDetailsAsNdjson,
|
||||
} from '../../../../common/detection_engine/schemas/response/export_rules_details_schema.mock';
|
||||
|
||||
type PromiseFromStreams = ImportRulesSchemaDecoded | Error;
|
||||
|
||||
|
@ -202,12 +206,13 @@ describe('create_rules_stream_from_ndjson', () => {
|
|||
test('filters the export details entry from the stream', async () => {
|
||||
const sample1 = getOutputSample();
|
||||
const sample2 = getOutputSample();
|
||||
const details = getOutputDetailsSample({ totalCount: 1, rulesCount: 1 });
|
||||
sample2.rule_id = 'rule-2';
|
||||
const ndJsonStream = new Readable({
|
||||
read() {
|
||||
this.push(getSampleAsNdjson(sample1));
|
||||
this.push(getSampleAsNdjson(sample2));
|
||||
this.push('{"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0}\n');
|
||||
this.push(getSampleDetailsAsNdjson(details));
|
||||
this.push(null);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -21,7 +21,6 @@ import {
|
|||
} from '../../../../common/detection_engine/schemas/request/import_rules_schema';
|
||||
import {
|
||||
parseNdjsonStrings,
|
||||
filterExportedRulesCounts,
|
||||
filterExceptions,
|
||||
createLimitStream,
|
||||
filterExportedCounts,
|
||||
|
@ -62,7 +61,6 @@ export const createRulesStreamFromNdJson = (ruleLimit: number) => {
|
|||
createSplitStream('\n'),
|
||||
parseNdjsonStrings(),
|
||||
filterExportedCounts(),
|
||||
filterExportedRulesCounts(),
|
||||
filterExceptions(),
|
||||
validateRules(),
|
||||
createLimitStream(ruleLimit),
|
||||
|
|
|
@ -15,6 +15,10 @@ import { rulesClientMock } from '../../../../../alerting/server/mocks';
|
|||
import { getExportAll } from './get_export_all';
|
||||
import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock';
|
||||
import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock';
|
||||
import {
|
||||
getOutputDetailsSampleWithExceptions,
|
||||
getSampleDetailsAsNdjson,
|
||||
} from '../../../../common/detection_engine/schemas/response/export_rules_details_schema.mock';
|
||||
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
import { getExceptionListClientMock } from '../../../../../lists/server/services/exception_lists/exception_list_client.mock';
|
||||
|
@ -103,6 +107,7 @@ describe.each([
|
|||
expect(detailsJson).toEqual({
|
||||
exported_exception_list_count: 1,
|
||||
exported_exception_list_item_count: 1,
|
||||
exported_count: 3,
|
||||
exported_rules_count: 1,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
|
@ -121,6 +126,7 @@ describe.each([
|
|||
total: 0,
|
||||
data: [],
|
||||
};
|
||||
const details = getOutputDetailsSampleWithExceptions();
|
||||
|
||||
rulesClient.find.mockResolvedValue(findResult);
|
||||
|
||||
|
@ -133,8 +139,7 @@ describe.each([
|
|||
);
|
||||
expect(exports).toEqual({
|
||||
rulesNdjson: '',
|
||||
exportDetails:
|
||||
'{"exported_rules_count":0,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n',
|
||||
exportDetails: getSampleDetailsAsNdjson(details),
|
||||
exceptionLists: '',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,6 +15,10 @@ import {
|
|||
import { rulesClientMock } from '../../../../../alerting/server/mocks';
|
||||
import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock';
|
||||
import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock';
|
||||
import {
|
||||
getSampleDetailsAsNdjson,
|
||||
getOutputDetailsSampleWithExceptions,
|
||||
} from '../../../../common/detection_engine/schemas/response/export_rules_details_schema.mock';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
import { getExceptionListClientMock } from '../../../../../lists/server/services/exception_lists/exception_list_client.mock';
|
||||
|
||||
|
@ -100,6 +104,7 @@ describe.each([
|
|||
exportDetails: {
|
||||
exported_exception_list_count: 0,
|
||||
exported_exception_list_item_count: 0,
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
|
@ -135,10 +140,13 @@ describe.each([
|
|||
logger,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
const details = getOutputDetailsSampleWithExceptions({
|
||||
missingRules: [{ rule_id: 'rule-1' }],
|
||||
missingCount: 1,
|
||||
});
|
||||
expect(exports).toEqual({
|
||||
rulesNdjson: '',
|
||||
exportDetails:
|
||||
'{"exported_rules_count":0,"missing_rules":[{"rule_id":"rule-1"}],"missing_rules_count":1,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n',
|
||||
exportDetails: getSampleDetailsAsNdjson(details),
|
||||
exceptionLists: '',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -72,7 +72,11 @@ export const getExportByObjectIds = async (
|
|||
exceptionDetails
|
||||
);
|
||||
|
||||
return { rulesNdjson, exportDetails, exceptionLists };
|
||||
return {
|
||||
rulesNdjson,
|
||||
exportDetails,
|
||||
exceptionLists,
|
||||
};
|
||||
};
|
||||
|
||||
export const getRulesFromObjects = async (
|
||||
|
|
|
@ -20,6 +20,7 @@ describe('getExportDetailsNdjson', () => {
|
|||
const details = getExportDetailsNdjson([rule]);
|
||||
const reParsed = JSON.parse(details);
|
||||
expect(reParsed).toEqual({
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_rules: [],
|
||||
missing_rules_count: 0,
|
||||
|
@ -31,6 +32,7 @@ describe('getExportDetailsNdjson', () => {
|
|||
const details = getExportDetailsNdjson([], [missingRule]);
|
||||
const reParsed = JSON.parse(details);
|
||||
expect(reParsed).toEqual({
|
||||
exported_count: 0,
|
||||
exported_rules_count: 0,
|
||||
missing_rules: [{ rule_id: 'rule-1' }],
|
||||
missing_rules_count: 1,
|
||||
|
@ -49,6 +51,7 @@ describe('getExportDetailsNdjson', () => {
|
|||
const details = getExportDetailsNdjson([rule1, rule2], [missingRule1, missingRule2]);
|
||||
const reParsed = JSON.parse(details);
|
||||
expect(reParsed).toEqual({
|
||||
exported_count: 2,
|
||||
exported_rules_count: 2,
|
||||
missing_rules: [missingRule1, missingRule2],
|
||||
missing_rules_count: 2,
|
||||
|
|
|
@ -5,18 +5,27 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExportExceptionDetails } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { ExportRulesDetails } from '../../../../common/detection_engine/schemas/response/export_rules_details_schema';
|
||||
import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema';
|
||||
|
||||
export const getExportDetailsNdjson = (
|
||||
rules: Array<Partial<RulesSchema>>,
|
||||
missingRules: Array<{ rule_id: string }> = [],
|
||||
extraMeta: Record<string, number | string | string[]> = {}
|
||||
exceptionDetails?: ExportExceptionDetails
|
||||
): string => {
|
||||
const stringified = JSON.stringify({
|
||||
const stringified: ExportRulesDetails = {
|
||||
exported_count:
|
||||
exceptionDetails == null
|
||||
? rules.length
|
||||
: rules.length +
|
||||
exceptionDetails.exported_exception_list_count +
|
||||
exceptionDetails.exported_exception_list_item_count,
|
||||
exported_rules_count: rules.length,
|
||||
missing_rules: missingRules,
|
||||
missing_rules_count: missingRules.length,
|
||||
...extraMeta,
|
||||
});
|
||||
return `${stringified}\n`;
|
||||
...exceptionDetails,
|
||||
};
|
||||
return `${JSON.stringify(stringified)}\n`;
|
||||
};
|
||||
|
|
|
@ -34,12 +34,6 @@ export const filterExportedCounts = (): Transform => {
|
|||
);
|
||||
};
|
||||
|
||||
export const filterExportedRulesCounts = (): Transform => {
|
||||
return createFilterStream<ImportRulesSchemaDecoded | RulesObjectsExportResultDetails>(
|
||||
(obj) => obj != null && !has('exported_rules_count', obj)
|
||||
);
|
||||
};
|
||||
|
||||
export const filterExceptions = (): Transform => {
|
||||
return createFilterStream<ImportRulesSchemaDecoded | RulesObjectsExportResultDetails>(
|
||||
(obj) => obj != null && !has('list_id', obj)
|
||||
|
|
|
@ -78,6 +78,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(bodySplitAndParsed).to.eql({
|
||||
exported_exception_list_count: 0,
|
||||
exported_exception_list_item_count: 0,
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
|
|
|
@ -79,6 +79,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(bodySplitAndParsed).to.eql({
|
||||
exported_exception_list_count: 0,
|
||||
exported_exception_list_item_count: 0,
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
|
|
|
@ -59,6 +59,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(exportDetails).to.eql({
|
||||
exported_exception_list_count: 0,
|
||||
exported_exception_list_item_count: 0,
|
||||
exported_count: 1,
|
||||
exported_rules_count: 1,
|
||||
missing_exception_list_item_count: 0,
|
||||
missing_exception_list_items: [],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue