[Security Solution] [Attack discovery] Removes the Attack discovery feature flag (#182975)

## [Security Solution] [Attack discovery] Removes the Attack discovery feature flag

### Summary

This PR removes the feature flag required to enable the _Attack discovery_ feature.

It (always) enables the Attack discovery feature in both self managed and serverless deployments.

### Desk testing

1. REMOVE the `attackDiscoveryEnabled` feature flag if it was previously enabled in `kibana.dev.yml`:

```
# xpack.securitySolution.enableExperimental: ['attackDiscoveryEnabled']
```

2. Restart Kibana

3. Navigate to Security > Attack discovery

**Expected results**

- `Attack discovery` appears in the global navigation, below `Alerts`
- The contents of the `Attack discovery` page are displayed, per the self managed and serverless examples in the screenshots below:

![self_managed](40d9c23c-af67-476b-997b-fca0ac1c5467)

_Above: Attack discovery in self managed_

![attack_discovery_serverless](1dadcf1a-8a46-4bd5-8b78-df1135e5933f)

_Above: Attack discovery in serverless_
This commit is contained in:
Andrew Macri 2024-05-09 03:48:10 -04:00 committed by GitHub
parent 3e614d25e8
commit 39fe68e8fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 13 additions and 54 deletions

View file

@ -15,5 +15,4 @@ export type AssistantFeatures = { [K in keyof typeof defaultAssistantFeatures]:
*/
export const defaultAssistantFeatures = Object.freeze({
assistantModelEvaluation: false,
attackDiscoveryEnabled: false,
});

View file

@ -19,5 +19,4 @@ import { z } from 'zod';
export type GetCapabilitiesResponse = z.infer<typeof GetCapabilitiesResponse>;
export const GetCapabilitiesResponse = z.object({
assistantModelEvaluation: z.boolean(),
attackDiscoveryEnabled: z.boolean(),
});

View file

@ -21,11 +21,8 @@ paths:
properties:
assistantModelEvaluation:
type: boolean
attackDiscoveryEnabled:
type: boolean
required:
- assistantModelEvaluation
- attackDiscoveryEnabled
'400':
description: Generic Error
content:

View file

@ -16,7 +16,6 @@ import { API_VERSIONS } from '@kbn/elastic-assistant-common';
const statusResponse = {
assistantModelEvaluation: true,
assistantStreamingEnabled: false,
attackDiscoveryEnabled: false,
};
const http = {

View file

@ -277,7 +277,7 @@ export const AssistantProvider: React.FC<AssistantProviderProps> = ({
// Fetch assistant capabilities
const { data: capabilities } = useCapabilities({ http, toasts });
const { assistantModelEvaluation: modelEvaluatorEnabled, attackDiscoveryEnabled } =
const { assistantModelEvaluation: modelEvaluatorEnabled } =
capabilities ?? defaultAssistantFeatures;
const value = useMemo(
@ -286,7 +286,6 @@ export const AssistantProvider: React.FC<AssistantProviderProps> = ({
alertsIndexPattern,
assistantAvailability,
assistantTelemetry,
attackDiscoveryEnabled,
augmentMessageCodeBlocks,
allQuickPrompts: localStorageQuickPrompts ?? [],
allSystemPrompts: localStorageSystemPrompts ?? [],
@ -326,7 +325,6 @@ export const AssistantProvider: React.FC<AssistantProviderProps> = ({
alertsIndexPattern,
assistantAvailability,
assistantTelemetry,
attackDiscoveryEnabled,
augmentMessageCodeBlocks,
localStorageQuickPrompts,
localStorageSystemPrompts,

View file

@ -16,7 +16,7 @@ import { ActionsClientLlm } from '@kbn/elastic-assistant-common/impl/language_mo
import { AnonymizationFieldResponse } from '@kbn/elastic-assistant-common/impl/schemas/anonymization_fields/bulk_crud_anonymization_fields_route.gen';
import { v4 as uuidv4 } from 'uuid';
import { AssistantToolParams, ElasticAssistantApiRequestHandlerContext } from '../../types';
import { AssistantToolParams } from '../../types';
export const REQUIRED_FOR_ATTACK_DISCOVERY: AnonymizationFieldResponse[] = [
{
@ -68,15 +68,3 @@ export const getAssistantToolParams = ({
request,
size,
});
export const isAttackDiscoveryFeatureEnabled = ({
assistantContext,
pluginName,
}: {
assistantContext: ElasticAssistantApiRequestHandlerContext;
pluginName: string;
}): boolean => {
const registeredFeatures = assistantContext.getRegisteredFeatures(pluginName);
return registeredFeatures.attackDiscoveryEnabled === true;
};

View file

@ -17,7 +17,7 @@ import {
import { transformError } from '@kbn/securitysolution-es-utils';
import { ATTACK_DISCOVERY } from '../../../common/constants';
import { getAssistantToolParams, isAttackDiscoveryFeatureEnabled } from './helpers';
import { getAssistantToolParams } from './helpers';
import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from '../helpers';
import { getLangSmithTracer } from '../evaluate/utils';
import { buildResponse } from '../../lib/build_response';
@ -63,16 +63,6 @@ export const postAttackDiscoveryRoute = (
logger,
});
// feature flag check:
const attackDiscoveryFeatureEnabled = isAttackDiscoveryFeatureEnabled({
assistantContext,
pluginName,
});
if (!attackDiscoveryFeatureEnabled) {
return response.notFound();
}
// get parameters from the request body
const alertsIndexPattern = decodeURIComponent(request.body.alertsIndexPattern);
const connectorId = decodeURIComponent(request.body.connectorId);

View file

@ -46,7 +46,6 @@ describe('Post Evaluate Route', () => {
it('returns a 404 if evaluate feature is not registered', async () => {
context.elasticAssistant.getRegisteredFeatures.mockReturnValueOnce({
assistantModelEvaluation: false,
attackDiscoveryEnabled: false,
});
const response = await server.inject(

View file

@ -54,7 +54,6 @@ describe('AppContextService', () => {
appContextService.start(mockAppContext);
appContextService.registerFeatures('super', {
assistantModelEvaluation: true,
attackDiscoveryEnabled: false,
});
appContextService.stop();
@ -104,7 +103,6 @@ describe('AppContextService', () => {
const pluginName = 'pluginName';
const features: AssistantFeatures = {
assistantModelEvaluation: true,
attackDiscoveryEnabled: false,
};
appContextService.start(mockAppContext);
@ -119,12 +117,10 @@ describe('AppContextService', () => {
const pluginOne = 'plugin1';
const featuresOne: AssistantFeatures = {
assistantModelEvaluation: true,
attackDiscoveryEnabled: false,
};
const pluginTwo = 'plugin2';
const featuresTwo: AssistantFeatures = {
assistantModelEvaluation: false,
attackDiscoveryEnabled: false,
};
appContextService.start(mockAppContext);
@ -139,11 +135,9 @@ describe('AppContextService', () => {
const pluginName = 'pluginName';
const featuresOne: AssistantFeatures = {
assistantModelEvaluation: true,
attackDiscoveryEnabled: false,
};
const featuresTwo: AssistantFeatures = {
assistantModelEvaluation: false,
attackDiscoveryEnabled: false,
};
appContextService.start(mockAppContext);

View file

@ -133,11 +133,6 @@ export const allowedExperimentalValues = Object.freeze({
*/
alertsPageFiltersEnabled: true,
/**
* Enables the Attack discovery feature and API endpoint
*/
attackDiscoveryEnabled: false,
/**
* Enables the Assistant Model Evaluation advanced setting and API endpoint, introduced in `8.11.0`.
*/

View file

@ -24,6 +24,7 @@ export const CATEGORIES: Array<SeparatorLinkCategory<SolutionPageName>> = [
linkIds: [
SecurityPageName.rulesLanding,
SecurityPageName.alerts,
SecurityPageName.attackDiscovery,
SecurityPageName.cloudSecurityPostureFindings,
SecurityPageName.case,
],

View file

@ -11,9 +11,9 @@ import { routes } from './routes';
export class AttackDiscovery {
public setup() {}
public start(isEnabled = false): SecuritySubPlugin {
public start(): SecuritySubPlugin {
return {
routes: isEnabled ? routes : [],
routes,
};
}
}

View file

@ -13,7 +13,6 @@ import type { LinkItem } from '../common/links/types';
export const links: LinkItem = {
capabilities: [`${SERVER_APP_ID}.show`],
experimentalKey: 'attackDiscoveryEnabled',
globalNavPosition: 4,
globalSearchKeywords: [
i18n.translate('xpack.securitySolution.appLinks.attackDiscovery', {

View file

@ -50,7 +50,6 @@ jest.mock(
jest.mock('../../common/links', () => ({
useLinkInfo: jest.fn().mockReturnValue({
capabilities: ['siem.show'],
experimentalKey: 'attackDiscoveryEnabled',
globalNavPosition: 4,
globalSearchKeywords: ['Attack discovery'],
id: 'attack_discovery',

View file

@ -97,7 +97,12 @@ const AttackDiscoveryTourComp = () => {
const isElementAtCurrentStepMounted = useIsElementMounted(attackDiscoveryTourStepOne?.anchor);
const isTestAutomation =
window.Cypress != null || // TODO: temporary workaround to disable the tour when running in Cypress, because the tour breaks other projects Cypress tests
navigator.webdriver === true; // TODO: temporary workaround to disable the tour when running in the FTR, because the tour breaks other projects FTR tests
if (
isTestAutomation ||
!tourState.isTourActive ||
(tourState.currentTourStep === 1 && !isElementAtCurrentStepMounted)
) {

View file

@ -318,9 +318,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
const subPlugins = await this.createSubPlugins();
return {
alerts: subPlugins.alerts.start(storage),
attackDiscovery: subPlugins.attackDiscovery.start(
this.experimentalFeatures.attackDiscoveryEnabled
),
attackDiscovery: subPlugins.attackDiscovery.start(),
cases: subPlugins.cases.start(),
cloudDefend: subPlugins.cloudDefend.start(),
cloudSecurityPosture: subPlugins.cloudSecurityPosture.start(),

View file

@ -567,7 +567,6 @@ export class Plugin implements ISecuritySolutionPlugin {
plugins.elasticAssistant.registerTools(APP_UI_ID, getAssistantTools());
plugins.elasticAssistant.registerFeatures(APP_UI_ID, {
assistantModelEvaluation: config.experimentalFeatures.assistantModelEvaluation,
attackDiscoveryEnabled: config.experimentalFeatures.attackDiscoveryEnabled,
});
plugins.elasticAssistant.registerFeatures('management', {
assistantModelEvaluation: config.experimentalFeatures.assistantModelEvaluation,

View file

@ -87,7 +87,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('Applications provider', function () {
it('can search for root-level applications', async () => {
const results = await findResultsWithApi('discover');
expect(results.length).to.be(1);
expect(results.length).to.be(2);
expect(results[0].title).to.be('Discover');
});