Skip to content

Commit

Permalink
Replace patch, patch bulk, import rules schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallmain committed Jun 27, 2022
1 parent bcc17e1 commit 751d300
Show file tree
Hide file tree
Showing 21 changed files with 533 additions and 1,107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

import * as t from 'io-ts';

import { patchRulesSchema, PatchRulesSchemaDecoded } from './patch_rules_schema';
import { patchRulesSchema } from './rule_schemas';

export const patchRulesBulkSchema = t.array(patchRulesSchema);
export type PatchRulesBulkSchema = t.TypeOf<typeof patchRulesBulkSchema>;

export type PatchRulesBulkSchemaDecoded = PatchRulesSchemaDecoded[];
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
throttle,
} from '@kbn/securitysolution-io-ts-alerting-types';
import { listArray } from '@kbn/securitysolution-io-ts-list-types';
import { version } from '@kbn/securitysolution-io-ts-types';
import { OnlyFalseAllowed, version } from '@kbn/securitysolution-io-ts-types';

import {
id,
Expand Down Expand Up @@ -207,6 +207,14 @@ export const sharedUpdateSchema = t.intersection([
]);
export type SharedUpdateSchema = t.TypeOf<typeof sharedUpdateSchema>;

export const sharedPatchSchema = t.intersection([
basePatchParams,
t.exact(t.partial({ rule_id })),
t.exact(t.partial({ id })),
]);

// START type specific parameter definitions
// -----------------------------------------
const eqlRuleParams = {
required: {
type: t.literal('eql'),
Expand Down Expand Up @@ -348,6 +356,8 @@ const {
} = buildAPISchemas(machineLearningRuleParams);

export { machineLearningCreateParams };
// ---------------------------------------
// END type specific parameter definitions

const createTypeSpecific = t.union([
eqlCreateParams,
Expand Down Expand Up @@ -379,6 +389,43 @@ export const previewRulesSchema = t.intersection([
]);
export type PreviewRulesSchema = t.TypeOf<typeof previewRulesSchema>;

// TODO: test `version` overwriting `version` from baseParams
export const addPrepackagedRulesSchema = t.intersection([
baseCreateParams,
createTypeSpecific,
// version is required in addPrepackagedRulesSchema, so this supercedes the defaultable
// version in baseParams
t.exact(t.type({ rule_id, version })),
t.exact(
t.partial({
related_integrations: RelatedIntegrationArray,
required_fields: RequiredFieldArray,
setup: SetupGuide,
})
),
]);
export type AddPrepackagedRulesSchema = t.TypeOf<typeof addPrepackagedRulesSchema>;

export const importRulesSchema = t.intersection([
baseCreateParams,
createTypeSpecific,
t.exact(t.type({ rule_id })),
t.exact(
t.partial({
id,
immutable: OnlyFalseAllowed,
updated_at,
updated_by,
created_at,
created_by,
related_integrations: RelatedIntegrationArray,
required_fields: RequiredFieldArray,
setup: SetupGuide,
})
),
]);
export type ImportRulesSchema = t.TypeOf<typeof importRulesSchema>;

type UpdateSchema<T> = SharedUpdateSchema & T;
export type EqlUpdateSchema = UpdateSchema<t.TypeOf<typeof eqlCreateParams>>;
export type ThreatMatchUpdateSchema = UpdateSchema<t.TypeOf<typeof threatMatchCreateParams>>;
Expand All @@ -397,6 +444,22 @@ const patchTypeSpecific = t.union([
thresholdPatchParams,
machineLearningPatchParams,
]);
export {
eqlPatchParams,
threatMatchPatchParams,
queryPatchParams,
savedQueryPatchParams,
thresholdPatchParams,
machineLearningPatchParams,
};
export type PatchTypeSpecific = t.TypeOf<typeof patchTypeSpecific>;

export type EqlPatchParams = t.TypeOf<typeof eqlPatchParams>;
export type ThreatMatchPatchParams = t.TypeOf<typeof threatMatchPatchParams>;
export type QueryPatchParams = t.TypeOf<typeof queryPatchParams>;
export type SavedQueryPatchParams = t.TypeOf<typeof savedQueryPatchParams>;
export type ThresholdPatchParams = t.TypeOf<typeof thresholdPatchParams>;
export type MachineLearningPatchParams = t.TypeOf<typeof machineLearningPatchParams>;

const responseTypeSpecific = t.union([
eqlResponseParams,
Expand All @@ -411,11 +474,27 @@ export type ResponseTypeSpecific = t.TypeOf<typeof responseTypeSpecific>;
export const updateRulesSchema = t.intersection([createTypeSpecific, sharedUpdateSchema]);
export type UpdateRulesSchema = t.TypeOf<typeof updateRulesSchema>;

export const fullPatchSchema = t.intersection([
basePatchParams,
patchTypeSpecific,
t.exact(t.partial({ id })),
export const eqlFullPatchSchema = t.intersection([eqlPatchParams, sharedPatchSchema]);
export type EqlFullPatchSchema = t.TypeOf<typeof eqlFullPatchSchema>;
export const threatMatchFullPatchSchema = t.intersection([
threatMatchPatchParams,
sharedPatchSchema,
]);
export type ThreatMatchFullPatchSchema = t.TypeOf<typeof threatMatchFullPatchSchema>;
export const queryFullPatchSchema = t.intersection([queryPatchParams, sharedPatchSchema]);
export type QueryFullPatchSchema = t.TypeOf<typeof queryFullPatchSchema>;
export const savedQueryFullPatchSchema = t.intersection([savedQueryPatchParams, sharedPatchSchema]);
export type SavedQueryFullPatchSchema = t.TypeOf<typeof savedQueryFullPatchSchema>;
export const thresholdFullPatchSchema = t.intersection([thresholdPatchParams, sharedPatchSchema]);
export type ThresholdFullPatchSchema = t.TypeOf<typeof thresholdFullPatchSchema>;
export const machineLearningFullPatchSchema = t.intersection([
machineLearningPatchParams,
sharedPatchSchema,
]);
export type MachineLearningFullPatchSchema = t.TypeOf<typeof machineLearningFullPatchSchema>;

export const patchRulesSchema = t.intersection([patchTypeSpecific, sharedPatchSchema]);
export type PatchRulesSchema = t.TypeOf<typeof patchRulesSchema>;

const responseRequiredFields = {
id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,8 @@ export const createPrepackagedRules = async (
});
const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules);
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules);
const signalsIndex = siemClient.getSignalsIndex();

await Promise.all(installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex));
await Promise.all(installPrepackagedRules(rulesClient, rulesToInstall, siemClient));
const timeline = await installPrepackagedTimelines(
maxTimelineImportExportSize,
frameworkRequest,
Expand All @@ -134,7 +133,7 @@ export const createPrepackagedRules = async (
rulesClient,
savedObjectsClient,
rulesToUpdate,
signalsIndex,
siemClient,
context.getRuleExecutionLog()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
} from './utils/import_rules_utils';
import { getReferencedExceptionLists } from './utils/gather_referenced_exceptions';
import { importRuleExceptions } from './utils/import_rule_exceptions';
import { ImportRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request';
import { ImportRulesSchema } from '../../../../../common/detection_engine/schemas/request/rule_schemas';

const CHUNK_PARSED_OBJECT_SIZE = 50;

Expand Down Expand Up @@ -91,6 +91,7 @@ export const importRulesRoute = (
});
const savedObjectsClient = ctx.core.savedObjects.client;
const exceptionsClient = ctx.lists?.getExceptionListClient();
const siemClient = ctx.securitySolution.getAppClient();

const mlAuthz = buildMlAuthz({
license: ctx.licensing.license,
Expand Down Expand Up @@ -140,10 +141,10 @@ export const importRulesRoute = (
let parsedRules;
let actionErrors: BulkError[] = [];
const actualRules = rules.filter(
(rule): rule is ImportRulesSchemaDecoded => !(rule instanceof Error)
(rule): rule is ImportRulesSchema => !(rule instanceof Error)
);

if (actualRules.some((rule) => rule.actions.length > 0)) {
if (actualRules.some((rule) => rule.actions && rule.actions.length > 0)) {
const [nonExistentActionErrors, uniqueParsedObjects] = await getInvalidConnectors(
migratedParsedObjectsWithoutDuplicateErrors,
actionsClient
Expand Down Expand Up @@ -171,6 +172,7 @@ export const importRulesRoute = (
exceptionsClient,
spaceId: ctx.securitySolution.getSpaceId(),
existingLists: foundReferencedExceptionLists,
siemClient,
});

const errorsResp = importRuleResponse.filter((resp) => isBulkError(resp)) as BulkError[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@

import { validate } from '@kbn/securitysolution-io-ts-utils';
import { Logger } from '@kbn/core/server';
import { RuleAlertAction } from '../../../../../common/detection_engine/types';
import {
patchRulesBulkSchema,
PatchRulesBulkSchemaDecoded,
} from '../../../../../common/detection_engine/schemas/request/patch_rules_bulk_schema';
import { patchRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/patch_rules_bulk_schema';
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema';
import type { SecuritySolutionPluginRouter } from '../../../../types';
Expand All @@ -24,7 +20,6 @@ import { getIdBulkError } from './utils';
import { transformValidateBulkError } from './validate';
import { patchRules } from '../../rules/patch_rules';
import { readRules } from '../../rules/read_rules';
import { PartialFilter } from '../../types';
import { legacyMigrate } from '../../rules/utils';
import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './utils/deprecation';

Expand All @@ -40,9 +35,7 @@ export const patchRulesBulkRoute = (
{
path: DETECTION_ENGINE_RULES_BULK_UPDATE,
validate: {
body: buildRouteValidation<typeof patchRulesBulkSchema, PatchRulesBulkSchemaDecoded>(
patchRulesBulkSchema
),
body: buildRouteValidation<typeof patchRulesBulkSchema>(patchRulesBulkSchema),
},
options: {
tags: ['access:securitySolution'],
Expand All @@ -67,75 +60,18 @@ export const patchRulesBulkRoute = (
});
const rules = await Promise.all(
request.body.map(async (payloadRule) => {
const {
actions: actionsRest,
author,
building_block_type: buildingBlockType,
description,
enabled,
timestamp_field: timestampField,
event_category_override: eventCategoryOverride,
tiebreaker_field: tiebreakerField,
false_positives: falsePositives,
from,
query,
language,
license,
output_index: outputIndex,
saved_id: savedId,
timeline_id: timelineId,
timeline_title: timelineTitle,
meta,
filters: filtersRest,
rule_id: ruleId,
id,
index,
data_view_id: dataViewId,
interval,
max_signals: maxSignals,
risk_score: riskScore,
risk_score_mapping: riskScoreMapping,
rule_name_override: ruleNameOverride,
name,
severity,
severity_mapping: severityMapping,
tags,
to,
type,
threat,
threshold,
threat_filters: threatFilters,
threat_index: threatIndex,
threat_indicator_path: threatIndicatorPath,
threat_query: threatQuery,
threat_mapping: threatMapping,
threat_language: threatLanguage,
concurrent_searches: concurrentSearches,
items_per_search: itemsPerSearch,
timestamp_override: timestampOverride,
throttle,
references,
note,
version,
anomaly_threshold: anomalyThreshold,
machine_learning_job_id: machineLearningJobId,
exceptions_list: exceptionsList,
} = payloadRule;
const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)';
// TODO: Fix these either with an is conversion or by better typing them within io-ts
const actions: RuleAlertAction[] = actionsRest as RuleAlertAction[];
const filters: PartialFilter[] | undefined = filtersRest as PartialFilter[];
const idOrRuleIdOrUnknown = payloadRule.id ?? payloadRule.rule_id ?? '(unknown id)';

try {
if (type) {
if (payloadRule.type) {
// reject an unauthorized "promotion" to ML
throwAuthzError(await mlAuthz.validateRuleType(type));
throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type));
}

const existingRule = await readRules({
rulesClient,
ruleId,
id,
ruleId: payloadRule.rule_id,
id: payloadRule.id,
});
if (existingRule?.params.type) {
// reject an unauthorized modification of an ML rule
Expand All @@ -151,62 +87,13 @@ export const patchRulesBulkRoute = (
const rule = await patchRules({
rule: migratedRule,
rulesClient,
author,
buildingBlockType,
description,
enabled,
timestampField,
eventCategoryOverride,
tiebreakerField,
falsePositives,
from,
query,
language,
license,
outputIndex,
savedId,
timelineId,
timelineTitle,
meta,
filters,
index,
dataViewId,
interval,
maxSignals,
riskScore,
riskScoreMapping,
ruleNameOverride,
name,
severity,
severityMapping,
tags,
to,
type,
threat,
threshold,
threatFilters,
threatIndex,
threatIndicatorPath,
threatQuery,
threatMapping,
threatLanguage,
throttle,
concurrentSearches,
itemsPerSearch,
timestampOverride,
references,
note,
version,
anomalyThreshold,
machineLearningJobId,
actions,
exceptionsList,
params: payloadRule,
});
if (rule != null && rule.enabled != null && rule.name != null) {
const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id);
return transformValidateBulkError(rule.id, rule, ruleExecutionSummary);
} else {
return getIdBulkError({ id, ruleId });
return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id });
}
} catch (err) {
return transformBulkError(idOrRuleIdOrUnknown, err);
Expand Down
Loading

0 comments on commit 751d300

Please sign in to comment.