Skip to content

Commit

Permalink
[Security Solution] Reintroduce ML Jobs warning popover on rule upgra…
Browse files Browse the repository at this point in the history
…de (#159868)

## Summary

Reintroduces ML Jobs warning popover that was removed during [new
install/upgrade initial
implementation.](#158450)

ML Jobs warning popover that appears when user has legacy ML jobs
installed, and the user attempts to update their prebuilt rules.

Modified behaviour so that the popover now appears in any of the three
cases: upgrading **all rules**, upgrading **specific rules** and
upgrading **a single rule**.

### Testing

In the `state` returned by `UpgradePrebuiltRulesTableContextProvider`,
replace the value of `legacyJobsInstalled` for a mock value. See below:
```ts
    return {
      state: {
        rules,
        tags,
        isFetched,
        isLoading: isLoading && loadingJobs,
        isRefetching,
        selectedRules,
        loadingRules,
        lastUpdated: dataUpdatedAt,
        legacyJobsInstalled: [
          {
            id: 'rc-rare-process-windows-5',
            description:
              'Looks for rare and anomalous processes on a Windows host. Requires process execution events from Sysmon.',
            groups: ['host'],
            processed_record_count: 8577,
            memory_status: 'ok',
            jobState: 'closed',
            hasDatafeed: true,
            datafeedId: 'datafeed-rc-rare-process-windows-5',
            datafeedIndices: ['winlogbeat-*'],
            datafeedState: 'stopped',
            latestTimestampMs: 1561402325194,
            earliestTimestampMs: 1554327458406,
            isSingleMetricViewerJob: true,
            awaitingNodeAssignment: false,
            jobTags: {},
            bucketSpanSeconds: 900,
          },
          {
            id: 'siem-api-rare_process_linux_ecs',
            description: 'SIEM Auditbeat: Detect unusually rare processes on Linux (beta)',
            groups: ['siem'],
            processed_record_count: 582251,
            memory_status: 'hard_limit',
            jobState: 'closed',
            hasDatafeed: true,
            datafeedId: 'datafeed-siem-api-rare_process_linux_ecs',
            datafeedIndices: ['auditbeat-*'],
            datafeedState: 'stopped',
            latestTimestampMs: 1557434782207,
            earliestTimestampMs: 1557353420495,
            isSingleMetricViewerJob: true,
            awaitingNodeAssignment: false,
            jobTags: {},
            bucketSpanSeconds: 900,
          },
        ],
        isUpgradeModalVisible,
        ruleIdToUpgrade,
        modalConfirmationUpdateMethod,
      },
      actions,
    };
```

### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)




### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
jpdjere authored Jun 20, 2023
1 parent a1f55b8 commit 21dbbd0
Showing 1 changed file with 45 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
* 2.0.
*/

// import { isEqual } from 'lodash';
import type { Dispatch, SetStateAction } from 'react';
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
import { useInstalledSecurityJobs } from '../../../../../common/components/ml/hooks/use_installed_security_jobs';
import { useBoolState } from '../../../../../common/hooks/use_bool_state';
import { affectedJobIds } from '../../../../../detections/components/callouts/ml_job_compatibility_callout/affected_job_ids';
import type { RuleUpgradeInfoForReview } from '../../../../../../common/detection_engine/prebuilt_rules/api/review_rule_upgrade/response_schema';
import type { RuleSignatureId } from '../../../../../../common/detection_engine/rule_schema';
import { invariant } from '../../../../../../common/utils/invariant';
Expand All @@ -18,6 +20,9 @@ import {
import { usePrebuiltRulesUpgradeReview } from '../../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_upgrade_review';
import type { UpgradePrebuiltRulesTableFilterOptions } from './use_filter_prebuilt_rules_to_upgrade';
import { useFilterPrebuiltRulesToUpgrade } from './use_filter_prebuilt_rules_to_upgrade';
import { useAsyncConfirmation } from '../rules_table/use_async_confirmation';

import { MlJobUpgradeModal } from '../../../../../detections/components/modals/ml_job_upgrade_modal';

export interface UpgradePrebuiltRulesTableState {
/**
Expand Down Expand Up @@ -90,7 +95,6 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
}: UpgradePrebuiltRulesTableContextProviderProps) => {
const [loadingRules, setLoadingRules] = useState<RuleSignatureId[]>([]);
const [selectedRules, setSelectedRules] = useState<RuleUpgradeInfoForReview[]>([]);

const [filterOptions, setFilterOptions] = useState<UpgradePrebuiltRulesTableFilterOptions>({
filter: '',
tags: [],
Expand All @@ -114,13 +118,29 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
const { mutateAsync: upgradeAllRulesRequest } = usePerformUpgradeAllRules();
const { mutateAsync: upgradeSpecificRulesRequest } = usePerformUpgradeSpecificRules();

// Wrapper to add confirmation modal for users who may be running older ML Jobs that would
// be overridden by updating their rules. For details, see: https://github.com/elastic/kibana/issues/128121
const [isUpgradeModalVisible, showUpgradeModal, hideUpgradeModal] = useBoolState(false);
const { loading: loadingJobs, jobs } = useInstalledSecurityJobs();
const legacyJobsInstalled = jobs.filter((job) => affectedJobIds.includes(job.id));

const [confirmUpgrade, handleUpgradeConfirm, handleUpgradeCancel] = useAsyncConfirmation({
onInit: showUpgradeModal,
onFinish: hideUpgradeModal,
});

const shouldConfirmUpgrade = legacyJobsInstalled.length > 0;

const upgradeOneRule = useCallback(
async (ruleId: RuleSignatureId) => {
const rule = rules.find((r) => r.rule_id === ruleId);
invariant(rule, `Rule with id ${ruleId} not found`);

setLoadingRules((prev) => [...prev, ruleId]);
try {
if (shouldConfirmUpgrade && !(await confirmUpgrade())) {
return;
}
await upgradeSpecificRulesRequest([
{
rule_id: ruleId,
Expand All @@ -132,7 +152,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
setLoadingRules((prev) => prev.filter((id) => id !== ruleId));
}
},
[rules, upgradeSpecificRulesRequest]
[confirmUpgrade, rules, shouldConfirmUpgrade, upgradeSpecificRulesRequest]
);

const upgradeSelectedRules = useCallback(async () => {
Expand All @@ -143,23 +163,29 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
}));
setLoadingRules((prev) => [...prev, ...rulesToUpgrade.map((r) => r.rule_id)]);
try {
if (shouldConfirmUpgrade && !(await confirmUpgrade())) {
return;
}
await upgradeSpecificRulesRequest(rulesToUpgrade);
} finally {
setLoadingRules((prev) => prev.filter((id) => !rulesToUpgrade.some((r) => r.rule_id === id)));
setSelectedRules([]);
}
}, [selectedRules, upgradeSpecificRulesRequest]);
}, [confirmUpgrade, selectedRules, shouldConfirmUpgrade, upgradeSpecificRulesRequest]);

const upgradeAllRules = useCallback(async () => {
// Unselect all rules so that the table doesn't show the "bulk actions" bar
setLoadingRules((prev) => [...prev, ...rules.map((r) => r.rule_id)]);
try {
if (shouldConfirmUpgrade && !(await confirmUpgrade())) {
return;
}
await upgradeAllRulesRequest();
} finally {
setLoadingRules([]);
setSelectedRules([]);
}
}, [rules, upgradeAllRulesRequest]);
}, [confirmUpgrade, rules, shouldConfirmUpgrade, upgradeAllRulesRequest]);

const actions = useMemo<UpgradePrebuiltRulesTableActions>(
() => ({
Expand All @@ -170,7 +196,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
setFilterOptions,
selectRules: setSelectedRules,
}),
[refetch, upgradeAllRules, upgradeOneRule, upgradeSelectedRules]
[refetch, upgradeOneRule, upgradeSelectedRules, upgradeAllRules]
);

const filteredRules = useFilterPrebuiltRulesToUpgrade({ filterOptions, rules });
Expand All @@ -183,11 +209,13 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
filterOptions,
tags,
isFetched,
isLoading,
isLoading: isLoading && loadingJobs,
isRefetching,
selectedRules,
loadingRules,
lastUpdated: dataUpdatedAt,
legacyJobsInstalled,
isUpgradeModalVisible,
},
actions,
};
Expand All @@ -198,15 +226,25 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
tags,
isFetched,
isLoading,
loadingJobs,
isRefetching,
selectedRules,
loadingRules,
dataUpdatedAt,
legacyJobsInstalled,
isUpgradeModalVisible,
actions,
]);

return (
<UpgradePrebuiltRulesTableContext.Provider value={providerValue}>
{isUpgradeModalVisible && (
<MlJobUpgradeModal
jobs={legacyJobsInstalled}
onCancel={handleUpgradeCancel}
onConfirm={handleUpgradeConfirm}
/>
)}
{children}
</UpgradePrebuiltRulesTableContext.Provider>
);
Expand Down

0 comments on commit 21dbbd0

Please sign in to comment.