Skip to content

Commit

Permalink
[SIEM][Detection Engine] Fixes import bug with non existent signals i…
Browse files Browse the repository at this point in the history
…ndex (elastic#65595) (elastic#65690)

See: elastic#65565

* Fixes it to where if there is an import without an index then the rule is not created

### Checklist

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
  • Loading branch information
FrankHassanabad committed May 7, 2020
1 parent 1c2449f commit 49feeb3
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,11 @@ describe('import_rules_route', () => {
clients.siemClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex());
const response = await server.inject(request, context);
expect(response.status).toEqual(200);
expect(response.status).toEqual(400);
expect(response.body).toEqual({
errors: [
{
error: {
message:
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
status_code: 409,
},
rule_id: 'rule-1',
},
],
success: false,
success_count: 0,
message:
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
status_code: 400,
});
});

Expand All @@ -145,19 +136,10 @@ describe('import_rules_route', () => {
});

const response = await server.inject(request, context);
expect(response.status).toEqual(200);
expect(response.status).toEqual(500);
expect(response.body).toEqual({
errors: [
{
error: {
message: 'Test error',
status_code: 400,
},
rule_id: 'rule-1',
},
],
success: false,
success_count: 0,
message: 'Test error',
status_code: 500,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => {
body: `Invalid file extension ${fileExtension}`,
});
}
const signalsIndex = siemClient.getSignalsIndex();
const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, signalsIndex);
if (!indexExists) {
return siemResponse.error({
statusCode: 400,
body: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`,
});
}

const objectLimit = config.maxRuleImportExportSize;
const readStream = createRulesStreamFromNdJson(objectLimit);
Expand All @@ -94,166 +102,150 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => {
const batchParseObjects = chunkParseObjects.shift() ?? [];
const newImportRuleResponse = await Promise.all(
batchParseObjects.reduce<Array<Promise<ImportRuleResponse>>>((accum, parsedRule) => {
const importsWorkerPromise = new Promise<ImportRuleResponse>(
async (resolve, reject) => {
if (parsedRule instanceof Error) {
// If the JSON object had a validation or parse error then we return
// early with the error and an (unknown) for the ruleId
resolve(
createBulkErrorObject({
statusCode: 400,
message: parsedRule.message,
})
);
return null;
}
const {
anomaly_threshold: anomalyThreshold,
description,
enabled,
false_positives: falsePositives,
from,
immutable,
query,
language,
machine_learning_job_id: machineLearningJobId,
output_index: outputIndex,
saved_id: savedId,
meta,
filters,
rule_id: ruleId,
index,
interval,
max_signals: maxSignals,
risk_score: riskScore,
name,
severity,
tags,
threat,
to,
type,
references,
note,
timeline_id: timelineId,
timeline_title: timelineTitle,
version,
exceptions_list,
} = parsedRule;
const importsWorkerPromise = new Promise<ImportRuleResponse>(async resolve => {
if (parsedRule instanceof Error) {
// If the JSON object had a validation or parse error then we return
// early with the error and an (unknown) for the ruleId
resolve(
createBulkErrorObject({
statusCode: 400,
message: parsedRule.message,
})
);
return null;
}
const {
anomaly_threshold: anomalyThreshold,
description,
enabled,
false_positives: falsePositives,
from,
immutable,
query,
language,
machine_learning_job_id: machineLearningJobId,
output_index: outputIndex,
saved_id: savedId,
meta,
filters,
rule_id: ruleId,
index,
interval,
max_signals: maxSignals,
risk_score: riskScore,
name,
severity,
tags,
threat,
to,
type,
references,
note,
timeline_id: timelineId,
timeline_title: timelineTitle,
version,
exceptions_list,
} = parsedRule;

try {
validateLicenseForRuleType({
license: context.licensing.license,
ruleType: type,
});
try {
validateLicenseForRuleType({
license: context.licensing.license,
ruleType: type,
});

const signalsIndex = siemClient.getSignalsIndex();
const indexExists = await getIndexExists(
clusterClient.callAsCurrentUser,
signalsIndex
);
if (!indexExists) {
resolve(
createBulkErrorObject({
ruleId,
statusCode: 409,
message: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`,
})
);
}
const rule = await readRules({ alertsClient, ruleId });
if (rule == null) {
await createRules({
alertsClient,
anomalyThreshold,
description,
enabled,
falsePositives,
from,
immutable,
query,
language,
machineLearningJobId,
outputIndex: signalsIndex,
savedId,
timelineId,
timelineTitle,
meta,
filters,
ruleId,
index,
interval,
maxSignals,
riskScore,
name,
severity,
tags,
to,
type,
threat,
references,
note,
version,
exceptions_list,
actions: [], // Actions are not imported nor exported at this time
});
resolve({ rule_id: ruleId, status_code: 200 });
} else if (rule != null && request.query.overwrite) {
await patchRules({
alertsClient,
savedObjectsClient,
description,
enabled,
falsePositives,
from,
immutable,
query,
language,
outputIndex,
savedId,
timelineId,
timelineTitle,
meta,
filters,
id: undefined,
ruleId,
index,
interval,
maxSignals,
riskScore,
name,
severity,
tags,
to,
type,
threat,
references,
note,
version,
exceptions_list,
anomalyThreshold,
machineLearningJobId,
});
resolve({ rule_id: ruleId, status_code: 200 });
} else if (rule != null) {
resolve(
createBulkErrorObject({
ruleId,
statusCode: 409,
message: `rule_id: "${ruleId}" already exists`,
})
);
}
} catch (err) {
const rule = await readRules({ alertsClient, ruleId });
if (rule == null) {
await createRules({
alertsClient,
anomalyThreshold,
description,
enabled,
falsePositives,
from,
immutable,
query,
language,
machineLearningJobId,
outputIndex: signalsIndex,
savedId,
timelineId,
timelineTitle,
meta,
filters,
ruleId,
index,
interval,
maxSignals,
riskScore,
name,
severity,
tags,
to,
type,
threat,
references,
note,
version,
exceptions_list,
actions: [], // Actions are not imported nor exported at this time
});
resolve({ rule_id: ruleId, status_code: 200 });
} else if (rule != null && request.query.overwrite) {
await patchRules({
alertsClient,
savedObjectsClient,
description,
enabled,
falsePositives,
from,
immutable,
query,
language,
outputIndex,
savedId,
timelineId,
timelineTitle,
meta,
filters,
id: undefined,
ruleId,
index,
interval,
maxSignals,
riskScore,
name,
severity,
tags,
to,
type,
threat,
references,
note,
version,
exceptions_list,
anomalyThreshold,
machineLearningJobId,
});
resolve({ rule_id: ruleId, status_code: 200 });
} else if (rule != null) {
resolve(
createBulkErrorObject({
ruleId,
statusCode: 400,
message: err.message,
statusCode: 409,
message: `rule_id: "${ruleId}" already exists`,
})
);
}
} catch (err) {
resolve(
createBulkErrorObject({
ruleId,
statusCode: 400,
message: err.message,
})
);
}
);
});
return [...accum, importsWorkerPromise];
}, [])
);
Expand Down
Loading

0 comments on commit 49feeb3

Please sign in to comment.