Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [SIEM] Detections create prepackage rules (#55403) #55503

Merged
merged 1 commit into from
Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion x-pack/legacy/plugins/siem/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export const DETECTION_ENGINE_PREPACKAGED_URL = `${DETECTION_ENGINE_RULES_URL}/p
export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges`;
export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index`;
export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags`;
export const DETECTION_ENGINE_RULES_STATUS = `${DETECTION_ENGINE_URL}/rules/_find_statuses`;
export const DETECTION_ENGINE_RULES_STATUS_URL = `${DETECTION_ENGINE_RULES_URL}/_find_statuses`;
export const DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL = `${DETECTION_ENGINE_RULES_URL}/prepackaged/_status`;

/**
* Default signals index key for kibana.dev.yml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import { throwIfNotOk } from '../../../hooks/api/api';
import {
DETECTION_ENGINE_RULES_URL,
DETECTION_ENGINE_PREPACKAGED_URL,
DETECTION_ENGINE_RULES_STATUS,
DETECTION_ENGINE_RULES_STATUS_URL,
DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL,
} from '../../../../common/constants';
import * as i18n from '../../../pages/detection_engine/rules/translations';

Expand Down Expand Up @@ -63,7 +64,7 @@ export const addRule = async ({ rule, signal }: AddRulesProps): Promise<NewRule>
export const fetchRules = async ({
filterOptions = {
filter: '',
sortField: 'enabled',
sortField: 'name',
sortOrder: 'desc',
},
pagination = {
Expand Down Expand Up @@ -313,6 +314,7 @@ export const exportRules = async ({
* Get Rule Status provided Rule ID
*
* @param id string of Rule ID's (not rule_id)
* @param signal AbortSignal for cancelling request
*
* @throws An error if response is not OK
*/
Expand All @@ -324,7 +326,7 @@ export const getRuleStatusById = async ({
signal: AbortSignal;
}): Promise<Record<string, RuleStatus>> => {
const response = await fetch(
`${chrome.getBasePath()}${DETECTION_ENGINE_RULES_STATUS}?ids=${encodeURIComponent(
`${chrome.getBasePath()}${DETECTION_ENGINE_RULES_STATUS_URL}?ids=${encodeURIComponent(
JSON.stringify([id])
)}`,
{
Expand All @@ -341,3 +343,36 @@ export const getRuleStatusById = async ({
await throwIfNotOk(response);
return response.json();
};

/**
* Get pre packaged rules Status
*
* @param signal AbortSignal for cancelling request
*
* @throws An error if response is not OK
*/
export const getPrePackagedRulesStatus = async ({
signal,
}: {
signal: AbortSignal;
}): Promise<{
rules_installed: number;
rules_not_installed: number;
rules_not_updated: number;
}> => {
const response = await fetch(
`${chrome.getBasePath()}${DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL}`,
{
method: 'GET',
credentials: 'same-origin',
headers: {
'content-type': 'application/json',
'kbn-xsrf': 'true',
},
signal,
}
);

await throwIfNotOk(response);
return response.json();
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export * from './persist_rule';
export * from './types';
export * from './use_rule';
export * from './use_rules';
export * from './use_pre_packaged_rules';
export * from './use_rule_status';
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ export const RULE_ADD_FAILURE = i18n.translate(
defaultMessage: 'Failed to add Rule',
}
);

export const RULE_PREPACKAGED_FAILURE = i18n.translate(
'xpack.siem.containers.detectionEngine.createPrePackagedRuleFailDescription',
{
defaultMessage: 'Failed to installed pre-packaged rules from elastic',
}
);

export const RULE_PREPACKAGED_SUCCESS = i18n.translate(
'xpack.siem.containers.detectionEngine.createPrePackagedRuleSuccesDescription',
{
defaultMessage: 'Installed pre-packaged rules from elastic',
}
);

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { useEffect, useState, useRef } from 'react';

import { useStateToaster, displaySuccessToast } from '../../../components/toasters';
import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
import { getPrePackagedRulesStatus, createPrepackagedRules } from './api';
import * as i18n from './translations';

type Func = () => void;
export type CreatePreBuiltRules = () => Promise<boolean>;
interface Return {
createPrePackagedRules: null | CreatePreBuiltRules;
loading: boolean;
loadingCreatePrePackagedRules: boolean;
refetchPrePackagedRulesStatus: Func | null;
rulesInstalled: number | null;
rulesNotInstalled: number | null;
rulesNotUpdated: number | null;
}

interface UsePrePackagedRuleProps {
canUserCRUD: boolean | null;
hasIndexManage: boolean | null;
hasManageApiKey: boolean | null;
isAuthenticated: boolean | null;
isSignalIndexExists: boolean | null;
}

/**
* Hook for using to get status about pre-packaged Rules from the Detection Engine API
*
* @param hasIndexManage boolean
* @param hasManageApiKey boolean
* @param isAuthenticated boolean
* @param isSignalIndexExists boolean
*
*/
export const usePrePackagedRules = ({
canUserCRUD,
hasIndexManage,
hasManageApiKey,
isAuthenticated,
isSignalIndexExists,
}: UsePrePackagedRuleProps): Return => {
const [rulesInstalled, setRulesInstalled] = useState<number | null>(null);
const [rulesNotInstalled, setRulesNotInstalled] = useState<number | null>(null);
const [rulesNotUpdated, setRulesNotUpdated] = useState<number | null>(null);
const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false);
const [loading, setLoading] = useState(true);
const createPrePackagedRules = useRef<null | CreatePreBuiltRules>(null);
const refetchPrePackagedRules = useRef<Func | null>(null);
const [, dispatchToaster] = useStateToaster();

useEffect(() => {
let isSubscribed = true;
const abortCtrl = new AbortController();

const fetchPrePackagedRules = async () => {
try {
setLoading(true);
const prePackagedRuleStatusResponse = await getPrePackagedRulesStatus({
signal: abortCtrl.signal,
});

if (isSubscribed) {
setRulesInstalled(prePackagedRuleStatusResponse.rules_installed);
setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed);
setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated);
}
} catch (error) {
if (isSubscribed) {
setRulesInstalled(null);
setRulesNotInstalled(null);
setRulesNotUpdated(null);
errorToToaster({ title: i18n.RULE_FETCH_FAILURE, error, dispatchToaster });
}
}
if (isSubscribed) {
setLoading(false);
}
};

const createElasticRules = async (): Promise<boolean> => {
return new Promise(async resolve => {
try {
if (
canUserCRUD &&
hasIndexManage &&
hasManageApiKey &&
isAuthenticated &&
isSignalIndexExists
) {
setLoadingCreatePrePackagedRules(true);
await createPrepackagedRules({
signal: abortCtrl.signal,
});

if (isSubscribed) {
let iterationTryOfFetchingPrePackagedCount = 0;
let timeoutId = -1;
const stopTimeOut = () => {
if (timeoutId !== -1) {
window.clearTimeout(timeoutId);
}
};
const reFetch = () =>
window.setTimeout(async () => {
iterationTryOfFetchingPrePackagedCount =
iterationTryOfFetchingPrePackagedCount + 1;
const prePackagedRuleStatusResponse = await getPrePackagedRulesStatus({
signal: abortCtrl.signal,
});
if (
isSubscribed &&
((prePackagedRuleStatusResponse.rules_not_installed === 0 &&
prePackagedRuleStatusResponse.rules_not_updated === 0) ||
iterationTryOfFetchingPrePackagedCount > 100)
) {
setLoadingCreatePrePackagedRules(false);
setRulesInstalled(prePackagedRuleStatusResponse.rules_installed);
setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed);
setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated);
displaySuccessToast(i18n.RULE_PREPACKAGED_SUCCESS, dispatchToaster);
stopTimeOut();
resolve(true);
} else {
timeoutId = reFetch();
}
}, 300);
timeoutId = reFetch();
}
}
} catch (error) {
if (isSubscribed) {
setLoadingCreatePrePackagedRules(false);
errorToToaster({ title: i18n.RULE_PREPACKAGED_FAILURE, error, dispatchToaster });
resolve(false);
}
}
});
};

fetchPrePackagedRules();
createPrePackagedRules.current = createElasticRules;
refetchPrePackagedRules.current = fetchPrePackagedRules;
return () => {
isSubscribed = false;
abortCtrl.abort();
};
}, [canUserCRUD, hasIndexManage, hasManageApiKey, isAuthenticated, isSignalIndexExists]);

return {
loading,
loadingCreatePrePackagedRules,
refetchPrePackagedRulesStatus: refetchPrePackagedRules.current,
rulesInstalled,
rulesNotInstalled,
rulesNotUpdated,
createPrePackagedRules: createPrePackagedRules.current,
};
};
Loading