Skip to content

Commit e3b4b99

Browse files
[SIEM][Detection Engine] Fixes import bug with non existent signals index (#65595)
See: #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
1 parent 459daa7 commit e3b4b99

File tree

3 files changed

+200
-177
lines changed

3 files changed

+200
-177
lines changed

x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,11 @@ describe('import_rules_route', () => {
122122
clients.siemClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
123123
clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex());
124124
const response = await server.inject(request, context);
125-
expect(response.status).toEqual(200);
125+
expect(response.status).toEqual(400);
126126
expect(response.body).toEqual({
127-
errors: [
128-
{
129-
error: {
130-
message:
131-
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
132-
status_code: 409,
133-
},
134-
rule_id: 'rule-1',
135-
},
136-
],
137-
success: false,
138-
success_count: 0,
127+
message:
128+
'To create a rule, the index must exist first. Index mockSignalsIndex does not exist',
129+
status_code: 400,
139130
});
140131
});
141132

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

147138
const response = await server.inject(request, context);
148-
expect(response.status).toEqual(200);
139+
expect(response.status).toEqual(500);
149140
expect(response.body).toEqual({
150-
errors: [
151-
{
152-
error: {
153-
message: 'Test error',
154-
status_code: 400,
155-
},
156-
rule_id: 'rule-1',
157-
},
158-
],
159-
success: false,
160-
success_count: 0,
141+
message: 'Test error',
142+
status_code: 500,
161143
});
162144
});
163145

x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts

Lines changed: 143 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => {
7575
body: `Invalid file extension ${fileExtension}`,
7676
});
7777
}
78+
const signalsIndex = siemClient.getSignalsIndex();
79+
const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, signalsIndex);
80+
if (!indexExists) {
81+
return siemResponse.error({
82+
statusCode: 400,
83+
body: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`,
84+
});
85+
}
7886

7987
const objectLimit = config.maxRuleImportExportSize;
8088
const readStream = createRulesStreamFromNdJson(objectLimit);
@@ -94,166 +102,150 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => {
94102
const batchParseObjects = chunkParseObjects.shift() ?? [];
95103
const newImportRuleResponse = await Promise.all(
96104
batchParseObjects.reduce<Array<Promise<ImportRuleResponse>>>((accum, parsedRule) => {
97-
const importsWorkerPromise = new Promise<ImportRuleResponse>(
98-
async (resolve, reject) => {
99-
if (parsedRule instanceof Error) {
100-
// If the JSON object had a validation or parse error then we return
101-
// early with the error and an (unknown) for the ruleId
102-
resolve(
103-
createBulkErrorObject({
104-
statusCode: 400,
105-
message: parsedRule.message,
106-
})
107-
);
108-
return null;
109-
}
110-
const {
111-
anomaly_threshold: anomalyThreshold,
112-
description,
113-
enabled,
114-
false_positives: falsePositives,
115-
from,
116-
immutable,
117-
query,
118-
language,
119-
machine_learning_job_id: machineLearningJobId,
120-
output_index: outputIndex,
121-
saved_id: savedId,
122-
meta,
123-
filters,
124-
rule_id: ruleId,
125-
index,
126-
interval,
127-
max_signals: maxSignals,
128-
risk_score: riskScore,
129-
name,
130-
severity,
131-
tags,
132-
threat,
133-
to,
134-
type,
135-
references,
136-
note,
137-
timeline_id: timelineId,
138-
timeline_title: timelineTitle,
139-
version,
140-
exceptions_list,
141-
} = parsedRule;
105+
const importsWorkerPromise = new Promise<ImportRuleResponse>(async resolve => {
106+
if (parsedRule instanceof Error) {
107+
// If the JSON object had a validation or parse error then we return
108+
// early with the error and an (unknown) for the ruleId
109+
resolve(
110+
createBulkErrorObject({
111+
statusCode: 400,
112+
message: parsedRule.message,
113+
})
114+
);
115+
return null;
116+
}
117+
const {
118+
anomaly_threshold: anomalyThreshold,
119+
description,
120+
enabled,
121+
false_positives: falsePositives,
122+
from,
123+
immutable,
124+
query,
125+
language,
126+
machine_learning_job_id: machineLearningJobId,
127+
output_index: outputIndex,
128+
saved_id: savedId,
129+
meta,
130+
filters,
131+
rule_id: ruleId,
132+
index,
133+
interval,
134+
max_signals: maxSignals,
135+
risk_score: riskScore,
136+
name,
137+
severity,
138+
tags,
139+
threat,
140+
to,
141+
type,
142+
references,
143+
note,
144+
timeline_id: timelineId,
145+
timeline_title: timelineTitle,
146+
version,
147+
exceptions_list,
148+
} = parsedRule;
142149

143-
try {
144-
validateLicenseForRuleType({
145-
license: context.licensing.license,
146-
ruleType: type,
147-
});
150+
try {
151+
validateLicenseForRuleType({
152+
license: context.licensing.license,
153+
ruleType: type,
154+
});
148155

149-
const signalsIndex = siemClient.getSignalsIndex();
150-
const indexExists = await getIndexExists(
151-
clusterClient.callAsCurrentUser,
152-
signalsIndex
153-
);
154-
if (!indexExists) {
155-
resolve(
156-
createBulkErrorObject({
157-
ruleId,
158-
statusCode: 409,
159-
message: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`,
160-
})
161-
);
162-
}
163-
const rule = await readRules({ alertsClient, ruleId });
164-
if (rule == null) {
165-
await createRules({
166-
alertsClient,
167-
anomalyThreshold,
168-
description,
169-
enabled,
170-
falsePositives,
171-
from,
172-
immutable,
173-
query,
174-
language,
175-
machineLearningJobId,
176-
outputIndex: signalsIndex,
177-
savedId,
178-
timelineId,
179-
timelineTitle,
180-
meta,
181-
filters,
182-
ruleId,
183-
index,
184-
interval,
185-
maxSignals,
186-
riskScore,
187-
name,
188-
severity,
189-
tags,
190-
to,
191-
type,
192-
threat,
193-
references,
194-
note,
195-
version,
196-
exceptions_list,
197-
actions: [], // Actions are not imported nor exported at this time
198-
});
199-
resolve({ rule_id: ruleId, status_code: 200 });
200-
} else if (rule != null && request.query.overwrite) {
201-
await patchRules({
202-
alertsClient,
203-
savedObjectsClient,
204-
description,
205-
enabled,
206-
falsePositives,
207-
from,
208-
immutable,
209-
query,
210-
language,
211-
outputIndex,
212-
savedId,
213-
timelineId,
214-
timelineTitle,
215-
meta,
216-
filters,
217-
id: undefined,
218-
ruleId,
219-
index,
220-
interval,
221-
maxSignals,
222-
riskScore,
223-
name,
224-
severity,
225-
tags,
226-
to,
227-
type,
228-
threat,
229-
references,
230-
note,
231-
version,
232-
exceptions_list,
233-
anomalyThreshold,
234-
machineLearningJobId,
235-
});
236-
resolve({ rule_id: ruleId, status_code: 200 });
237-
} else if (rule != null) {
238-
resolve(
239-
createBulkErrorObject({
240-
ruleId,
241-
statusCode: 409,
242-
message: `rule_id: "${ruleId}" already exists`,
243-
})
244-
);
245-
}
246-
} catch (err) {
156+
const rule = await readRules({ alertsClient, ruleId });
157+
if (rule == null) {
158+
await createRules({
159+
alertsClient,
160+
anomalyThreshold,
161+
description,
162+
enabled,
163+
falsePositives,
164+
from,
165+
immutable,
166+
query,
167+
language,
168+
machineLearningJobId,
169+
outputIndex: signalsIndex,
170+
savedId,
171+
timelineId,
172+
timelineTitle,
173+
meta,
174+
filters,
175+
ruleId,
176+
index,
177+
interval,
178+
maxSignals,
179+
riskScore,
180+
name,
181+
severity,
182+
tags,
183+
to,
184+
type,
185+
threat,
186+
references,
187+
note,
188+
version,
189+
exceptions_list,
190+
actions: [], // Actions are not imported nor exported at this time
191+
});
192+
resolve({ rule_id: ruleId, status_code: 200 });
193+
} else if (rule != null && request.query.overwrite) {
194+
await patchRules({
195+
alertsClient,
196+
savedObjectsClient,
197+
description,
198+
enabled,
199+
falsePositives,
200+
from,
201+
immutable,
202+
query,
203+
language,
204+
outputIndex,
205+
savedId,
206+
timelineId,
207+
timelineTitle,
208+
meta,
209+
filters,
210+
id: undefined,
211+
ruleId,
212+
index,
213+
interval,
214+
maxSignals,
215+
riskScore,
216+
name,
217+
severity,
218+
tags,
219+
to,
220+
type,
221+
threat,
222+
references,
223+
note,
224+
version,
225+
exceptions_list,
226+
anomalyThreshold,
227+
machineLearningJobId,
228+
});
229+
resolve({ rule_id: ruleId, status_code: 200 });
230+
} else if (rule != null) {
247231
resolve(
248232
createBulkErrorObject({
249233
ruleId,
250-
statusCode: 400,
251-
message: err.message,
234+
statusCode: 409,
235+
message: `rule_id: "${ruleId}" already exists`,
252236
})
253237
);
254238
}
239+
} catch (err) {
240+
resolve(
241+
createBulkErrorObject({
242+
ruleId,
243+
statusCode: 400,
244+
message: err.message,
245+
})
246+
);
255247
}
256-
);
248+
});
257249
return [...accum, importsWorkerPromise];
258250
}, [])
259251
);

0 commit comments

Comments
 (0)