From 78c2a33243690975b048da2aebaf925a93705972 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 05:51:56 -0400
Subject: [PATCH 01/36] [Security Solution][Endpoint] Fix unhandled promise
rejections in skipped tests (#115354) (#115399)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Fix errors and comment code in middleware (pending to fix this)
* Fix endpoint list middleware test
* Fix policy TA layout test
* Fix test returning missing promise
Co-authored-by: David Sánchez
---
.../search_exceptions.test.tsx | 1 -
.../management/pages/endpoint_hosts/mocks.ts | 39 ++++++-------
.../endpoint_hosts/store/middleware.test.ts | 4 +-
.../policy_trusted_apps_layout.test.tsx | 56 +++++++++++++------
4 files changed, 59 insertions(+), 41 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
index d7db249475df7..084978d35d03a 100644
--- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
@@ -20,7 +20,6 @@ jest.mock('../../../common/components/user_privileges/use_endpoint_privileges');
let onSearchMock: jest.Mock;
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
-// unhandled promise rejection: https://github.com/elastic/kibana/issues/112699
describe('Search exceptions', () => {
let appTestContext: AppContextTestRender;
let renderResult: ReturnType;
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
index e0b5837c2f78a..c724773593f53 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts
@@ -122,30 +122,27 @@ export const endpointActivityLogHttpMock =
const responseData = fleetActionGenerator.generateResponse({
agent_id: endpointMetadata.agent.id,
});
-
return {
- body: {
- page: 1,
- pageSize: 50,
- startDate: 'now-1d',
- endDate: 'now',
- data: [
- {
- type: 'response',
- item: {
- id: '',
- data: responseData,
- },
+ page: 1,
+ pageSize: 50,
+ startDate: 'now-1d',
+ endDate: 'now',
+ data: [
+ {
+ type: 'response',
+ item: {
+ id: '',
+ data: responseData,
},
- {
- type: 'action',
- item: {
- id: '',
- data: actionData,
- },
+ },
+ {
+ type: 'action',
+ item: {
+ id: '',
+ data: actionData,
},
- ],
- },
+ },
+ ],
};
},
},
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts
index 43fa4e104067f..81c4dc6f2f7de 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts
@@ -61,8 +61,7 @@ jest.mock('../../../../common/lib/kibana');
type EndpointListStore = Store, Immutable>;
-// unhandled promise rejection: https://github.com/elastic/kibana/issues/112699
-describe.skip('endpoint list middleware', () => {
+describe('endpoint list middleware', () => {
const getKibanaServicesMock = KibanaServices.get as jest.Mock;
let fakeCoreStart: jest.Mocked;
let depsStart: DepsStartMock;
@@ -390,7 +389,6 @@ describe.skip('endpoint list middleware', () => {
it('should call get Activity Log API with correct paging options', async () => {
dispatchUserChangedUrl();
-
const updatePagingDispatched = waitForAction('endpointDetailsActivityLogUpdatePaging');
dispatchGetActivityLogPaging({ page: 3 });
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
index e519d19d60fdc..43e19c00bcc8e 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
@@ -19,20 +19,14 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../..
import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing';
import { EndpointDocGenerator } from '../../../../../../../common/endpoint/generate_data';
import { policyListApiPathHandlers } from '../../../store/test_mock_utils';
-import { licenseService } from '../../../../../../common/hooks/use_license';
+import {
+ EndpointPrivileges,
+ useEndpointPrivileges,
+} from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
jest.mock('../../../../trusted_apps/service');
-jest.mock('../../../../../../common/hooks/use_license', () => {
- const licenseServiceInstance = {
- isPlatinumPlus: jest.fn(),
- };
- return {
- licenseService: licenseServiceInstance,
- useLicense: () => {
- return licenseServiceInstance;
- },
- };
-});
+jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges');
+const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
let mockedContext: AppContextTestRender;
let waitForAction: MiddlewareActionSpyHelper['waitForAction'];
@@ -42,8 +36,17 @@ let coreStart: AppContextTestRender['coreStart'];
let http: typeof coreStart.http;
const generator = new EndpointDocGenerator();
-// unhandled promise rejection: https://github.com/elastic/kibana/issues/112699
-describe.skip('Policy trusted apps layout', () => {
+describe('Policy trusted apps layout', () => {
+ const loadedUserEndpointPrivilegesState = (
+ endpointOverrides: Partial = {}
+ ): EndpointPrivileges => ({
+ loading: false,
+ canAccessFleet: true,
+ canAccessEndpointManagement: true,
+ isPlatinumPlus: true,
+ ...endpointOverrides,
+ });
+
beforeEach(() => {
mockedContext = createAppRootMockRenderer();
http = mockedContext.coreStart.http;
@@ -59,6 +62,14 @@ describe.skip('Policy trusted apps layout', () => {
});
}
+ // GET Agent status for agent policy
+ if (path === '/api/fleet/agent-status') {
+ return Promise.resolve({
+ results: { events: 0, total: 5, online: 3, error: 1, offline: 1 },
+ success: true,
+ });
+ }
+
// Get package data
// Used in tests that route back to the list
if (policyListApiHandlers[path]) {
@@ -78,6 +89,10 @@ describe.skip('Policy trusted apps layout', () => {
render = () => mockedContext.render();
});
+ afterAll(() => {
+ mockUseEndpointPrivileges.mockReset();
+ });
+
afterEach(() => reactTestingLibrary.cleanup());
it('should renders layout with no existing TA data', async () => {
@@ -121,7 +136,11 @@ describe.skip('Policy trusted apps layout', () => {
});
it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => {
- (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
+ mockUseEndpointPrivileges.mockReturnValue(
+ loadedUserEndpointPrivilegesState({
+ isPlatinumPlus: false,
+ })
+ );
const component = render();
mockedContext.history.push(getPolicyDetailsArtifactsListPath('1234'));
@@ -133,8 +152,13 @@ describe.skip('Policy trusted apps layout', () => {
});
expect(component.queryByTestId('assign-ta-button')).toBeNull();
});
+
it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => {
- (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
+ mockUseEndpointPrivileges.mockReturnValue(
+ loadedUserEndpointPrivilegesState({
+ isPlatinumPlus: false,
+ })
+ );
TrustedAppsHttpServiceMock.mockImplementation(() => {
return {
getTrustedAppsList: () => getMockListResponse(),
From 2d953bc83c34cb427030815bf976cc7d1660f594 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 06:12:26 -0400
Subject: [PATCH 02/36] Fix alerts Count table title overflow wraps prematurely
(#115364) (#115510)
Co-authored-by: Pablo Machado
---
.../components/alerts_kpis/alerts_count_panel/index.tsx | 2 +-
.../components/alerts_kpis/alerts_histogram_panel/index.tsx | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
index 29324d186784e..c8d45ca67068a 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
@@ -94,7 +94,7 @@ export const AlertsCountPanel = memo(
{i18n.COUNT_TABLE_TITLE}}
titleSize="s"
hideSubtitle
>
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
index 0613c619d89b9..07fa81f27684c 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
@@ -257,7 +257,11 @@ export const AlertsHistogramPanel = memo(
}, [showLinkToAlerts, goToDetectionEngine, formatUrl]);
const titleText = useMemo(
- () => (onlyField == null ? title : i18n.TOP(onlyField)),
+ () => (
+
+ {onlyField == null ? title : i18n.TOP(onlyField)}
+
+ ),
[onlyField, title]
);
From 65786cf25123381e5124c76687f7b0a15600b68e Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 06:17:37 -0400
Subject: [PATCH 03/36] [Security Solution][Endpoint] Change Trusted Apps to
use `item_id` as its identifier and Enable Trusted Apps filtering by id in
the UI (#115276) (#115509)
* Add `item_id` to list of searchable fields
* trusted apps api changes to use `item_id` instead of SO `id`
* Change Policy Details Trusted App "View all details" action URL to show TA list filtered by the TA id
Co-authored-by: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
---
.../list/policy_trusted_apps_list.test.tsx | 2 +-
.../list/policy_trusted_apps_list.tsx | 2 +-
.../pages/trusted_apps/constants.ts | 1 +
.../routes/trusted_apps/handlers.test.ts | 5 +-
.../endpoint/routes/trusted_apps/mapping.ts | 2 +-
.../routes/trusted_apps/service.test.ts | 24 +++++-
.../endpoint/routes/trusted_apps/service.ts | 77 +++++++++++++------
7 files changed, 84 insertions(+), 29 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
index a8d3cc1505463..b5bfc16db2899 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
@@ -207,7 +207,7 @@ describe('when rendering the PolicyTrustedAppsList', () => {
expect(appTestContext.coreStart.application.navigateToApp).toHaveBeenCalledWith(
APP_ID,
expect.objectContaining({
- path: '/administration/trusted_apps?show=edit&id=89f72d8a-05b5-4350-8cad-0dc3661d6e67',
+ path: '/administration/trusted_apps?filter=89f72d8a-05b5-4350-8cad-0dc3661d6e67',
})
);
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
index f6afd9d502486..7b1f8753831c8 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
@@ -113,7 +113,7 @@ export const PolicyTrustedAppsList = memo(
for (const trustedApp of trustedAppItems) {
const isGlobal = trustedApp.effectScope.type === 'global';
- const viewUrlPath = getTrustedAppsListPath({ id: trustedApp.id, show: 'edit' });
+ const viewUrlPath = getTrustedAppsListPath({ filter: trustedApp.id });
const assignedPoliciesMenuItems: ArtifactEntryCollapsibleCardProps['policies'] =
trustedApp.effectScope.type === 'global'
? undefined
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts
index 0602ae18c1408..beefb8587d787 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts
@@ -8,6 +8,7 @@
export const SEARCHABLE_FIELDS: Readonly = [
`name`,
`description`,
+ 'item_id',
`entries.value`,
`entries.entries.value`,
];
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts
index 547c1f6a2e5ff..614ad4fb548ea 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts
@@ -110,7 +110,7 @@ const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' }
const packagePolicyClient =
createPackagePolicyServiceMock() as jest.Mocked;
-describe('handlers', () => {
+describe('TrustedApps API Handlers', () => {
beforeEach(() => {
packagePolicyClient.getByIDs.mockReset();
});
@@ -195,6 +195,7 @@ describe('handlers', () => {
const mockResponse = httpServerMock.createResponseFactory();
exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null);
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
await deleteTrustedAppHandler(
createHandlerContextMock(),
@@ -582,7 +583,7 @@ describe('handlers', () => {
});
it('should return 404 if trusted app does not exist', async () => {
- exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
await updateHandler(
createHandlerContextMock(),
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts
index 2c085c14db009..08c1a3a809d4a 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts
@@ -122,7 +122,7 @@ export const exceptionListItemToTrustedApp = (
const grouped = entriesToConditionEntriesMap(exceptionListItem.entries);
return {
- id: exceptionListItem.id,
+ id: exceptionListItem.item_id,
version: exceptionListItem._version || '',
name: exceptionListItem.name,
description: exceptionListItem.description,
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts
index dce84df735929..c57416ff1c974 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts
@@ -85,9 +85,10 @@ const TRUSTED_APP: TrustedApp = {
],
};
-describe('service', () => {
+describe('TrustedApps service', () => {
beforeEach(() => {
exceptionsListClient.deleteExceptionListItem.mockReset();
+ exceptionsListClient.getExceptionListItem.mockReset();
exceptionsListClient.createExceptionListItem.mockReset();
exceptionsListClient.findExceptionListItem.mockReset();
exceptionsListClient.createTrustedAppsList.mockReset();
@@ -96,6 +97,7 @@ describe('service', () => {
describe('deleteTrustedApp', () => {
it('should delete existing trusted app', async () => {
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);
exceptionsListClient.deleteExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);
expect(await deleteTrustedApp(exceptionsListClient, { id: '123' })).toBeUndefined();
@@ -107,6 +109,7 @@ describe('service', () => {
});
it('should throw for non existing trusted app', async () => {
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null);
await expect(deleteTrustedApp(exceptionsListClient, { id: '123' })).rejects.toBeInstanceOf(
@@ -393,7 +396,7 @@ describe('service', () => {
});
it('should throw a Not Found error if trusted app is not found prior to making update', async () => {
- exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
await expect(
updateTrustedApp(
exceptionsListClient,
@@ -489,5 +492,22 @@ describe('service', () => {
TrustedAppNotFoundError
);
});
+
+ it('should try to find trusted app by `itemId` and then by `id`', async () => {
+ exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
+ await getTrustedApp(exceptionsListClient, '123').catch(() => Promise.resolve());
+
+ expect(exceptionsListClient.getExceptionListItem).toHaveBeenCalledTimes(2);
+ expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(1, {
+ itemId: '123',
+ id: undefined,
+ namespaceType: 'agnostic',
+ });
+ expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(2, {
+ itemId: undefined,
+ id: '123',
+ namespaceType: 'agnostic',
+ });
+ });
});
});
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts
index 856a615c1ffa2..7a4b2372ece8f 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts
@@ -15,13 +15,13 @@ import {
DeleteTrustedAppsRequestParams,
GetOneTrustedAppResponse,
GetTrustedAppsListRequest,
- GetTrustedAppsSummaryResponse,
GetTrustedAppsListResponse,
+ GetTrustedAppsSummaryRequest,
+ GetTrustedAppsSummaryResponse,
PostTrustedAppCreateRequest,
PostTrustedAppCreateResponse,
PutTrustedAppUpdateRequest,
PutTrustedAppUpdateResponse,
- GetTrustedAppsSummaryRequest,
TrustedApp,
} from '../../../../common/endpoint/types';
@@ -33,8 +33,8 @@ import {
} from './mapping';
import {
TrustedAppNotFoundError,
- TrustedAppVersionConflictError,
TrustedAppPolicyNotExistsError,
+ TrustedAppVersionConflictError,
} from './errors';
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
import { PackagePolicy } from '../../../../../fleet/common';
@@ -87,30 +87,61 @@ const isUserTryingToModifyEffectScopeWithoutPermissions = (
}
};
-export const deleteTrustedApp = async (
+/**
+ * Attempts to first fine the ExceptionItem using `item_id` and if not found, then a second attempt wil be done
+ * against the Saved Object `id`.
+ * @param exceptionsListClient
+ * @param id
+ */
+export const findTrustedAppExceptionItemByIdOrItemId = async (
exceptionsListClient: ExceptionListClient,
- { id }: DeleteTrustedAppsRequestParams
-) => {
- const exceptionListItem = await exceptionsListClient.deleteExceptionListItem({
- id,
+ id: string
+): Promise => {
+ const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({
+ itemId: id,
+ id: undefined,
+ namespaceType: 'agnostic',
+ });
+
+ if (trustedAppExceptionItem) {
+ return trustedAppExceptionItem;
+ }
+
+ return exceptionsListClient.getExceptionListItem({
itemId: undefined,
+ id,
namespaceType: 'agnostic',
});
+};
- if (!exceptionListItem) {
+export const deleteTrustedApp = async (
+ exceptionsListClient: ExceptionListClient,
+ { id }: DeleteTrustedAppsRequestParams
+): Promise => {
+ const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
+ exceptionsListClient,
+ id
+ );
+
+ if (!trustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
}
+
+ await exceptionsListClient.deleteExceptionListItem({
+ id: trustedAppExceptionItem.id,
+ itemId: undefined,
+ namespaceType: 'agnostic',
+ });
};
export const getTrustedApp = async (
exceptionsListClient: ExceptionListClient,
id: string
): Promise => {
- const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({
- itemId: '',
- id,
- namespaceType: 'agnostic',
- });
+ const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
+ exceptionsListClient,
+ id
+ );
if (!trustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
@@ -189,19 +220,18 @@ export const updateTrustedApp = async (
updatedTrustedApp: PutTrustedAppUpdateRequest,
isAtLeastPlatinum: boolean
): Promise => {
- const currentTrustedApp = await exceptionsListClient.getExceptionListItem({
- itemId: '',
- id,
- namespaceType: 'agnostic',
- });
+ const currentTrustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
+ exceptionsListClient,
+ id
+ );
- if (!currentTrustedApp) {
+ if (!currentTrustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
}
if (
isUserTryingToModifyEffectScopeWithoutPermissions(
- exceptionListItemToTrustedApp(currentTrustedApp),
+ exceptionListItemToTrustedApp(currentTrustedAppExceptionItem),
updatedTrustedApp,
isAtLeastPlatinum
)
@@ -226,7 +256,10 @@ export const updateTrustedApp = async (
try {
updatedTrustedAppExceptionItem = await exceptionsListClient.updateExceptionListItem(
- updatedTrustedAppToUpdateExceptionListItemOptions(currentTrustedApp, updatedTrustedApp)
+ updatedTrustedAppToUpdateExceptionListItemOptions(
+ currentTrustedAppExceptionItem,
+ updatedTrustedApp
+ )
);
} catch (e) {
if (e?.output?.statusCode === 409) {
From 8fec45c45972495ff9f8e95b555509501c2eeadc Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 06:47:44 -0400
Subject: [PATCH 04/36] [Discover] Enable description for saved search modal
(#114257) (#115514)
* [Discover] enable description for saved search
* [Discover] remove i18n translations for removed description
* [Discover] apply Tim's suggestion
* [Discover] update snapshot
* [Discover] reorder top nav buttons in tests
* [Description] fix description save action
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com>
---
.../components/top_nav/discover_topnav.test.tsx | 2 +-
.../components/top_nav/get_top_nav_links.test.ts | 16 +++++++++-------
.../main/components/top_nav/get_top_nav_links.ts | 4 +++-
.../main/components/top_nav/on_save_search.tsx | 10 +++++-----
.../embeddable/saved_search_embeddable.tsx | 4 ++++
.../plugins/translations/translations/ja-JP.json | 1 -
.../plugins/translations/translations/zh-CN.json | 1 -
7 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx
index 4b572f6e348b8..808346b53304c 100644
--- a/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx
+++ b/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx
@@ -42,7 +42,7 @@ describe('Discover topnav component', () => {
const props = getProps(true);
const component = shallowWithIntl();
const topMenuConfig = component.props().config.map((obj: TopNavMenuData) => obj.id);
- expect(topMenuConfig).toEqual(['options', 'new', 'save', 'open', 'share', 'inspect']);
+ expect(topMenuConfig).toEqual(['options', 'new', 'open', 'share', 'inspect', 'save']);
});
test('generated config of TopNavMenu config is correct when no discover save permissions are assigned', () => {
diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts
index d31ac6e0f2fea..20c5b9bae332d 100644
--- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts
+++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts
@@ -53,13 +53,6 @@ test('getTopNavLinks result', () => {
"run": [Function],
"testId": "discoverNewButton",
},
- Object {
- "description": "Save Search",
- "id": "save",
- "label": "Save",
- "run": [Function],
- "testId": "discoverSaveButton",
- },
Object {
"description": "Open Saved Search",
"id": "open",
@@ -81,6 +74,15 @@ test('getTopNavLinks result', () => {
"run": [Function],
"testId": "openInspectorButton",
},
+ Object {
+ "description": "Save Search",
+ "emphasize": true,
+ "iconType": "save",
+ "id": "save",
+ "label": "Save",
+ "run": [Function],
+ "testId": "discoverSaveButton",
+ },
]
`);
});
diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts
index 81be662470306..44d2999947f41 100644
--- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts
+++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts
@@ -76,6 +76,8 @@ export const getTopNavLinks = ({
defaultMessage: 'Save Search',
}),
testId: 'discoverSaveButton',
+ iconType: 'save',
+ emphasize: true,
run: () => onSaveSearch({ savedSearch, services, indexPattern, navigateTo, state }),
};
@@ -153,9 +155,9 @@ export const getTopNavLinks = ({
return [
...(services.capabilities.advancedSettings.save ? [options] : []),
newSearch,
- ...(services.capabilities.discover.save ? [saveSearch] : []),
openSearch,
shareSearch,
inspectSearch,
+ ...(services.capabilities.discover.save ? [saveSearch] : []),
];
};
diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx
index 18766b5df7f33..25b04e12c650a 100644
--- a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx
+++ b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx
@@ -98,16 +98,19 @@ export async function onSaveSearch({
const onSave = async ({
newTitle,
newCopyOnSave,
+ newDescription,
isTitleDuplicateConfirmed,
onTitleDuplicate,
}: {
newTitle: string;
newCopyOnSave: boolean;
+ newDescription: string;
isTitleDuplicateConfirmed: boolean;
onTitleDuplicate: () => void;
}) => {
const currentTitle = savedSearch.title;
savedSearch.title = newTitle;
+ savedSearch.description = newDescription;
const saveOptions: SaveSavedSearchOptions = {
onTitleDuplicate,
copyOnSave: newCopyOnSave,
@@ -136,14 +139,11 @@ export async function onSaveSearch({
onClose={() => {}}
title={savedSearch.title ?? ''}
showCopyOnSave={!!savedSearch.id}
+ description={savedSearch.description}
objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', {
defaultMessage: 'search',
})}
- description={i18n.translate('discover.localMenu.saveSaveSearchDescription', {
- defaultMessage:
- 'Save your Discover search so you can use it in visualizations and dashboards',
- })}
- showDescription={false}
+ showDescription={true}
/>
);
showSaveModal(saveModal, services.core.i18n.Context);
diff --git a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx
index 8849806cf5959..89c47559d7b4c 100644
--- a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx
+++ b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx
@@ -402,6 +402,10 @@ export class SavedSearchEmbeddable
return this.inspectorAdapters;
}
+ public getDescription() {
+ return this.savedSearch.description;
+ }
+
public destroy() {
super.destroy();
if (this.searchProps) {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 113eae5d08e07..f909a03909e3f 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -2534,7 +2534,6 @@
"discover.localMenu.openSavedSearchDescription": "保存された検索を開きます",
"discover.localMenu.openTitle": "開く",
"discover.localMenu.optionsDescription": "オプション",
- "discover.localMenu.saveSaveSearchDescription": "ビジュアライゼーションとダッシュボードで使用できるように Discover の検索を保存します",
"discover.localMenu.saveSaveSearchObjectType": "検索",
"discover.localMenu.saveSearchDescription": "検索を保存します",
"discover.localMenu.saveTitle": "保存",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 15933599699eb..4de407cf8e464 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -2560,7 +2560,6 @@
"discover.localMenu.openSavedSearchDescription": "打开已保存搜索",
"discover.localMenu.openTitle": "打开",
"discover.localMenu.optionsDescription": "选项",
- "discover.localMenu.saveSaveSearchDescription": "保存您的 Discover 搜索,以便可以在可视化和仪表板中使用该搜索",
"discover.localMenu.saveSaveSearchObjectType": "搜索",
"discover.localMenu.saveSearchDescription": "保存搜索",
"discover.localMenu.saveTitle": "保存",
From 44e0e53877b16723cf792e2ebea0e345c6de0231 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 07:10:02 -0400
Subject: [PATCH 05/36] [Security Solution][Rules] Halt Indicator Match
execution after interval has passed (#115288) (#115517)
* Throw an error to stop execution if IM rule has exceeded its interval
* Extract and unit test our timeout validation
* Add integration test around timeout behavior
Configures a very slow rule to trigger a timeout and assert the
corresponding failure.
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Ryland Herrick
---
.../threat_mapping/create_threat_signals.ts | 6 ++-
.../signals/threat_mapping/utils.test.ts | 23 ++++++++++
.../signals/threat_mapping/utils.ts | 21 +++++++++
.../tests/create_threat_matching.ts | 46 +++++++++++++++++++
4 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
index 169a820392a6e..677a2028acdf7 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
@@ -11,7 +11,7 @@ import { getThreatList, getThreatListCount } from './get_threat_list';
import { CreateThreatSignalsOptions } from './types';
import { createThreatSignal } from './create_threat_signal';
import { SearchAfterAndBulkCreateReturnType } from '../types';
-import { combineConcurrentResults } from './utils';
+import { buildExecutionIntervalValidator, combineConcurrentResults } from './utils';
import { buildThreatEnrichment } from './build_threat_enrichment';
export const createThreatSignals = async ({
@@ -46,6 +46,9 @@ export const createThreatSignals = async ({
const params = ruleSO.attributes.params;
logger.debug(buildRuleMessage('Indicator matching rule starting'));
const perPage = concurrentSearches * itemsPerSearch;
+ const verifyExecutionCanProceed = buildExecutionIntervalValidator(
+ ruleSO.attributes.schedule.interval
+ );
let results: SearchAfterAndBulkCreateReturnType = {
success: true,
@@ -99,6 +102,7 @@ export const createThreatSignals = async ({
});
while (threatList.hits.hits.length !== 0) {
+ verifyExecutionCanProceed();
const chunks = chunk(itemsPerSearch, threatList.hits.hits);
logger.debug(buildRuleMessage(`${chunks.length} concurrent indicator searches are starting.`));
const concurrentSearchesPerformed = chunks.map>(
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
index ec826b44023f6..f029b02127b08 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
@@ -10,6 +10,7 @@ import { sampleSignalHit } from '../__mocks__/es_results';
import { ThreatMatchNamedQuery } from './types';
import {
+ buildExecutionIntervalValidator,
calculateAdditiveMax,
calculateMax,
calculateMaxLookBack,
@@ -712,4 +713,26 @@ describe('utils', () => {
});
});
});
+
+ describe('buildExecutionIntervalValidator', () => {
+ it('succeeds if the validator is called within the specified interval', () => {
+ const validator = buildExecutionIntervalValidator('1m');
+ expect(() => validator()).not.toThrowError();
+ });
+
+ it('throws an error if the validator is called after the specified interval', async () => {
+ const validator = buildExecutionIntervalValidator('1s');
+
+ await new Promise((r) => setTimeout(r, 1001));
+ expect(() => validator()).toThrowError(
+ 'Current rule execution has exceeded its allotted interval (1s) and has been stopped.'
+ );
+ });
+
+ it('throws an error if the interval cannot be parsed', () => {
+ expect(() => buildExecutionIntervalValidator('badString')).toThrowError(
+ 'Unable to parse rule interval (badString); stopping rule execution since allotted duration is undefined'
+ );
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
index 4d9fda43f032e..99f6609faec91 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
@@ -5,7 +5,10 @@
* 2.0.
*/
+import moment from 'moment';
+
import { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../types';
+import { parseInterval } from '../utils';
import { ThreatMatchNamedQuery } from './types';
/**
@@ -146,3 +149,21 @@ export const decodeThreatMatchNamedQuery = (encoded: string): ThreatMatchNamedQu
export const extractNamedQueries = (hit: SignalSourceHit): ThreatMatchNamedQuery[] =>
hit.matched_queries?.map((match) => decodeThreatMatchNamedQuery(match)) ?? [];
+
+export const buildExecutionIntervalValidator: (interval: string) => () => void = (interval) => {
+ const intervalDuration = parseInterval(interval);
+
+ if (intervalDuration == null) {
+ throw new Error(
+ `Unable to parse rule interval (${interval}); stopping rule execution since allotted duration is undefined.`
+ );
+ }
+
+ const executionEnd = moment().add(intervalDuration);
+ return () => {
+ if (moment().isAfter(executionEnd)) {
+ const message = `Current rule execution has exceeded its allotted interval (${interval}) and has been stopped.`;
+ throw new Error(message);
+ }
+ };
+};
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts
index 0aad3c699805a..223529fce54f6 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts
@@ -411,6 +411,52 @@ export default ({ getService }: FtrProviderContext) => {
expect(signalsOpen.hits.hits.length).equal(0);
});
+ describe('timeout behavior', () => {
+ it('will return an error if a rule execution exceeds the rule interval', async () => {
+ const rule: CreateRulesSchema = {
+ description: 'Detecting root and admin users',
+ name: 'Query with a short interval',
+ severity: 'high',
+ index: ['auditbeat-*'],
+ type: 'threat_match',
+ risk_score: 55,
+ language: 'kuery',
+ rule_id: 'rule-1',
+ from: '1900-01-01T00:00:00.000Z',
+ query: '*:*',
+ threat_query: '*:*', // broad query to take more time
+ threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
+ threat_mapping: [
+ {
+ entries: [
+ {
+ field: 'host.name',
+ value: 'host.name',
+ type: 'mapping',
+ },
+ ],
+ },
+ ],
+ threat_filters: [],
+ concurrent_searches: 1,
+ interval: '1s', // short interval
+ items_per_search: 1, // iterate only 1 threat item per loop to ensure we're slow
+ };
+
+ const { id } = await createRule(supertest, rule);
+ await waitForRuleSuccessOrStatus(supertest, id, 'failed');
+
+ const { body } = await supertest
+ .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`)
+ .set('kbn-xsrf', 'true')
+ .send({ ids: [id] })
+ .expect(200);
+ expect(body[id].current_status.last_failure_message).to.contain(
+ 'execution has exceeded its allotted interval'
+ );
+ });
+ });
+
describe('indicator enrichment', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/filebeat/threat_intel');
From 0c86129e4a21838983b23bdbc778d2f20d2751c0 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 07:37:34 -0400
Subject: [PATCH 06/36] [Security Solution][Detections] Hide building block
rules in "Security/Overview" (#105611) (#115521)
* Hide building block rules in "Security/Overview"
* Add Cypress tests for alerts generated by building block rules
Co-authored-by: Dmitry Shevchenko
Co-authored-by: Georgii Gorbachev
Co-authored-by: Dmitry Shevchenko
---
.../building_block_alerts.spec.ts | 40 +++++++++++++++++++
.../security_solution/cypress/objects/rule.ts | 20 ++++++++++
.../cypress/screens/overview.ts | 2 +
.../cypress/tasks/api_calls/rules.ts | 1 +
.../components/signals_by_category/index.tsx | 15 +++++--
.../use_filters_for_signals_by_category.ts | 37 +++++++++++++++++
6 files changed, 111 insertions(+), 4 deletions(-)
create mode 100644 x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts
create mode 100644 x-pack/plugins/security_solution/public/overview/components/signals_by_category/use_filters_for_signals_by_category.ts
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts
new file mode 100644
index 0000000000000..262ffe8163e57
--- /dev/null
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { getBuildingBlockRule } from '../../objects/rule';
+import { OVERVIEW_ALERTS_HISTOGRAM } from '../../screens/overview';
+import { OVERVIEW } from '../../screens/security_header';
+import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
+import { createCustomRuleActivated } from '../../tasks/api_calls/rules';
+import { cleanKibana } from '../../tasks/common';
+import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule';
+import { loginAndWaitForPage } from '../../tasks/login';
+import { navigateFromHeaderTo } from '../../tasks/security_header';
+import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
+
+const EXPECTED_NUMBER_OF_ALERTS = 16;
+
+describe('Alerts generated by building block rules', () => {
+ beforeEach(() => {
+ cleanKibana();
+ });
+
+ it('Alerts should be visible on the Rule Detail page and not visible on the Overview page', () => {
+ createCustomRuleActivated(getBuildingBlockRule());
+ loginAndWaitForPage(DETECTIONS_RULE_MANAGEMENT_URL);
+ goToRuleDetails();
+ waitForTheRuleToBeExecuted();
+
+ // Check that generated events are visible on the Details page
+ waitForAlertsToPopulate(EXPECTED_NUMBER_OF_ALERTS);
+
+ navigateFromHeaderTo(OVERVIEW);
+
+ // Check that generated events are hidden on the Overview page
+ cy.get(OVERVIEW_ALERTS_HISTOGRAM).should('contain.text', 'No data to display');
+ });
+});
diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts
index 4b061865d632b..27973854097db 100644
--- a/x-pack/plugins/security_solution/cypress/objects/rule.ts
+++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts
@@ -58,6 +58,7 @@ export interface CustomRule {
lookBack: Interval;
timeline: CompleteTimeline;
maxSignals: number;
+ buildingBlockType?: string;
}
export interface ThresholdRule extends CustomRule {
@@ -188,6 +189,25 @@ export const getNewRule = (): CustomRule => ({
maxSignals: 100,
});
+export const getBuildingBlockRule = (): CustomRule => ({
+ customQuery: 'host.name: *',
+ index: getIndexPatterns(),
+ name: 'Building Block Rule Test',
+ description: 'The new rule description.',
+ severity: 'High',
+ riskScore: '17',
+ tags: ['test', 'newRule'],
+ referenceUrls: ['http://example.com/', 'https://example.com/'],
+ falsePositivesExamples: ['False1', 'False2'],
+ mitre: [getMitre1(), getMitre2()],
+ note: '# test markdown',
+ runsEvery: getRunsEvery(),
+ lookBack: getLookBack(),
+ timeline: getTimeline(),
+ maxSignals: 100,
+ buildingBlockType: 'default',
+});
+
export const getUnmappedRule = (): CustomRule => ({
customQuery: '*:*',
index: ['unmapped*'],
diff --git a/x-pack/plugins/security_solution/cypress/screens/overview.ts b/x-pack/plugins/security_solution/cypress/screens/overview.ts
index 1376a39e5ee79..1945b7e3ce3e7 100644
--- a/x-pack/plugins/security_solution/cypress/screens/overview.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/overview.ts
@@ -166,3 +166,5 @@ export const OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON =
export const OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT = `${OVERVIEW_RISKY_HOSTS_LINKS} [data-test-subj="header-panel-subtitle"]`;
export const OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON =
'[data-test-subj="risky-hosts-enable-module-button"]';
+
+export const OVERVIEW_ALERTS_HISTOGRAM = '[data-test-subj="alerts-histogram-panel"]';
diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts
index 04ff0fcabc081..fd2838e5b3caa 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts
@@ -114,6 +114,7 @@ export const createCustomRuleActivated = (
enabled: true,
tags: ['rule1'],
max_signals: maxSignals,
+ building_block_type: rule.buildingBlockType,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
diff --git a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx
index 321e6d00b5301..cbeb1464e1b41 100644
--- a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx
@@ -7,19 +7,24 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
+import { Filter, Query } from '@kbn/es-query';
import { AlertsHistogramPanel } from '../../../detections/components/alerts_kpis/alerts_histogram_panel';
import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index';
import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions';
-import { Filter, Query } from '../../../../../../../src/plugins/data/public';
+
import { InputsModelId } from '../../../common/store/inputs/constants';
-import * as i18n from '../../pages/translations';
import { UpdateDateRange } from '../../../common/components/charts/common';
+
import { AlertsStackByField } from '../../../detections/components/alerts_kpis/common/types';
+import * as i18n from '../../pages/translations';
+
+import { useFiltersForSignalsByCategory } from './use_filters_for_signals_by_category';
+
interface Props {
combinedQueries?: string;
- filters?: Filter[];
+ filters: Filter[];
headerChildren?: React.ReactNode;
/** Override all defaults, and only display this field */
onlyField?: AlertsStackByField;
@@ -43,6 +48,8 @@ const SignalsByCategoryComponent: React.FC = ({
}) => {
const dispatch = useDispatch();
const { signalIndexName } = useSignalIndex();
+ const filtersForSignalsByCategory = useFiltersForSignalsByCategory(filters);
+
const updateDateRangeCallback = useCallback(
({ x }) => {
if (!x) {
@@ -63,7 +70,7 @@ const SignalsByCategoryComponent: React.FC = ({
return (
{
+ // TODO: Once we are past experimental phase this code should be removed
+ const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
+
+ const resultingFilters = useMemo(
+ () => [
+ ...baseFilters,
+ ...(ruleRegistryEnabled
+ ? buildShowBuildingBlockFilterRuleRegistry(SHOW_BUILDING_BLOCK_ALERTS) // TODO: Once we are past experimental phase this code should be removed
+ : buildShowBuildingBlockFilter(SHOW_BUILDING_BLOCK_ALERTS)),
+ ],
+ [baseFilters, ruleRegistryEnabled]
+ );
+
+ return resultingFilters;
+};
From f54724dc2b9f4597b137054864db3e63e6402144 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 07:37:47 -0400
Subject: [PATCH 07/36] [Unified Integrations] Clean up empty states, tutorial
links and routing to prefer unified integrations (#114911) (#115493)
Cleans up the integrations view and redirects all links to the integration manager.
Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com>
---
.../chrome/ui/header/collapsible_nav.tsx | 2 +-
.../__snapshots__/add_data.test.tsx.snap | 16 +-
.../components/add_data/add_data.test.tsx | 4 +-
.../components/add_data/add_data.tsx | 152 +++++++++---------
.../components/sample_data/index.tsx | 4 +-
.../components/tutorial_directory.js | 56 +------
.../public/application/components/welcome.tsx | 3 +-
src/plugins/home/public/index.ts | 1 -
src/plugins/home/public/services/index.ts | 1 -
.../home/public/services/tutorials/index.ts | 1 -
.../tutorials/tutorial_service.mock.ts | 1 -
.../tutorials/tutorial_service.test.tsx | 32 ----
.../services/tutorials/tutorial_service.ts | 18 ---
.../empty_index_list_prompt.tsx | 2 +-
.../__snapshots__/overview.test.tsx.snap | 110 +++----------
.../public/components/overview/overview.tsx | 12 +-
.../public/assets/elastic_beats_card_dark.svg | 1 -
.../assets/elastic_beats_card_light.svg | 1 -
.../__snapshots__/no_data_page.test.tsx.snap | 4 +-
.../elastic_agent_card.test.tsx.snap | 55 ++++++-
.../elastic_beats_card.test.tsx.snap | 70 --------
.../no_data_card/elastic_agent_card.test.tsx | 10 +-
.../no_data_card/elastic_agent_card.tsx | 44 ++++-
.../no_data_card/elastic_beats_card.test.tsx | 45 ------
.../no_data_card/elastic_beats_card.tsx | 66 --------
.../no_data_page/no_data_card/index.ts | 1 -
.../no_data_page/no_data_page.tsx | 14 +-
.../components/app/RumDashboard/RumHome.tsx | 8 +-
.../routing/templates/no_data_config.ts | 10 +-
.../epm/components/package_list_grid.tsx | 2 +-
.../components/home_integration/index.tsx | 8 -
.../tutorial_directory_header_link.tsx | 16 +-
.../tutorial_directory_notice.tsx | 147 -----------------
x-pack/plugins/fleet/public/plugin.ts | 7 +-
.../infra/public/pages/logs/page_content.tsx | 2 +-
.../infra/public/pages/logs/page_template.tsx | 6 +-
.../logs/stream/page_no_indices_content.tsx | 4 +-
.../infra/public/pages/metrics/index.tsx | 4 +-
.../metric_detail/components/invalid_node.tsx | 4 +-
.../public/pages/metrics/page_template.tsx | 9 +-
.../components/app/header/header_menu.tsx | 2 +-
.../public/utils/no_data_config.ts | 7 +-
.../security_solution/common/constants.ts | 2 +-
.../components/overview_empty/index.test.tsx | 12 +-
.../components/overview_empty/index.tsx | 50 ++----
.../translations/translations/ja-JP.json | 11 --
.../translations/translations/zh-CN.json | 11 --
x-pack/test/accessibility/apps/home.ts | 27 ----
48 files changed, 292 insertions(+), 783 deletions(-)
delete mode 100644 src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg
delete mode 100644 src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg
delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap
delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx
delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx
delete mode 100644 x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_notice.tsx
diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx
index ad590865b9e14..ccc0e17b655b1 100644
--- a/src/core/public/chrome/ui/header/collapsible_nav.tsx
+++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx
@@ -362,7 +362,7 @@ export function CollapsibleNav({
iconType="plusInCircleFilled"
>
{i18n.translate('core.ui.primaryNav.addData', {
- defaultMessage: 'Add data',
+ defaultMessage: 'Add integrations',
})}
diff --git a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap
index 26b5697f008b6..de6beab31247a 100644
--- a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap
+++ b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap
@@ -17,7 +17,7 @@ exports[`AddData render 1`] = `
id="homDataAdd__title"
>
@@ -43,17 +43,25 @@ exports[`AddData render 1`] = `
grow={false}
>
diff --git a/src/plugins/home/public/application/components/add_data/add_data.test.tsx b/src/plugins/home/public/application/components/add_data/add_data.test.tsx
index 4018ae67c19ee..3aa51f89c7d67 100644
--- a/src/plugins/home/public/application/components/add_data/add_data.test.tsx
+++ b/src/plugins/home/public/application/components/add_data/add_data.test.tsx
@@ -27,7 +27,9 @@ beforeEach(() => {
jest.clearAllMocks();
});
-const applicationStartMock = {} as unknown as ApplicationStart;
+const applicationStartMock = {
+ capabilities: { navLinks: { integrations: true } },
+} as unknown as ApplicationStart;
const addBasePathMock = jest.fn((path: string) => (path ? path : 'path'));
diff --git a/src/plugins/home/public/application/components/add_data/add_data.tsx b/src/plugins/home/public/application/components/add_data/add_data.tsx
index 97ba28a04a07e..50d6079dd8df3 100644
--- a/src/plugins/home/public/application/components/add_data/add_data.tsx
+++ b/src/plugins/home/public/application/components/add_data/add_data.tsx
@@ -22,8 +22,6 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { METRIC_TYPE } from '@kbn/analytics';
import { ApplicationStart } from 'kibana/public';
import { createAppNavigationHandler } from '../app_navigation_handler';
-// @ts-expect-error untyped component
-import { Synopsis } from '../synopsis';
import { getServices } from '../../kibana_services';
import { RedirectAppLinks } from '../../../../../kibana_react/public';
@@ -35,87 +33,91 @@ interface Props {
export const AddData: FC = ({ addBasePath, application, isDarkMode }) => {
const { trackUiMetric } = getServices();
+ const canAccessIntegrations = application.capabilities.navLinks.integrations;
+ if (canAccessIntegrations) {
+ return (
+ <>
+
+
+
+
+
+
+
+
- return (
- <>
-
-
-
-
-
-
-
-
+
-
+
+
+
+
+
-
-
-
-
-
+
-
+
+
+
+ {/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
+ {
+ trackUiMetric(METRIC_TYPE.CLICK, 'home_tutorial_directory');
+ createAppNavigationHandler('/app/integrations/browse')(event);
+ }}
+ >
+
+
+
+
-
-
-
- {/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
- {
- trackUiMetric(METRIC_TYPE.CLICK, 'home_tutorial_directory');
- createAppNavigationHandler('/app/home#/tutorial_directory')(event);
- }}
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
- >
- );
+
+ >
+ );
+ } else {
+ return null;
+ }
};
diff --git a/src/plugins/home/public/application/components/sample_data/index.tsx b/src/plugins/home/public/application/components/sample_data/index.tsx
index d6b9328f57e9b..b65fbb5d002b0 100644
--- a/src/plugins/home/public/application/components/sample_data/index.tsx
+++ b/src/plugins/home/public/application/components/sample_data/index.tsx
@@ -40,7 +40,7 @@ export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) {
image={cardGraphicURL}
textAlign="left"
title={
-
+
}
description={
-
+
{
- const notices = getServices().tutorialService.getDirectoryNotices();
- return notices.length ? (
-
- {notices.map((DirectoryNotice, index) => (
-
-
-
- ))}
-
- ) : null;
- };
-
renderHeaderLinks = () => {
const headerLinks = getServices().tutorialService.getDirectoryHeaderLinks();
return headerLinks.length ? (
@@ -245,7 +203,6 @@ class TutorialDirectoryUi extends React.Component {
render() {
const headerLinks = this.renderHeaderLinks();
const tabs = this.getTabs();
- const notices = this.renderNotices();
return (
+
),
tabs,
rightSideItems: headerLinks ? [headerLinks] : [],
}}
>
- {notices && (
- <>
- {notices}
-
- >
- )}
{this.renderTabContent()}
);
diff --git a/src/plugins/home/public/application/components/welcome.tsx b/src/plugins/home/public/application/components/welcome.tsx
index ca7e6874c75c2..03dff22c7b33f 100644
--- a/src/plugins/home/public/application/components/welcome.tsx
+++ b/src/plugins/home/public/application/components/welcome.tsx
@@ -48,8 +48,7 @@ export class Welcome extends React.Component {
};
private redirecToAddData() {
- const path = this.services.addBasePath('#/tutorial_directory');
- window.location.href = path;
+ this.services.application.navigateToApp('integrations', { path: '/browse' });
}
private onSampleDataDecline = () => {
diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts
index dd02bf65dd8b0..7abaf5d19f008 100644
--- a/src/plugins/home/public/index.ts
+++ b/src/plugins/home/public/index.ts
@@ -23,7 +23,6 @@ export type {
FeatureCatalogueSolution,
Environment,
TutorialVariables,
- TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './services';
diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts
index 65913df6310b1..2ee68a9eef0c2 100644
--- a/src/plugins/home/public/services/index.ts
+++ b/src/plugins/home/public/services/index.ts
@@ -22,7 +22,6 @@ export { TutorialService } from './tutorials';
export type {
TutorialVariables,
TutorialServiceSetup,
- TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './tutorials';
diff --git a/src/plugins/home/public/services/tutorials/index.ts b/src/plugins/home/public/services/tutorials/index.ts
index 8de12c31249d8..e007a5ea4d552 100644
--- a/src/plugins/home/public/services/tutorials/index.ts
+++ b/src/plugins/home/public/services/tutorials/index.ts
@@ -11,7 +11,6 @@ export { TutorialService } from './tutorial_service';
export type {
TutorialVariables,
TutorialServiceSetup,
- TutorialDirectoryNoticeComponent,
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './tutorial_service';
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
index 0c109d61912ca..ab38a32a1a5b3 100644
--- a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
@@ -25,7 +25,6 @@ const createMock = (): jest.Mocked> => {
const service = {
setup: jest.fn(),
getVariables: jest.fn(() => ({})),
- getDirectoryNotices: jest.fn(() => []),
getDirectoryHeaderLinks: jest.fn(() => []),
getModuleNotices: jest.fn(() => []),
getCustomStatusCheck: jest.fn(),
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx
index a88cf526e3716..b90165aafb45f 100644
--- a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx
@@ -27,22 +27,6 @@ describe('TutorialService', () => {
}).toThrow();
});
- test('allows multiple register directory notice calls', () => {
- const setup = new TutorialService().setup();
- expect(() => {
- setup.registerDirectoryNotice('abc', () => );
- setup.registerDirectoryNotice('def', () => );
- }).not.toThrow();
- });
-
- test('throws when same directory notice is registered twice', () => {
- const setup = new TutorialService().setup();
- expect(() => {
- setup.registerDirectoryNotice('abc', () => );
- setup.registerDirectoryNotice('abc', () => );
- }).toThrow();
- });
-
test('allows multiple register directory header link calls', () => {
const setup = new TutorialService().setup();
expect(() => {
@@ -91,22 +75,6 @@ describe('TutorialService', () => {
});
});
- describe('getDirectoryNotices', () => {
- test('returns empty array', () => {
- const service = new TutorialService();
- expect(service.getDirectoryNotices()).toEqual([]);
- });
-
- test('returns last state of register calls', () => {
- const service = new TutorialService();
- const setup = service.setup();
- const notices = [() => , () => ];
- setup.registerDirectoryNotice('abc', notices[0]);
- setup.registerDirectoryNotice('def', notices[1]);
- expect(service.getDirectoryNotices()).toEqual(notices);
- });
- });
-
describe('getDirectoryHeaderLinks', () => {
test('returns empty array', () => {
const service = new TutorialService();
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.ts b/src/plugins/home/public/services/tutorials/tutorial_service.ts
index 839b0702a499e..81b6bbe72e3e9 100644
--- a/src/plugins/home/public/services/tutorials/tutorial_service.ts
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.ts
@@ -11,9 +11,6 @@ import React from 'react';
/** @public */
export type TutorialVariables = Partial>;
-/** @public */
-export type TutorialDirectoryNoticeComponent = React.FC;
-
/** @public */
export type TutorialDirectoryHeaderLinkComponent = React.FC;
@@ -27,7 +24,6 @@ type CustomComponent = () => Promise;
export class TutorialService {
private tutorialVariables: TutorialVariables = {};
- private tutorialDirectoryNotices: { [key: string]: TutorialDirectoryNoticeComponent } = {};
private tutorialDirectoryHeaderLinks: {
[key: string]: TutorialDirectoryHeaderLinkComponent;
} = {};
@@ -47,16 +43,6 @@ export class TutorialService {
this.tutorialVariables[key] = value;
},
- /**
- * Registers a component that will be rendered at the top of tutorial directory page.
- */
- registerDirectoryNotice: (id: string, component: TutorialDirectoryNoticeComponent) => {
- if (this.tutorialDirectoryNotices[id]) {
- throw new Error(`directory notice ${id} already set`);
- }
- this.tutorialDirectoryNotices[id] = component;
- },
-
/**
* Registers a component that will be rendered next to tutorial directory title/header area.
*/
@@ -94,10 +80,6 @@ export class TutorialService {
return this.tutorialVariables;
}
- public getDirectoryNotices() {
- return Object.values(this.tutorialDirectoryNotices);
- }
-
public getDirectoryHeaderLinks() {
return Object.values(this.tutorialDirectoryHeaderLinks);
}
diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx
index 1331eb9b7c4ac..d00f9e2368e21 100644
--- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx
+++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx
@@ -91,7 +91,7 @@ export const EmptyIndexListPrompt = ({
{
- navigateToApp('home', { path: '#/tutorial_directory' });
+ navigateToApp('home', { path: '/app/integrations/browse' });
closeFlyout();
}}
icon={}
diff --git a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
index 6da2f95fa394d..babcab15a4974 100644
--- a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
+++ b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
@@ -226,10 +226,7 @@ exports[`Overview render 1`] = `
[MockFunction] {
"calls": Array [
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -259,11 +256,7 @@ exports[`Overview render 1`] = `
"results": Array [
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -533,10 +526,7 @@ exports[`Overview without features 1`] = `
[MockFunction] {
"calls": Array [
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -563,16 +553,10 @@ exports[`Overview without features 1`] = `
"/plugins/kibanaReact/assets/solutions_solution_4.svg",
],
Array [
- "/app/home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
- "home#/tutorial_directory",
- ],
- Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -602,11 +586,7 @@ exports[`Overview without features 1`] = `
"results": Array [
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -642,19 +622,11 @@ exports[`Overview without features 1`] = `
},
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "/app/home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -801,10 +773,7 @@ exports[`Overview without solutions 1`] = `
[MockFunction] {
"calls": Array [
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -831,20 +800,13 @@ exports[`Overview without solutions 1`] = `
"/plugins/kibanaReact/assets/solutions_solution_4.svg",
],
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
],
"results": Array [
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -880,11 +842,7 @@ exports[`Overview without solutions 1`] = `
},
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
],
}
@@ -898,10 +856,7 @@ exports[`Overview without solutions 1`] = `
[MockFunction] {
"calls": Array [
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -928,20 +883,13 @@ exports[`Overview without solutions 1`] = `
"/plugins/kibanaReact/assets/solutions_solution_4.svg",
],
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
],
"results": Array [
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -977,11 +925,7 @@ exports[`Overview without solutions 1`] = `
},
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
],
}
@@ -1001,10 +945,7 @@ exports[`Overview without solutions 1`] = `
[MockFunction] {
"calls": Array [
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
Array [
"kibana_landing_page",
@@ -1031,20 +972,13 @@ exports[`Overview without solutions 1`] = `
"/plugins/kibanaReact/assets/solutions_solution_4.svg",
],
Array [
- "/app/home#/tutorial_directory",
- ],
- Array [
- "home#/tutorial_directory",
+ "/app/integrations/browse",
],
],
"results": Array [
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
Object {
"type": "return",
@@ -1080,11 +1014,7 @@ exports[`Overview without solutions 1`] = `
},
Object {
"type": "return",
- "value": "/app/home#/tutorial_directory",
- },
- Object {
- "type": "return",
- "value": "home#/tutorial_directory",
+ "value": "/app/integrations/browse",
},
],
}
diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx
index 07769e2f3c474..6a0279bd12465 100644
--- a/src/plugins/kibana_overview/public/components/overview/overview.tsx
+++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx
@@ -61,7 +61,7 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
// Home does not have a locator implemented, so hard-code it here.
- const addDataHref = addBasePath('/app/home#/tutorial_directory');
+ const addDataHref = addBasePath('/app/integrations/browse');
const devToolsHref = share.url.locators.get('CONSOLE_APP_LOCATOR')?.useUrl({});
const managementHref = share.url.locators
.get('MANAGEMENT_APP_LOCATOR')
@@ -86,8 +86,14 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
}),
logo: 'logoKibana',
actions: {
- beats: {
- href: addBasePath(`home#/tutorial_directory`),
+ elasticAgent: {
+ title: i18n.translate('kibanaOverview.noDataConfig.title', {
+ defaultMessage: 'Add integrations',
+ }),
+ description: i18n.translate('kibanaOverview.noDataConfig.description', {
+ defaultMessage:
+ 'Use Elastic Agent or Beats to collect data and build out Analytics solutions.',
+ }),
},
},
docsLink: docLinks.links.kibana,
diff --git a/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg b/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg
deleted file mode 100644
index 8652d8d921506..0000000000000
--- a/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg b/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg
deleted file mode 100644
index f54786c1b950c..0000000000000
--- a/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap
index d8bc5745ec8e5..8842a3c9f5842 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap
@@ -73,9 +73,9 @@ exports[`NoDataPage render 1`] = `
-
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap
index 3f72ae5597a98..f66d05140b2e9 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap
@@ -13,7 +13,36 @@ exports[`ElasticAgentCard props button 1`] = `
href="/app/integrations/browse"
image="/plugins/kibanaReact/assets/elastic_agent_card.svg"
paddingSize="l"
- title="Add Elastic Agent"
+ title={
+
+
+ Add Elastic Agent
+
+
+ }
+/>
+`;
+
+exports[`ElasticAgentCard props category 1`] = `
+
+ Add Elastic Agent
+
+ }
+ href="/app/integrations/browse/custom"
+ image="/plugins/kibanaReact/assets/elastic_agent_card.svg"
+ paddingSize="l"
+ title={
+
+
+ Add Elastic Agent
+
+
+ }
/>
`;
@@ -30,7 +59,13 @@ exports[`ElasticAgentCard props href 1`] = `
href="#"
image="/plugins/kibanaReact/assets/elastic_agent_card.svg"
paddingSize="l"
- title="Add Elastic Agent"
+ title={
+
+
+ Add Elastic Agent
+
+
+ }
/>
`;
@@ -48,7 +83,13 @@ exports[`ElasticAgentCard props recommended 1`] = `
href="/app/integrations/browse"
image="/plugins/kibanaReact/assets/elastic_agent_card.svg"
paddingSize="l"
- title="Add Elastic Agent"
+ title={
+
+
+ Add Elastic Agent
+
+
+ }
/>
`;
@@ -65,6 +106,12 @@ exports[`ElasticAgentCard renders 1`] = `
href="/app/integrations/browse"
image="/plugins/kibanaReact/assets/elastic_agent_card.svg"
paddingSize="l"
- title="Add Elastic Agent"
+ title={
+
+
+ Add Elastic Agent
+
+
+ }
/>
`;
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap
deleted file mode 100644
index af26f9e93ebac..0000000000000
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap
+++ /dev/null
@@ -1,70 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ElasticBeatsCard props button 1`] = `
-
- Button
-
- }
- href="/app/home#/tutorial_directory"
- image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg"
- paddingSize="l"
- title="Add data"
-/>
-`;
-
-exports[`ElasticBeatsCard props href 1`] = `
-
- Button
-
- }
- href="#"
- image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg"
- paddingSize="l"
- title="Add data"
-/>
-`;
-
-exports[`ElasticBeatsCard props recommended 1`] = `
-
- Add data
-
- }
- href="/app/home#/tutorial_directory"
- image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg"
- paddingSize="l"
- title="Add data"
-/>
-`;
-
-exports[`ElasticBeatsCard renders 1`] = `
-
- Add data
-
- }
- href="/app/home#/tutorial_directory"
- image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg"
- paddingSize="l"
- title="Add data"
-/>
-`;
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx
index 45cc32cae06d6..b971abf06a437 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx
@@ -14,7 +14,10 @@ jest.mock('../../../context', () => ({
...jest.requireActual('../../../context'),
useKibana: jest.fn().mockReturnValue({
services: {
- http: { basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) } },
+ http: {
+ basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) },
+ },
+ application: { capabilities: { navLinks: { integrations: true } } },
uiSettings: { get: jest.fn() },
},
}),
@@ -41,5 +44,10 @@ describe('ElasticAgentCard', () => {
const component = shallow();
expect(component).toMatchSnapshot();
});
+
+ test('category', () => {
+ const component = shallow();
+ expect(component).toMatchSnapshot();
+ });
});
});
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx
index f071bd9fab25a..5a91e568471d1 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx
@@ -9,7 +9,7 @@
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { CoreStart } from 'kibana/public';
-import { EuiButton, EuiCard } from '@elastic/eui';
+import { EuiButton, EuiCard, EuiTextColor, EuiScreenReaderOnly } from '@elastic/eui';
import { useKibana } from '../../../context';
import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page';
@@ -27,13 +27,40 @@ export const ElasticAgentCard: FunctionComponent = ({
href,
button,
layout,
+ category,
...cardRest
}) => {
const {
- services: { http },
+ services: { http, application },
} = useKibana();
const addBasePath = http.basePath.prepend;
- const basePathUrl = '/plugins/kibanaReact/assets/';
+ const image = addBasePath(`/plugins/kibanaReact/assets/elastic_agent_card.svg`);
+ const canAccessFleet = application.capabilities.navLinks.integrations;
+ const hasCategory = category ? `/${category}` : '';
+
+ if (!canAccessFleet) {
+ return (
+
+ {i18n.translate('kibana-react.noDataPage.elasticAgentCard.noPermission.title', {
+ defaultMessage: `Contact your administrator`,
+ })}
+
+ }
+ description={
+
+ {i18n.translate('kibana-react.noDataPage.elasticAgentCard.noPermission.description', {
+ defaultMessage: `This integration is not yet enabled. Your administrator has the required permissions to turn it on.`,
+ })}
+
+ }
+ isDisabled
+ />
+ );
+ }
const defaultCTAtitle = i18n.translate('kibana-react.noDataPage.elasticAgentCard.title', {
defaultMessage: 'Add Elastic Agent',
@@ -51,12 +78,17 @@ export const ElasticAgentCard: FunctionComponent = ({
return (
+ {defaultCTAtitle}
+
+ }
description={i18n.translate('kibana-react.noDataPage.elasticAgentCard.description', {
defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`,
})}
- image={addBasePath(`${basePathUrl}elastic_agent_card.svg`)}
betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined}
footer={footer}
layout={layout as 'vertical' | undefined}
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx
deleted file mode 100644
index 6ea41bf6b3e1f..0000000000000
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import { shallow } from 'enzyme';
-import React from 'react';
-import { ElasticBeatsCard } from './elastic_beats_card';
-
-jest.mock('../../../context', () => ({
- ...jest.requireActual('../../../context'),
- useKibana: jest.fn().mockReturnValue({
- services: {
- http: { basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) } },
- uiSettings: { get: jest.fn() },
- },
- }),
-}));
-
-describe('ElasticBeatsCard', () => {
- test('renders', () => {
- const component = shallow();
- expect(component).toMatchSnapshot();
- });
-
- describe('props', () => {
- test('recommended', () => {
- const component = shallow();
- expect(component).toMatchSnapshot();
- });
-
- test('button', () => {
- const component = shallow();
- expect(component).toMatchSnapshot();
- });
-
- test('href', () => {
- const component = shallow();
- expect(component).toMatchSnapshot();
- });
- });
-});
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx
deleted file mode 100644
index 0372d12096489..0000000000000
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React, { FunctionComponent } from 'react';
-import { i18n } from '@kbn/i18n';
-import { CoreStart } from 'kibana/public';
-import { EuiButton, EuiCard } from '@elastic/eui';
-import { useKibana } from '../../../context';
-import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page';
-
-export type ElasticBeatsCardProps = NoDataPageActions & {
- solution: string;
-};
-
-export const ElasticBeatsCard: FunctionComponent = ({
- recommended,
- title,
- button,
- href,
- solution, // unused for now
- layout,
- ...cardRest
-}) => {
- const {
- services: { http, uiSettings },
- } = useKibana();
- const addBasePath = http.basePath.prepend;
- const basePathUrl = '/plugins/kibanaReact/assets/';
- const IS_DARK_THEME = uiSettings.get('theme:darkMode');
-
- const defaultCTAtitle = i18n.translate('kibana-react.noDataPage.elasticBeatsCard.title', {
- defaultMessage: 'Add data',
- });
-
- const footer =
- typeof button !== 'string' && typeof button !== 'undefined' ? (
- button
- ) : (
- // The href and/or onClick are attached to the whole Card, so the button is just for show.
- // Do not add the behavior here too or else it will propogate through
- {button || title || defaultCTAtitle}
- );
-
- return (
-
- );
-};
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts
index 3744239d9a472..e05d4d9675ca9 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts
@@ -7,5 +7,4 @@
*/
export * from './elastic_agent_card';
-export * from './elastic_beats_card';
export * from './no_data_card';
diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx
index 56eb0f34617d6..b2d9ef6ca5008 100644
--- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx
+++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx
@@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { KibanaPageTemplateProps } from '../page_template';
-import { ElasticAgentCard, ElasticBeatsCard, NoDataCard } from './no_data_card';
+import { ElasticAgentCard, NoDataCard } from './no_data_card';
import { KibanaPageTemplateSolutionNavAvatar } from '../solution_nav';
export const NO_DATA_PAGE_MAX_WIDTH = 950;
@@ -55,6 +55,10 @@ export type NoDataPageActions = Partial & {
* Remapping `onClick` to any element
*/
onClick?: MouseEventHandler;
+ /**
+ * Category to auto-select within Fleet
+ */
+ category?: string;
};
export type NoDataPageActionsProps = Record;
@@ -107,18 +111,12 @@ export const NoDataPage: FunctionComponent = ({
const actionsKeys = Object.keys(sortedData);
const renderActions = useMemo(() => {
return Object.values(sortedData).map((action, i) => {
- if (actionsKeys[i] === 'elasticAgent') {
+ if (actionsKeys[i] === 'elasticAgent' || actionsKeys[i] === 'beats') {
return (
);
- } else if (actionsKeys[i] === 'beats') {
- return (
-
-
-
- );
} else {
return (
),
discussForumLink: (
-
+
import('./tutorial_directory_notice'));
-export const TutorialDirectoryNotice: TutorialDirectoryNoticeComponent = () => (
- }>
-
-
-);
-
const TutorialDirectoryHeaderLinkLazy = React.lazy(
() => import('./tutorial_directory_header_link')
);
diff --git a/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx b/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx
index 074a1c40bdb19..18fdd875c7379 100644
--- a/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx
+++ b/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { memo, useState, useEffect } from 'react';
+import React, { memo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButtonEmpty } from '@elastic/eui';
import type { TutorialDirectoryHeaderLinkComponent } from 'src/plugins/home/public';
@@ -13,25 +13,15 @@ import type { TutorialDirectoryHeaderLinkComponent } from 'src/plugins/home/publ
import { RedirectAppLinks } from '../../../../../../src/plugins/kibana_react/public';
import { useLink, useCapabilities, useStartServices } from '../../hooks';
-import { tutorialDirectoryNoticeState$ } from './tutorial_directory_notice';
-
const TutorialDirectoryHeaderLink: TutorialDirectoryHeaderLinkComponent = memo(() => {
const { getHref } = useLink();
const { application } = useStartServices();
const { show: hasIngestManager } = useCapabilities();
- const [noticeState, setNoticeState] = useState({
+ const [noticeState] = useState({
settingsDataLoaded: false,
- hasSeenNotice: false,
});
- useEffect(() => {
- const subscription = tutorialDirectoryNoticeState$.subscribe((value) => setNoticeState(value));
- return () => {
- subscription.unsubscribe();
- };
- }, []);
-
- return hasIngestManager && noticeState.settingsDataLoaded && noticeState.hasSeenNotice ? (
+ return hasIngestManager && noticeState.settingsDataLoaded ? (
{
- const { getHref } = useLink();
- const { application } = useStartServices();
- const { show: hasIngestManager } = useCapabilities();
- const { data: settingsData, isLoading } = useGetSettings();
- const [dismissedNotice, setDismissedNotice] = useState(false);
-
- const dismissNotice = useCallback(async () => {
- setDismissedNotice(true);
- await sendPutSettings({
- has_seen_add_data_notice: true,
- });
- }, []);
-
- useEffect(() => {
- tutorialDirectoryNoticeState$.next({
- settingsDataLoaded: !isLoading,
- hasSeenNotice: Boolean(dismissedNotice || settingsData?.item?.has_seen_add_data_notice),
- });
- }, [isLoading, settingsData, dismissedNotice]);
-
- const hasSeenNotice =
- isLoading || settingsData?.item?.has_seen_add_data_notice || dismissedNotice;
-
- return hasIngestManager && !hasSeenNotice ? (
- <>
-
-
-
- ),
- }}
- />
- }
- >
-
-
-
-
- ),
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- dismissNotice();
- }}
- >
-
-
-
-
-
-
-
- >
- ) : null;
-});
-
-// Needed for React.lazy
-// eslint-disable-next-line import/no-default-export
-export default TutorialDirectoryNotice;
diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts
index e1f263b0763e8..4a2a6900cc78c 100644
--- a/x-pack/plugins/fleet/public/plugin.ts
+++ b/x-pack/plugins/fleet/public/plugin.ts
@@ -44,11 +44,7 @@ import { CUSTOM_LOGS_INTEGRATION_NAME, INTEGRATIONS_BASE_PATH } from './constant
import { licenseService } from './hooks';
import { setHttpClient } from './hooks/use_request';
import { createPackageSearchProvider } from './search_provider';
-import {
- TutorialDirectoryNotice,
- TutorialDirectoryHeaderLink,
- TutorialModuleNotice,
-} from './components/home_integration';
+import { TutorialDirectoryHeaderLink, TutorialModuleNotice } from './components/home_integration';
import { createExtensionRegistrationCallback } from './services/ui_extensions';
import type { UIExtensionRegistrationCallback, UIExtensionsStorage } from './types';
import { LazyCustomLogsAssetsExtension } from './lazy_custom_logs_assets_extension';
@@ -197,7 +193,6 @@ export class FleetPlugin implements Plugin {
diff --git a/x-pack/plugins/infra/public/pages/logs/page_template.tsx b/x-pack/plugins/infra/public/pages/logs/page_template.tsx
index 7ee60ab84bf25..6de13b495f0ba 100644
--- a/x-pack/plugins/infra/public/pages/logs/page_template.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/page_template.tsx
@@ -44,13 +44,13 @@ export const LogsPageTemplate: React.FC = ({
actions: {
beats: {
title: i18n.translate('xpack.infra.logs.noDataConfig.beatsCard.title', {
- defaultMessage: 'Add logs with Beats',
+ defaultMessage: 'Add a logging integration',
}),
description: i18n.translate('xpack.infra.logs.noDataConfig.beatsCard.description', {
defaultMessage:
- 'Use Beats to send logs to Elasticsearch. We make it easy with modules for many popular systems and apps.',
+ 'Use the Elastic Agent or Beats to send logs to Elasticsearch. We make it easy with integrations for many popular systems and apps.',
}),
- href: basePath + `/app/home#/tutorial_directory/logging`,
+ href: basePath + `/app/integrations/browse`,
},
},
docsLink: docLinks.links.observability.guide,
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
index bc3bc22f3f1b2..2259a8d3528af 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
@@ -22,8 +22,8 @@ export const LogsPageNoIndicesContent = () => {
const canConfigureSource = application?.capabilities?.logs?.configureSource ? true : false;
const tutorialLinkProps = useLinkProps({
- app: 'home',
- hash: '/tutorial_directory/logging',
+ app: 'integrations',
+ hash: '/browse',
});
return (
diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx
index ae375dc504e7a..1a79cd996087d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx
@@ -93,9 +93,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
index 2a436eac30b2c..17e6382ce65cc 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx
@@ -18,8 +18,8 @@ interface InvalidNodeErrorProps {
export const InvalidNodeError: React.FunctionComponent = ({ nodeName }) => {
const tutorialLinkProps = useLinkProps({
- app: 'home',
- hash: '/tutorial_directory/metrics',
+ app: 'integrations',
+ hash: '/browse',
});
return (
diff --git a/x-pack/plugins/infra/public/pages/metrics/page_template.tsx b/x-pack/plugins/infra/public/pages/metrics/page_template.tsx
index 41ea12c280841..4da671283644d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/page_template.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/page_template.tsx
@@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n';
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
import type { LazyObservabilityPageTemplateProps } from '../../../../observability/public';
import { KibanaPageTemplateProps } from '../../../../../../src/plugins/kibana_react/public';
-import { useLinkProps } from '../../hooks/use_link_props';
interface MetricsPageTemplateProps extends LazyObservabilityPageTemplateProps {
hasData?: boolean;
@@ -30,11 +29,6 @@ export const MetricsPageTemplate: React.FC = ({
},
} = useKibanaContextForPlugin();
- const tutorialLinkProps = useLinkProps({
- app: 'home',
- hash: '/tutorial_directory/metrics',
- });
-
const noDataConfig: KibanaPageTemplateProps['noDataConfig'] = hasData
? undefined
: {
@@ -44,13 +38,12 @@ export const MetricsPageTemplate: React.FC = ({
actions: {
beats: {
title: i18n.translate('xpack.infra.metrics.noDataConfig.beatsCard.title', {
- defaultMessage: 'Add metrics with Beats',
+ defaultMessage: 'Add a metrics integration',
}),
description: i18n.translate('xpack.infra.metrics.noDataConfig.beatsCard.description', {
defaultMessage:
'Use Beats to send metrics data to Elasticsearch. We make it easy with modules for many popular systems and apps.',
}),
- ...tutorialLinkProps,
},
},
docsLink: docLinks.links.observability.guide,
diff --git a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx b/x-pack/plugins/observability/public/components/app/header/header_menu.tsx
index 707cb241501fd..0ed01b7d3673e 100644
--- a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx
+++ b/x-pack/plugins/observability/public/components/app/header/header_menu.tsx
@@ -26,7 +26,7 @@ export function ObservabilityHeaderMenu(): React.ReactElement | null {
{addDataLinkText}
diff --git a/x-pack/plugins/observability/public/utils/no_data_config.ts b/x-pack/plugins/observability/public/utils/no_data_config.ts
index 1e16fb145bdce..2c87b1434a0b4 100644
--- a/x-pack/plugins/observability/public/utils/no_data_config.ts
+++ b/x-pack/plugins/observability/public/utils/no_data_config.ts
@@ -24,12 +24,15 @@ export function getNoDataConfig({
defaultMessage: 'Observability',
}),
actions: {
- beats: {
+ elasticAgent: {
+ title: i18n.translate('xpack.observability.noDataConfig.beatsCard.title', {
+ defaultMessage: 'Add integrations',
+ }),
description: i18n.translate('xpack.observability.noDataConfig.beatsCard.description', {
defaultMessage:
'Use Beats and APM agents to send observability data to Elasticsearch. We make it easy with support for many popular systems, apps, and languages.',
}),
- href: basePath.prepend(`/app/home#/tutorial_directory/logging`),
+ href: basePath.prepend(`/app/integrations`),
},
},
docsLink,
diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index 5c41e92661e58..5a7e19e2cdd05 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -16,7 +16,7 @@ export const APP_NAME = 'Security';
export const APP_ICON = 'securityAnalyticsApp';
export const APP_ICON_SOLUTION = 'logoSecurity';
export const APP_PATH = `/app/security`;
-export const ADD_DATA_PATH = `/app/home#/tutorial_directory/security`;
+export const ADD_DATA_PATH = `/app/integrations/browse/security`;
export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern';
export const DEFAULT_DATE_FORMAT = 'dateFormat';
export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz';
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx
index 5fa2725f9ee6f..61e9e66f1bb87 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx
@@ -45,9 +45,10 @@ describe('OverviewEmpty', () => {
expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({
actions: {
elasticAgent: {
+ category: 'security',
description:
- 'Use Elastic Agent to collect security events and protect your endpoints from threats. Manage your agents in Fleet and add integrations with a single click.',
- href: '/app/integrations/browse/security',
+ 'Use Elastic Agent to collect security events and protect your endpoints from threats.',
+ title: 'Add a Security integration',
},
},
docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html',
@@ -68,8 +69,11 @@ describe('OverviewEmpty', () => {
it('render with correct actions ', () => {
expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({
actions: {
- beats: {
- href: '/app/home#/tutorial_directory/security',
+ elasticAgent: {
+ category: 'security',
+ description:
+ 'Use Elastic Agent to collect security events and protect your endpoints from threats.',
+ title: 'Add a Security integration',
},
},
docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html',
diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx
index bc76333943191..9b20c079002e6 100644
--- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx
@@ -5,13 +5,10 @@
* 2.0.
*/
-import React, { useMemo } from 'react';
+import React from 'react';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../../../common/lib/kibana';
-import { ADD_DATA_PATH } from '../../../../common/constants';
-import { pagePathGetters } from '../../../../../fleet/public';
import { SOLUTION_NAME } from '../../../../public/common/translations';
-import { useUserPrivileges } from '../../../common/components/user_privileges';
import {
KibanaPageTemplate,
@@ -19,42 +16,27 @@ import {
} from '../../../../../../../src/plugins/kibana_react/public';
const OverviewEmptyComponent: React.FC = () => {
- const { http, docLinks } = useKibana().services;
- const basePath = http.basePath.get();
- const canAccessFleet = useUserPrivileges().endpointPrivileges.canAccessFleet;
- const integrationsPathComponents = pagePathGetters.integrations_all({ category: 'security' });
-
- const agentAction: NoDataPageActionsProps = useMemo(
- () => ({
- elasticAgent: {
- href: `${basePath}${integrationsPathComponents[0]}${integrationsPathComponents[1]}`,
- description: i18n.translate(
- 'xpack.securitySolution.pages.emptyPage.beatsCard.description',
- {
- defaultMessage:
- 'Use Elastic Agent to collect security events and protect your endpoints from threats. Manage your agents in Fleet and add integrations with a single click.',
- }
- ),
- },
- }),
- [basePath, integrationsPathComponents]
- );
-
- const beatsAction: NoDataPageActionsProps = useMemo(
- () => ({
- beats: {
- href: `${basePath}${ADD_DATA_PATH}`,
- },
- }),
- [basePath]
- );
+ const { docLinks } = useKibana().services;
+
+ const agentAction: NoDataPageActionsProps = {
+ elasticAgent: {
+ category: 'security',
+ title: i18n.translate('xpack.securitySolution.pages.emptyPage.beatsCard.title', {
+ defaultMessage: 'Add a Security integration',
+ }),
+ description: i18n.translate('xpack.securitySolution.pages.emptyPage.beatsCard.description', {
+ defaultMessage:
+ 'Use Elastic Agent to collect security events and protect your endpoints from threats.',
+ }),
+ },
+ };
return (
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index f909a03909e3f..95f6909b12f6c 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -3014,11 +3014,7 @@
"home.tutorial.savedObject.unableToAddErrorMessage": "{savedObjectsLength} 件中 {errorsLength} 件の kibana オブジェクトが追加できません。エラー:{errorMessage}",
"home.tutorial.selectionLegend": "デプロイタイプ",
"home.tutorial.selfManagedButtonLabel": "自己管理",
- "home.tutorial.tabs.allTitle": "すべて",
- "home.tutorial.tabs.loggingTitle": "ログ",
- "home.tutorial.tabs.metricsTitle": "メトリック",
"home.tutorial.tabs.sampleDataTitle": "サンプルデータ",
- "home.tutorial.tabs.securitySolutionTitle": "セキュリティ",
"home.tutorial.unexpectedStatusCheckStateErrorDescription": "予期せぬステータス確認ステータス {statusCheckState}",
"home.tutorial.unhandledInstructionTypeErrorDescription": "予期せぬ指示タイプ {visibleInstructions}",
"home.tutorialDirectory.featureCatalogueDescription": "一般的なアプリやサービスからデータを取り込みます。",
@@ -4209,8 +4205,6 @@
"kibana-react.noDataPage.cantDecide.link": "詳細については、ドキュメントをご確認ください。",
"kibana-react.noDataPage.elasticAgentCard.description": "Elasticエージェントを使用すると、シンプルで統一された方法でコンピューターからデータを収集するできます。",
"kibana-react.noDataPage.elasticAgentCard.title": "Elasticエージェントの追加",
- "kibana-react.noDataPage.elasticBeatsCard.description": "Beatsを使用して、さまざまなシステムのデータをElasticsearchに追加します。",
- "kibana-react.noDataPage.elasticBeatsCard.title": "データの追加",
"kibana-react.noDataPage.intro": "データを追加して開始するか、{solution}については{link}をご覧ください。",
"kibana-react.noDataPage.intro.link": "詳細",
"kibana-react.noDataPage.noDataPage.recommended": "推奨",
@@ -11205,12 +11199,7 @@
"xpack.fleet.fleetServerUpgradeModal.modalTitle": "エージェントをFleetサーバーに登録",
"xpack.fleet.fleetServerUpgradeModal.onPremDescriptionMessage": "Fleetサーバーが使用できます。スケーラビリティとセキュリティが改善されています。{existingAgentsMessage} Fleetを使用し続けるには、Fleetサーバーと新しいバージョンのElasticエージェントを各ホストにインストールする必要があります。詳細については、{link}をご覧ください。",
"xpack.fleet.genericActionsMenuText": "開く",
- "xpack.fleet.homeIntegration.tutorialDirectory.dismissNoticeButtonText": "メッセージを消去",
"xpack.fleet.homeIntegration.tutorialDirectory.fleetAppButtonText": "統合を試す",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeText": "Elasticエージェント統合では、シンプルかつ統合された方法で、ログ、メトリック、他の種類のデータの監視をホストに追加することができます。複数のBeatsをインストールする必要はありません。このため、インフラストラクチャ全体でのポリシーのデプロイが簡単で高速になりました。詳細については、{blogPostLink}をお読みください。",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeText.blogPostLink": "発表ブログ投稿",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle": "{newPrefix} Elasticエージェント統合",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle.newPrefix": "一般公開へ:",
"xpack.fleet.homeIntegration.tutorialModule.noticeText": "{notePrefix}このモジュールの新しいバージョンは{availableAsIntegrationLink}です。統合と新しいElasticエージェントの詳細については、{blogPostLink}をお読みください。",
"xpack.fleet.homeIntegration.tutorialModule.noticeText.blogPostLink": "発表ブログ投稿",
"xpack.fleet.homeIntegration.tutorialModule.noticeText.integrationLink": "Elasticエージェント統合として提供",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 4de407cf8e464..80dad5a2c0c8b 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -3043,11 +3043,7 @@
"home.tutorial.savedObject.unableToAddErrorMessage": "{savedObjectsLength} 个 kibana 对象中有 {errorsLength} 个无法添加,错误:{errorMessage}",
"home.tutorial.selectionLegend": "部署类型",
"home.tutorial.selfManagedButtonLabel": "自管型",
- "home.tutorial.tabs.allTitle": "全部",
- "home.tutorial.tabs.loggingTitle": "日志",
- "home.tutorial.tabs.metricsTitle": "指标",
"home.tutorial.tabs.sampleDataTitle": "样例数据",
- "home.tutorial.tabs.securitySolutionTitle": "安全",
"home.tutorial.unexpectedStatusCheckStateErrorDescription": "意外的状态检查状态 {statusCheckState}",
"home.tutorial.unhandledInstructionTypeErrorDescription": "未处理的指令类型 {visibleInstructions}",
"home.tutorialDirectory.featureCatalogueDescription": "从热门应用和服务中采集数据。",
@@ -4249,8 +4245,6 @@
"kibana-react.noDataPage.cantDecide.link": "请参阅我们的文档以了解更多信息。",
"kibana-react.noDataPage.elasticAgentCard.description": "使用 Elastic 代理以简单统一的方式从您的计算机中收集数据。",
"kibana-react.noDataPage.elasticAgentCard.title": "添加 Elastic 代理",
- "kibana-react.noDataPage.elasticBeatsCard.description": "使用 Beats 将各种系统的数据添加到 Elasticsearch。",
- "kibana-react.noDataPage.elasticBeatsCard.title": "添加数据",
"kibana-react.noDataPage.intro": "添加您的数据以开始,或{link}{solution}。",
"kibana-react.noDataPage.intro.link": "了解详情",
"kibana-react.noDataPage.noDataPage.recommended": "推荐",
@@ -11321,12 +11315,7 @@
"xpack.fleet.fleetServerUpgradeModal.modalTitle": "将代理注册到 Fleet 服务器",
"xpack.fleet.fleetServerUpgradeModal.onPremDescriptionMessage": "Fleet 服务器现在可用且提供改善的可扩展性和安全性。{existingAgentsMessage}要继续使用 Fleet,必须在各个主机上安装 Fleet 服务器和新版 Elastic 代理。详细了解我们的 {link}。",
"xpack.fleet.genericActionsMenuText": "打开",
- "xpack.fleet.homeIntegration.tutorialDirectory.dismissNoticeButtonText": "关闭消息",
"xpack.fleet.homeIntegration.tutorialDirectory.fleetAppButtonText": "试用集成",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeText": "通过 Elastic 代理集成,可以简单统一的方式将日志、指标和其他类型数据的监测添加到主机。不再需要安装多个 Beats,这样将策略部署到整个基础架构更容易也更快速。有关更多信息,请阅读我们的{blogPostLink}。",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeText.blogPostLink": "公告博客",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle": "{newPrefix}Elastic 代理集成",
- "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle.newPrefix": "已正式发布:",
"xpack.fleet.homeIntegration.tutorialModule.noticeText": "{notePrefix}此模块的较新版本{availableAsIntegrationLink}。要详细了解集成和新 Elastic 代理,请阅读我们的{blogPostLink}。",
"xpack.fleet.homeIntegration.tutorialModule.noticeText.blogPostLink": "公告博客",
"xpack.fleet.homeIntegration.tutorialModule.noticeText.integrationLink": "将作为 Elastic 代理集成来提供",
diff --git a/x-pack/test/accessibility/apps/home.ts b/x-pack/test/accessibility/apps/home.ts
index a7158d9579b60..61297859c29f8 100644
--- a/x-pack/test/accessibility/apps/home.ts
+++ b/x-pack/test/accessibility/apps/home.ts
@@ -64,33 +64,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
- it('Add data page meets a11y requirements ', async () => {
- await home.clickGoHome();
- await testSubjects.click('homeAddData');
- await a11y.testAppSnapshot();
- });
-
- it('Sample data page meets a11y requirements ', async () => {
- await testSubjects.click('homeTab-sampleData');
- await a11y.testAppSnapshot();
- });
-
- it('click on Add logs panel to open all log examples page meets a11y requirements ', async () => {
- await testSubjects.click('sampleDataSetCardlogs');
- await a11y.testAppSnapshot();
- });
-
- it('click on ActiveMQ logs panel to open tutorial meets a11y requirements', async () => {
- await testSubjects.click('homeTab-all');
- await testSubjects.click('homeSynopsisLinkactivemqlogs');
- await a11y.testAppSnapshot();
- });
-
- it('click on cloud tutorial meets a11y requirements', async () => {
- await testSubjects.click('onCloudTutorial');
- await a11y.testAppSnapshot();
- });
-
it('passes with searchbox open', async () => {
await testSubjects.click('nav-search-popover');
await a11y.testAppSnapshot();
From 828437621762ec327a5bf8f2b53927204d21776b Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 07:43:48 -0400
Subject: [PATCH 08/36] [RAC] [Metrics UI] Include group name in the reason
message (#115171) (#115523)
* [RAC] [Metrics UI] Include group name in the reason message
* remove console log
* fix i18n errors
* fix more i18n errors
* fix i18n & check errors and move group to the end of the reason text
* add empty lines at the end of translation files
* fix more i18n tests
* try to remove manually added translations
* Revert "try to remove manually added translations"
This reverts commit 6949af2f70aff46b088bab5c942497ad46081d90.
* apply i18n_check fix and reorder values in the formatted reason
* log threshold reformat reason message and move group info at the end
Co-authored-by: mgiota
---
.../server/lib/alerting/common/messages.ts | 18 ++++++----
.../inventory_metric_threshold_executor.ts | 35 +++++++++++--------
.../log_threshold/reason_formatters.ts | 4 +--
.../metric_threshold_executor.ts | 9 ++---
.../translations/translations/ja-JP.json | 3 --
.../translations/translations/zh-CN.json | 3 --
6 files changed, 40 insertions(+), 32 deletions(-)
diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts
index 084043f357bb1..23c89abf4a7aa 100644
--- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts
@@ -109,15 +109,17 @@ const thresholdToI18n = ([a, b]: Array) => {
};
export const buildFiredAlertReason: (alertResult: {
+ group: string;
metric: string;
comparator: Comparator;
threshold: Array;
currentValue: number | string;
-}) => string = ({ metric, comparator, threshold, currentValue }) =>
+}) => string = ({ group, metric, comparator, threshold, currentValue }) =>
i18n.translate('xpack.infra.metrics.alerting.threshold.firedAlertReason', {
defaultMessage:
- '{metric} is {comparator} a threshold of {threshold} (current value is {currentValue})',
+ '{metric} is {comparator} a threshold of {threshold} (current value is {currentValue}) for {group}',
values: {
+ group,
metric,
comparator: comparatorToI18n(comparator, threshold.map(toNumber), toNumber(currentValue)),
threshold: thresholdToI18n(threshold),
@@ -126,14 +128,15 @@ export const buildFiredAlertReason: (alertResult: {
});
export const buildRecoveredAlertReason: (alertResult: {
+ group: string;
metric: string;
comparator: Comparator;
threshold: Array;
currentValue: number | string;
-}) => string = ({ metric, comparator, threshold, currentValue }) =>
+}) => string = ({ group, metric, comparator, threshold, currentValue }) =>
i18n.translate('xpack.infra.metrics.alerting.threshold.recoveredAlertReason', {
defaultMessage:
- '{metric} is now {comparator} a threshold of {threshold} (current value is {currentValue})',
+ '{metric} is now {comparator} a threshold of {threshold} (current value is {currentValue}) for {group}',
values: {
metric,
comparator: recoveredComparatorToI18n(
@@ -143,19 +146,22 @@ export const buildRecoveredAlertReason: (alertResult: {
),
threshold: thresholdToI18n(threshold),
currentValue,
+ group,
},
});
export const buildNoDataAlertReason: (alertResult: {
+ group: string;
metric: string;
timeSize: number;
timeUnit: string;
-}) => string = ({ metric, timeSize, timeUnit }) =>
+}) => string = ({ group, metric, timeSize, timeUnit }) =>
i18n.translate('xpack.infra.metrics.alerting.threshold.noDataAlertReason', {
- defaultMessage: '{metric} has reported no data over the past {interval}',
+ defaultMessage: '{metric} has reported no data over the past {interval} for {group}',
values: {
metric,
interval: `${timeSize}${timeUnit}`,
+ group,
},
});
diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts
index 72d9ea9e39def..d8b66b35c703b 100644
--- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts
@@ -111,18 +111,18 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
)
);
const inventoryItems = Object.keys(first(results)!);
- for (const item of inventoryItems) {
+ for (const group of inventoryItems) {
// AND logic; all criteria must be across the threshold
const shouldAlertFire = results.every((result) => {
// Grab the result of the most recent bucket
- return last(result[item].shouldFire);
+ return last(result[group].shouldFire);
});
- const shouldAlertWarn = results.every((result) => last(result[item].shouldWarn));
+ const shouldAlertWarn = results.every((result) => last(result[group].shouldWarn));
// AND logic; because we need to evaluate all criteria, if one of them reports no data then the
// whole alert is in a No Data/Error state
- const isNoData = results.some((result) => last(result[item].isNoData));
- const isError = results.some((result) => result[item].isError);
+ const isNoData = results.some((result) => last(result[group].isNoData));
+ const isError = results.some((result) => result[group].isError);
const nextState = isError
? AlertStates.ERROR
@@ -138,7 +138,8 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
reason = results
.map((result) =>
buildReasonWithVerboseMetricName(
- result[item],
+ group,
+ result[group],
buildFiredAlertReason,
nextState === AlertStates.WARNING
)
@@ -151,19 +152,23 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
*/
// } else if (nextState === AlertStates.OK && prevState?.alertState === AlertStates.ALERT) {
// reason = results
- // .map((result) => buildReasonWithVerboseMetricName(result[item], buildRecoveredAlertReason))
+ // .map((result) => buildReasonWithVerboseMetricName(group, result[group], buildRecoveredAlertReason))
// .join('\n');
}
if (alertOnNoData) {
if (nextState === AlertStates.NO_DATA) {
reason = results
- .filter((result) => result[item].isNoData)
- .map((result) => buildReasonWithVerboseMetricName(result[item], buildNoDataAlertReason))
+ .filter((result) => result[group].isNoData)
+ .map((result) =>
+ buildReasonWithVerboseMetricName(group, result[group], buildNoDataAlertReason)
+ )
.join('\n');
} else if (nextState === AlertStates.ERROR) {
reason = results
- .filter((result) => result[item].isError)
- .map((result) => buildReasonWithVerboseMetricName(result[item], buildErrorAlertReason))
+ .filter((result) => result[group].isError)
+ .map((result) =>
+ buildReasonWithVerboseMetricName(group, result[group], buildErrorAlertReason)
+ )
.join('\n');
}
}
@@ -175,7 +180,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
? WARNING_ACTIONS.id
: FIRED_ACTIONS.id;
- const alertInstance = alertInstanceFactory(`${item}`, reason);
+ const alertInstance = alertInstanceFactory(`${group}`, reason);
alertInstance.scheduleActions(
/**
* TODO: We're lying to the compiler here as explicitly calling `scheduleActions` on
@@ -183,12 +188,12 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
*/
actionGroupId as unknown as InventoryMetricThresholdAllowedActionGroups,
{
- group: item,
+ group,
alertState: stateToAlertMessage[nextState],
reason,
timestamp: moment().toISOString(),
value: mapToConditionsLookup(results, (result) =>
- formatMetric(result[item].metric, result[item].currentValue)
+ formatMetric(result[group].metric, result[group].currentValue)
),
threshold: mapToConditionsLookup(criteria, (c) => c.threshold),
metric: mapToConditionsLookup(criteria, (c) => c.metric),
@@ -199,6 +204,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
});
const buildReasonWithVerboseMetricName = (
+ group: string,
resultItem: any,
buildReason: (r: any) => string,
useWarningThreshold?: boolean
@@ -206,6 +212,7 @@ const buildReasonWithVerboseMetricName = (
if (!resultItem) return '';
const resultWithVerboseMetricName = {
...resultItem,
+ group,
metric:
toMetricOpt(resultItem.metric)?.text ||
(resultItem.metric === 'custom'
diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts
index cd579b9965b66..f70e0a0140ce8 100644
--- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts
@@ -34,7 +34,7 @@ export const getReasonMessageForGroupedCountAlert = (
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription', {
defaultMessage:
- '{groupName}: {actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions.',
+ '{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions for {groupName}.',
values: {
actualCount,
expectedCount,
@@ -66,7 +66,7 @@ export const getReasonMessageForGroupedRatioAlert = (
) =>
i18n.translate('xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription', {
defaultMessage:
- '{groupName}: The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}).',
+ 'The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}) for {groupName}.',
values: {
actualRatio,
expectedRatio,
diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts
index af5f945eeb4bb..e4887e922bb66 100644
--- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts
@@ -143,9 +143,10 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) {
reason = alertResults
.map((result) =>
- buildFiredAlertReason(
- formatAlertResult(result[group], nextState === AlertStates.WARNING)
- )
+ buildFiredAlertReason({
+ ...formatAlertResult(result[group], nextState === AlertStates.WARNING),
+ group,
+ })
)
.join('\n');
/*
@@ -181,7 +182,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
if (nextState === AlertStates.NO_DATA) {
reason = alertResults
.filter((result) => result[group].isNoData)
- .map((result) => buildNoDataAlertReason(result[group]))
+ .map((result) => buildNoDataAlertReason({ ...result[group], group }))
.join('\n');
} else if (nextState === AlertStates.ERROR) {
reason = alertResults
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 95f6909b12f6c..f86167046c4c2 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -13580,15 +13580,12 @@
"xpack.infra.metrics.alerting.threshold.errorAlertReason": "{metric}のデータのクエリを試行しているときに、Elasticsearchが失敗しました",
"xpack.infra.metrics.alerting.threshold.errorState": "エラー",
"xpack.infra.metrics.alerting.threshold.fired": "アラート",
- "xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric}は{comparator} {threshold}のしきい値です(現在の値は{currentValue})",
"xpack.infra.metrics.alerting.threshold.gtComparator": "より大きい",
"xpack.infra.metrics.alerting.threshold.ltComparator": "より小さい",
- "xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric}は過去{interval}にデータを報告していません",
"xpack.infra.metrics.alerting.threshold.noDataFormattedValue": "[データなし]",
"xpack.infra.metrics.alerting.threshold.noDataState": "データなし",
"xpack.infra.metrics.alerting.threshold.okState": "OK [回復済み]",
"xpack.infra.metrics.alerting.threshold.outsideRangeComparator": "の間にない",
- "xpack.infra.metrics.alerting.threshold.recoveredAlertReason": "{metric}は{comparator} {threshold}のしきい値です(現在の値は{currentValue})",
"xpack.infra.metrics.alerting.threshold.thresholdRange": "{a}と{b}",
"xpack.infra.metrics.alerting.threshold.warning": "警告",
"xpack.infra.metrics.alerting.threshold.warningState": "警告",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 80dad5a2c0c8b..6cfdc69c9e897 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -13769,15 +13769,12 @@
"xpack.infra.metrics.alerting.threshold.errorAlertReason": "Elasticsearch 尝试查询 {metric} 的数据时出现故障",
"xpack.infra.metrics.alerting.threshold.errorState": "错误",
"xpack.infra.metrics.alerting.threshold.fired": "告警",
- "xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric} {comparator}阈值 {threshold}(当前值为 {currentValue})",
"xpack.infra.metrics.alerting.threshold.gtComparator": "大于",
"xpack.infra.metrics.alerting.threshold.ltComparator": "小于",
- "xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric} 在过去 {interval}中未报告数据",
"xpack.infra.metrics.alerting.threshold.noDataFormattedValue": "[无数据]",
"xpack.infra.metrics.alerting.threshold.noDataState": "无数据",
"xpack.infra.metrics.alerting.threshold.okState": "正常 [已恢复]",
"xpack.infra.metrics.alerting.threshold.outsideRangeComparator": "不介于",
- "xpack.infra.metrics.alerting.threshold.recoveredAlertReason": "{metric} 现在{comparator}阈值 {threshold}(当前值为 {currentValue})",
"xpack.infra.metrics.alerting.threshold.thresholdRange": "{a} 和 {b}",
"xpack.infra.metrics.alerting.threshold.warning": "警告",
"xpack.infra.metrics.alerting.threshold.warningState": "警告",
From fe17761ae8f644df5f10af1275fe0640eff7e4a2 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 19 Oct 2021 15:38:12 +0300
Subject: [PATCH 09/36] [VisEditors] Sets level for all registered deprecations
(#115505)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
src/plugins/kibana_legacy/server/index.ts | 5 ++++-
src/plugins/vis_types/metric/server/index.ts | 2 +-
src/plugins/vis_types/table/server/index.ts | 4 ++--
src/plugins/vis_types/tagcloud/server/index.ts | 2 +-
src/plugins/vis_types/timelion/server/index.ts | 15 ++++++++++-----
src/plugins/vis_types/timeseries/server/index.ts | 8 +++++---
src/plugins/vis_types/vega/server/index.ts | 6 ++++--
7 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/src/plugins/kibana_legacy/server/index.ts b/src/plugins/kibana_legacy/server/index.ts
index 15ae8547c73e1..3f731efbfe857 100644
--- a/src/plugins/kibana_legacy/server/index.ts
+++ b/src/plugins/kibana_legacy/server/index.ts
@@ -18,7 +18,10 @@ export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
// TODO: Remove deprecation once defaultAppId is deleted
- renameFromRoot('kibana.defaultAppId', 'kibana_legacy.defaultAppId', { silent: true }),
+ renameFromRoot('kibana.defaultAppId', 'kibana_legacy.defaultAppId', {
+ silent: true,
+ level: 'critical',
+ }),
(completeConfig, rootPath, addDeprecation) => {
if (
get(completeConfig, 'kibana.defaultAppId') === undefined &&
diff --git a/src/plugins/vis_types/metric/server/index.ts b/src/plugins/vis_types/metric/server/index.ts
index 740fe3426dd84..0b6768074d6cb 100644
--- a/src/plugins/vis_types/metric/server/index.ts
+++ b/src/plugins/vis_types/metric/server/index.ts
@@ -13,7 +13,7 @@ import { configSchema, ConfigSchema } from '../config';
export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
- renameFromRoot('metric_vis.enabled', 'vis_type_metric.enabled'),
+ renameFromRoot('metric_vis.enabled', 'vis_type_metric.enabled', { level: 'critical' }),
],
};
diff --git a/src/plugins/vis_types/table/server/index.ts b/src/plugins/vis_types/table/server/index.ts
index eed1134f3ff48..103586305139c 100644
--- a/src/plugins/vis_types/table/server/index.ts
+++ b/src/plugins/vis_types/table/server/index.ts
@@ -15,9 +15,9 @@ import { registerVisTypeTableUsageCollector } from './usage_collector';
export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot, unused }) => [
- renameFromRoot('table_vis.enabled', 'vis_type_table.enabled'),
+ renameFromRoot('table_vis.enabled', 'vis_type_table.enabled', { level: 'critical' }),
// Unused property which should be removed after releasing Kibana v8.0:
- unused('legacyVisEnabled'),
+ unused('legacyVisEnabled', { level: 'critical' }),
],
};
diff --git a/src/plugins/vis_types/tagcloud/server/index.ts b/src/plugins/vis_types/tagcloud/server/index.ts
index 6899a333a8812..35c0a127c873f 100644
--- a/src/plugins/vis_types/tagcloud/server/index.ts
+++ b/src/plugins/vis_types/tagcloud/server/index.ts
@@ -13,7 +13,7 @@ import { configSchema, ConfigSchema } from '../config';
export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
- renameFromRoot('tagcloud.enabled', 'vis_type_tagcloud.enabled'),
+ renameFromRoot('tagcloud.enabled', 'vis_type_tagcloud.enabled', { level: 'critical' }),
],
};
diff --git a/src/plugins/vis_types/timelion/server/index.ts b/src/plugins/vis_types/timelion/server/index.ts
index ef7baf981de1a..b5985932188fb 100644
--- a/src/plugins/vis_types/timelion/server/index.ts
+++ b/src/plugins/vis_types/timelion/server/index.ts
@@ -13,12 +13,17 @@ import { TimelionPlugin } from './plugin';
export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot, unused }) => [
- renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled'),
- renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled'),
- renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls'),
+ renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled', { level: 'critical' }),
+ renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled', { level: 'critical' }),
+ renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls', {
+ level: 'critical',
+ }),
// Unused properties which should be removed after releasing Kibana v8.0:
- renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled', { silent: true }),
- unused('ui.enabled'),
+ renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled', {
+ silent: true,
+ level: 'critical',
+ }),
+ unused('ui.enabled', { level: 'critical' }),
],
};
diff --git a/src/plugins/vis_types/timeseries/server/index.ts b/src/plugins/vis_types/timeseries/server/index.ts
index 0890b37e77926..ff2e54b31084b 100644
--- a/src/plugins/vis_types/timeseries/server/index.ts
+++ b/src/plugins/vis_types/timeseries/server/index.ts
@@ -15,17 +15,19 @@ export { VisTypeTimeseriesSetup } from './plugin';
export const config: PluginConfigDescriptor = {
deprecations: ({ unused, renameFromRoot }) => [
// In Kibana v7.8 plugin id was renamed from 'metrics' to 'vis_type_timeseries':
- renameFromRoot('metrics.enabled', 'vis_type_timeseries.enabled'),
+ renameFromRoot('metrics.enabled', 'vis_type_timeseries.enabled', { level: 'critical' }),
renameFromRoot('metrics.chartResolution', 'vis_type_timeseries.chartResolution', {
silent: true,
+ level: 'critical',
}),
renameFromRoot('metrics.minimumBucketSize', 'vis_type_timeseries.minimumBucketSize', {
silent: true,
+ level: 'critical',
}),
// Unused properties which should be removed after releasing Kibana v8.0:
- unused('chartResolution'),
- unused('minimumBucketSize'),
+ unused('chartResolution', { level: 'critical' }),
+ unused('minimumBucketSize', { level: 'critical' }),
],
schema: configSchema,
};
diff --git a/src/plugins/vis_types/vega/server/index.ts b/src/plugins/vis_types/vega/server/index.ts
index 156dec027372a..220d049c739ea 100644
--- a/src/plugins/vis_types/vega/server/index.ts
+++ b/src/plugins/vis_types/vega/server/index.ts
@@ -17,8 +17,10 @@ export const config: PluginConfigDescriptor = {
},
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
- renameFromRoot('vega.enableExternalUrls', 'vis_type_vega.enableExternalUrls'),
- renameFromRoot('vega.enabled', 'vis_type_vega.enabled'),
+ renameFromRoot('vega.enableExternalUrls', 'vis_type_vega.enableExternalUrls', {
+ level: 'critical',
+ }),
+ renameFromRoot('vega.enabled', 'vis_type_vega.enabled', { level: 'critical' }),
],
};
From c2aae3be130cab8af34e088a07012f9861a5bfcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20S=C3=A1nchez?=
Date: Tue, 19 Oct 2021 14:57:24 +0200
Subject: [PATCH 10/36] [7.x] [Security Solution][Endpoint] Adds additional
endpoint privileges to the `useUserPrivileges()` hook (#115051) (#115511)
* [Security Solution][Endpoint] Adds additional endpoint privileges to the `useUserPrivileges()` hook (#115051)
* Adds new `canIsolateHost` and `canCreateArtifactsByPolicy` privileges for endpoint
* Refactor `useEndpointPrivileges` mocks to also provide a test function to return the full set of default privileges
* refactor useEndpointPrivileges tests to be more resilient to future changes
---
.../__mocks__/use_endpoint_privileges.ts | 18 ----
.../__mocks__/use_endpoint_privileges.ts | 12 +++
.../user_privileges/endpoint/index.ts | 9 ++
.../user_privileges/endpoint/mocks.ts | 29 ++++++
.../use_endpoint_privileges.test.ts | 89 ++++++++++---------
.../{ => endpoint}/use_endpoint_privileges.ts | 25 ++++--
.../user_privileges/endpoint/utils.ts | 19 ++++
.../components/user_privileges/index.tsx | 10 +--
.../components/user_info/index.test.tsx | 2 +-
.../alerts/use_alerts_privileges.test.tsx | 6 +-
.../alerts/use_signal_index.test.tsx | 2 +-
.../search_exceptions.test.tsx | 17 ++--
.../search_exceptions/search_exceptions.tsx | 2 +-
.../view/event_filters_list_page.test.tsx | 2 +-
.../host_isolation_exceptions_list.test.tsx | 3 +-
.../policy_trusted_apps_empty_unassigned.tsx | 2 +-
.../policy_trusted_apps_flyout.test.tsx | 2 +-
.../policy_trusted_apps_layout.test.tsx | 22 ++---
.../layout/policy_trusted_apps_layout.tsx | 2 +-
.../list/policy_trusted_apps_list.test.tsx | 10 +--
.../list/policy_trusted_apps_list.tsx | 2 +-
.../view/trusted_apps_page.test.tsx | 2 +-
.../public/overview/pages/overview.test.tsx | 2 +-
23 files changed, 170 insertions(+), 119 deletions(-)
delete mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts
create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts
create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts
create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts
rename x-pack/plugins/security_solution/public/common/components/user_privileges/{ => endpoint}/use_endpoint_privileges.test.ts (63%)
rename x-pack/plugins/security_solution/public/common/components/user_privileges/{ => endpoint}/use_endpoint_privileges.ts (71%)
create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts
deleted file mode 100644
index 80ca534534187..0000000000000
--- a/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { EndpointPrivileges } from '../use_endpoint_privileges';
-
-export const useEndpointPrivileges = jest.fn(() => {
- const endpointPrivilegesMock: EndpointPrivileges = {
- loading: false,
- canAccessFleet: true,
- canAccessEndpointManagement: true,
- isPlatinumPlus: true,
- };
- return endpointPrivilegesMock;
-});
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts
new file mode 100644
index 0000000000000..ae9aacaf3d55b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { getEndpointPrivilegesInitialStateMock } from '../mocks';
+
+export { getEndpointPrivilegesInitialState } from '../utils';
+
+export const useEndpointPrivileges = jest.fn(getEndpointPrivilegesInitialStateMock);
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts
new file mode 100644
index 0000000000000..adea89ce1a051
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './use_endpoint_privileges';
+export { getEndpointPrivilegesInitialState } from './utils';
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts
new file mode 100644
index 0000000000000..2851c92816cea
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { EndpointPrivileges } from './use_endpoint_privileges';
+import { getEndpointPrivilegesInitialState } from './utils';
+
+export const getEndpointPrivilegesInitialStateMock = (
+ overrides: Partial = {}
+): EndpointPrivileges => {
+ // Get the initial state and set all permissions to `true` (enabled) for testing
+ const endpointPrivilegesMock: EndpointPrivileges = {
+ ...(
+ Object.entries(getEndpointPrivilegesInitialState()) as Array<
+ [keyof EndpointPrivileges, boolean]
+ >
+ ).reduce((mockPrivileges, [key, value]) => {
+ mockPrivileges[key] = !value;
+
+ return mockPrivileges;
+ }, {} as EndpointPrivileges),
+ ...overrides,
+ };
+
+ return endpointPrivilegesMock;
+};
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts
similarity index 63%
rename from x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts
rename to x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts
index 82443e913499b..d4ba29a4ef950 100644
--- a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts
@@ -6,16 +6,17 @@
*/
import { act, renderHook, RenderHookResult, RenderResult } from '@testing-library/react-hooks';
-import { useHttp, useCurrentUser } from '../../lib/kibana';
+import { useHttp, useCurrentUser } from '../../../lib/kibana';
import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges';
-import { securityMock } from '../../../../../security/public/mocks';
-import { appRoutesService } from '../../../../../fleet/common';
-import { AuthenticatedUser } from '../../../../../security/common';
-import { licenseService } from '../../hooks/use_license';
-import { fleetGetCheckPermissionsHttpMock } from '../../../management/pages/mocks';
-
-jest.mock('../../lib/kibana');
-jest.mock('../../hooks/use_license', () => {
+import { securityMock } from '../../../../../../security/public/mocks';
+import { appRoutesService } from '../../../../../../fleet/common';
+import { AuthenticatedUser } from '../../../../../../security/common';
+import { licenseService } from '../../../hooks/use_license';
+import { fleetGetCheckPermissionsHttpMock } from '../../../../management/pages/mocks';
+import { getEndpointPrivilegesInitialStateMock } from './mocks';
+
+jest.mock('../../../lib/kibana');
+jest.mock('../../../hooks/use_license', () => {
const licenseServiceInstance = {
isPlatinumPlus: jest.fn(),
};
@@ -27,6 +28,8 @@ jest.mock('../../hooks/use_license', () => {
};
});
+const licenseServiceMock = licenseService as jest.Mocked;
+
describe('When using useEndpointPrivileges hook', () => {
let authenticatedUser: AuthenticatedUser;
let fleetApiMock: ReturnType;
@@ -45,7 +48,7 @@ describe('When using useEndpointPrivileges hook', () => {
fleetApiMock = fleetGetCheckPermissionsHttpMock(
useHttp() as Parameters[0]
);
- (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true);
+ licenseServiceMock.isPlatinumPlus.mockReturnValue(true);
render = () => {
const hookRenderResponse = renderHook(() => useEndpointPrivileges());
@@ -69,34 +72,31 @@ describe('When using useEndpointPrivileges hook', () => {
(useCurrentUser as jest.Mock).mockReturnValue(null);
const { rerender } = render();
- expect(result.current).toEqual({
- canAccessEndpointManagement: false,
- canAccessFleet: false,
- loading: true,
- isPlatinumPlus: true,
- });
+ expect(result.current).toEqual(
+ getEndpointPrivilegesInitialStateMock({
+ canAccessEndpointManagement: false,
+ canAccessFleet: false,
+ loading: true,
+ })
+ );
// Make user service available
(useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser);
rerender();
- expect(result.current).toEqual({
- canAccessEndpointManagement: false,
- canAccessFleet: false,
- loading: true,
- isPlatinumPlus: true,
- });
+ expect(result.current).toEqual(
+ getEndpointPrivilegesInitialStateMock({
+ canAccessEndpointManagement: false,
+ canAccessFleet: false,
+ loading: true,
+ })
+ );
// Release the API response
await act(async () => {
fleetApiMock.waitForApi();
releaseApiResponse!();
});
- expect(result.current).toEqual({
- canAccessEndpointManagement: true,
- canAccessFleet: true,
- loading: false,
- isPlatinumPlus: true,
- });
+ expect(result.current).toEqual(getEndpointPrivilegesInitialStateMock());
});
it('should call Fleet permissions api to determine user privilege to fleet', async () => {
@@ -113,12 +113,11 @@ describe('When using useEndpointPrivileges hook', () => {
render();
await waitForNextUpdate();
await fleetApiMock.waitForApi();
- expect(result.current).toEqual({
- canAccessEndpointManagement: false,
- canAccessFleet: true, // this is only true here because I did not adjust the API mock
- loading: false,
- isPlatinumPlus: true,
- });
+ expect(result.current).toEqual(
+ getEndpointPrivilegesInitialStateMock({
+ canAccessEndpointManagement: false,
+ })
+ );
});
it('should set privileges to false if fleet api check returns failure', async () => {
@@ -130,11 +129,21 @@ describe('When using useEndpointPrivileges hook', () => {
render();
await waitForNextUpdate();
await fleetApiMock.waitForApi();
- expect(result.current).toEqual({
- canAccessEndpointManagement: false,
- canAccessFleet: false,
- loading: false,
- isPlatinumPlus: true,
- });
+ expect(result.current).toEqual(
+ getEndpointPrivilegesInitialStateMock({
+ canAccessEndpointManagement: false,
+ canAccessFleet: false,
+ })
+ );
});
+
+ it.each([['canIsolateHost'], ['canCreateArtifactsByPolicy']])(
+ 'should set %s to false if license is not PlatinumPlus',
+ async (privilege) => {
+ licenseServiceMock.isPlatinumPlus.mockReturnValue(false);
+ render();
+ await waitForNextUpdate();
+ expect(result.current).toEqual(expect.objectContaining({ [privilege]: false }));
+ }
+ );
});
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
similarity index 71%
rename from x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts
rename to x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
index 315935104d107..36f02d22487fc 100644
--- a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
@@ -6,9 +6,9 @@
*/
import { useEffect, useMemo, useRef, useState } from 'react';
-import { useCurrentUser, useHttp } from '../../lib/kibana';
-import { appRoutesService, CheckPermissionsResponse } from '../../../../../fleet/common';
-import { useLicense } from '../../hooks/use_license';
+import { useCurrentUser, useHttp } from '../../../lib/kibana';
+import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common';
+import { useLicense } from '../../../hooks/use_license';
export interface EndpointPrivileges {
loading: boolean;
@@ -16,6 +16,11 @@ export interface EndpointPrivileges {
canAccessFleet: boolean;
/** If user has permissions to access Endpoint management (includes check to ensure they also have access to fleet) */
canAccessEndpointManagement: boolean;
+ /** if user has permissions to create Artifacts by Policy */
+ canCreateArtifactsByPolicy: boolean;
+ /** If user has permissions to use the Host isolation feature */
+ canIsolateHost: boolean;
+ /** @deprecated do not use. instead, use one of the other privileges defined */
isPlatinumPlus: boolean;
}
@@ -29,7 +34,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => {
const http = useHttp();
const user = useCurrentUser();
const isMounted = useRef(true);
- const license = useLicense();
+ const isPlatinumPlusLicense = useLicense().isPlatinumPlus();
const [canAccessFleet, setCanAccessFleet] = useState(false);
const [fleetCheckDone, setFleetCheckDone] = useState(false);
@@ -61,13 +66,19 @@ export const useEndpointPrivileges = (): EndpointPrivileges => {
}, [user?.roles]);
const privileges = useMemo(() => {
- return {
+ const privilegeList: EndpointPrivileges = {
loading: !fleetCheckDone || !user,
canAccessFleet,
canAccessEndpointManagement: canAccessFleet && isSuperUser,
- isPlatinumPlus: license.isPlatinumPlus(),
+ canCreateArtifactsByPolicy: isPlatinumPlusLicense,
+ canIsolateHost: isPlatinumPlusLicense,
+ // FIXME: Remove usages of the property below
+ /** @deprecated */
+ isPlatinumPlus: isPlatinumPlusLicense,
};
- }, [canAccessFleet, fleetCheckDone, isSuperUser, user, license]);
+
+ return privilegeList;
+ }, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]);
// Capture if component is unmounted
useEffect(
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts
new file mode 100644
index 0000000000000..df91314479f18
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EndpointPrivileges } from './use_endpoint_privileges';
+
+export const getEndpointPrivilegesInitialState = (): EndpointPrivileges => {
+ return {
+ loading: true,
+ canAccessFleet: false,
+ canAccessEndpointManagement: false,
+ canIsolateHost: false,
+ canCreateArtifactsByPolicy: false,
+ isPlatinumPlus: false,
+ };
+};
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx
index 437d27278102b..bc0640296b33d 100644
--- a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx
@@ -11,9 +11,10 @@ import { DeepReadonly } from 'utility-types';
import { Capabilities } from '../../../../../../../src/core/public';
import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges';
import { useFetchListPrivileges } from '../../../detections/components/user_privileges/use_fetch_list_privileges';
-import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges';
+import { EndpointPrivileges, useEndpointPrivileges } from './endpoint';
import { SERVER_APP_ID } from '../../../../common/constants';
+import { getEndpointPrivilegesInitialState } from './endpoint/utils';
export interface UserPrivilegesState {
listPrivileges: ReturnType;
detectionEnginePrivileges: ReturnType;
@@ -24,12 +25,7 @@ export interface UserPrivilegesState {
export const initialUserPrivilegesState = (): UserPrivilegesState => ({
listPrivileges: { loading: false, error: undefined, result: undefined },
detectionEnginePrivileges: { loading: false, error: undefined, result: undefined },
- endpointPrivileges: {
- loading: true,
- canAccessEndpointManagement: false,
- canAccessFleet: false,
- isPlatinumPlus: false,
- },
+ endpointPrivileges: getEndpointPrivilegesInitialState(),
kibanaSecuritySolutionsPrivileges: { crud: false, read: false },
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx
index 3d95dca81165e..1a8588017e4d6 100644
--- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx
@@ -17,7 +17,7 @@ import { UserPrivilegesProvider } from '../../../common/components/user_privileg
jest.mock('../../../common/lib/kibana');
jest.mock('../../containers/detection_engine/alerts/api');
-jest.mock('../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
describe('useUserInfo', () => {
beforeAll(() => {
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx
index 40894c1d01929..1dc1423606097 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx
@@ -12,6 +12,7 @@ import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock';
import { useUserPrivileges } from '../../../../common/components/user_privileges';
import { Privilege } from './types';
import { UseAlertsPrivelegesReturn, useAlertsPrivileges } from './use_alerts_privileges';
+import { getEndpointPrivilegesInitialStateMock } from '../../../../common/components/user_privileges/endpoint/mocks';
jest.mock('./api');
jest.mock('../../../../common/hooks/use_app_toasts');
@@ -86,12 +87,11 @@ const userPrivilegesInitial: ReturnType = {
result: undefined,
error: undefined,
},
- endpointPrivileges: {
+ endpointPrivileges: getEndpointPrivilegesInitialStateMock({
loading: true,
canAccessEndpointManagement: false,
canAccessFleet: false,
- isPlatinumPlus: true,
- },
+ }),
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
};
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx
index ade83fed4fd6b..ad4ad5062c9d5 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx
@@ -13,7 +13,7 @@ import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
jest.mock('./api');
jest.mock('../../../../common/hooks/use_app_toasts');
-jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
describe('useSignalIndex', () => {
let appToastsMock: jest.Mocked>;
diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
index 084978d35d03a..3b987a7211411 100644
--- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx
@@ -11,11 +11,12 @@ import { AppContextTestRender, createAppRootMockRenderer } from '../../../common
import {
EndpointPrivileges,
useEndpointPrivileges,
-} from '../../../common/components/user_privileges/use_endpoint_privileges';
+} from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
import { SearchExceptions, SearchExceptionsProps } from '.';
-jest.mock('../../../common/components/user_privileges/use_endpoint_privileges');
+import { getEndpointPrivilegesInitialStateMock } from '../../../common/components/user_privileges/endpoint/mocks';
+jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
let onSearchMock: jest.Mock;
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
@@ -29,13 +30,11 @@ describe('Search exceptions', () => {
const loadedUserEndpointPrivilegesState = (
endpointOverrides: Partial = {}
- ): EndpointPrivileges => ({
- loading: false,
- canAccessFleet: true,
- canAccessEndpointManagement: true,
- isPlatinumPlus: false,
- ...endpointOverrides,
- });
+ ): EndpointPrivileges =>
+ getEndpointPrivilegesInitialStateMock({
+ isPlatinumPlus: false,
+ ...endpointOverrides,
+ });
beforeEach(() => {
onSearchMock = jest.fn();
diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx
index 1f3eab5db2947..569916ac20315 100644
--- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx
+++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx
@@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/e
import { i18n } from '@kbn/i18n';
import { PolicySelectionItem, PoliciesSelector } from '../policies_selector';
import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types';
-import { useEndpointPrivileges } from '../../../common/components/user_privileges/use_endpoint_privileges';
+import { useEndpointPrivileges } from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
export interface SearchExceptionsProps {
defaultValue?: string;
diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx
index 0729f95bb44a9..02efce1ab59e8 100644
--- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx
@@ -14,7 +14,7 @@ import { isFailedResourceState, isLoadedResourceState } from '../../../state';
// Needed to mock the data services used by the ExceptionItem component
jest.mock('../../../../common/lib/kibana');
-jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
describe('When on the Event Filters List Page', () => {
let render: () => ReturnType;
diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx
index 5113457e5bccc..625da11a3644e 100644
--- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx
@@ -16,8 +16,7 @@ import { getHostIsolationExceptionItems } from '../service';
import { HostIsolationExceptionsList } from './host_isolation_exceptions_list';
import { useLicense } from '../../../../common/hooks/use_license';
-jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges');
-
+jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
jest.mock('../service');
jest.mock('../../../../common/hooks/use_license');
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx
index ee52e1210a481..c12bec03ada04 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx
@@ -10,7 +10,7 @@ import { EuiEmptyPrompt, EuiButton, EuiPageTemplate, EuiLink } from '@elastic/eu
import { FormattedMessage } from '@kbn/i18n/react';
import { usePolicyDetailsNavigateCallback } from '../../policy_hooks';
import { useGetLinkTo } from './use_policy_trusted_apps_empty_hooks';
-import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
+import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
interface CommonProps {
policyId: string;
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx
index c1d00f7a3f99b..8e412d2020b72 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx
@@ -21,7 +21,7 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../..
import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing';
jest.mock('../../../../trusted_apps/service');
-jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
let mockedContext: AppContextTestRender;
let waitForAction: MiddlewareActionSpyHelper['waitForAction'];
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
index 43e19c00bcc8e..dbb18a1b0f2ef 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx
@@ -19,13 +19,11 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../..
import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing';
import { EndpointDocGenerator } from '../../../../../../../common/endpoint/generate_data';
import { policyListApiPathHandlers } from '../../../store/test_mock_utils';
-import {
- EndpointPrivileges,
- useEndpointPrivileges,
-} from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
+import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
+import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
jest.mock('../../../../trusted_apps/service');
-jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
let mockedContext: AppContextTestRender;
@@ -37,16 +35,6 @@ let http: typeof coreStart.http;
const generator = new EndpointDocGenerator();
describe('Policy trusted apps layout', () => {
- const loadedUserEndpointPrivilegesState = (
- endpointOverrides: Partial = {}
- ): EndpointPrivileges => ({
- loading: false,
- canAccessFleet: true,
- canAccessEndpointManagement: true,
- isPlatinumPlus: true,
- ...endpointOverrides,
- });
-
beforeEach(() => {
mockedContext = createAppRootMockRenderer();
http = mockedContext.coreStart.http;
@@ -137,7 +125,7 @@ describe('Policy trusted apps layout', () => {
it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => {
mockUseEndpointPrivileges.mockReturnValue(
- loadedUserEndpointPrivilegesState({
+ getEndpointPrivilegesInitialStateMock({
isPlatinumPlus: false,
})
);
@@ -155,7 +143,7 @@ describe('Policy trusted apps layout', () => {
it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => {
mockUseEndpointPrivileges.mockReturnValue(
- loadedUserEndpointPrivilegesState({
+ getEndpointPrivilegesInitialStateMock({
isPlatinumPlus: false,
})
);
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx
index a3f1ed215286a..49f76ad2e02c6 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx
@@ -30,7 +30,7 @@ import {
import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks';
import { PolicyTrustedAppsFlyout } from '../flyout';
import { PolicyTrustedAppsList } from '../list/policy_trusted_apps_list';
-import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
+import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
import { useAppUrl } from '../../../../../../common/lib/kibana';
import { APP_ID } from '../../../../../../../common/constants';
import { getTrustedAppsListPath } from '../../../../../common/routing';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
index b5bfc16db2899..9165aec3bef8d 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx
@@ -24,9 +24,10 @@ import { APP_ID } from '../../../../../../../common/constants';
import {
EndpointPrivileges,
useEndpointPrivileges,
-} from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
+} from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
+import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
-jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
describe('when rendering the PolicyTrustedAppsList', () => {
@@ -43,10 +44,7 @@ describe('when rendering the PolicyTrustedAppsList', () => {
const loadedUserEndpointPrivilegesState = (
endpointOverrides: Partial = {}
): EndpointPrivileges => ({
- loading: false,
- canAccessFleet: true,
- canAccessEndpointManagement: true,
- isPlatinumPlus: true,
+ ...getEndpointPrivilegesInitialStateMock(),
...endpointOverrides,
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
index 7b1f8753831c8..89ff6bd099be4 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx
@@ -38,7 +38,7 @@ import { ContextMenuItemNavByRouterProps } from '../../../../../components/conte
import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card';
import { useTestIdGenerator } from '../../../../../components/hooks/use_test_id_generator';
import { RemoveTrustedAppFromPolicyModal } from './remove_trusted_app_from_policy_modal';
-import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
+import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
const DATA_TEST_SUBJ = 'policyTrustedAppsGrid';
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx
index f39fd47c78771..b4366a8922927 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx
@@ -52,7 +52,7 @@ jest.mock('../../../../common/hooks/use_license', () => {
};
});
-jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges');
+jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
describe('When on the Trusted Apps Page', () => {
const expectedAboutInfo =
diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
index cab02450f8886..ab5ae4f613e38 100644
--- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
@@ -30,7 +30,7 @@ import {
mockCtiLinksResponse,
} from '../components/overview_cti_links/mock';
import { useCtiDashboardLinks } from '../containers/overview_cti_links';
-import { EndpointPrivileges } from '../../common/components/user_privileges/use_endpoint_privileges';
+import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { useHostsRiskScore } from '../containers/overview_risky_host_links/use_hosts_risk_score';
From bd42367bdbfb028c805cc0a22b967e32367e2a86 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 09:27:09 -0400
Subject: [PATCH 11/36] [ML] Delete annotation directly from the index it is
stored in (#115328) (#115539)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Pete Harverson
---
.../ml/common/constants/index_patterns.ts | 1 -
.../ml/server/lib/check_annotations/index.ts | 11 ++-----
.../annotation_service/annotation.test.ts | 3 +-
.../models/annotation_service/annotation.ts | 33 ++++++++++++++++---
4 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/x-pack/plugins/ml/common/constants/index_patterns.ts b/x-pack/plugins/ml/common/constants/index_patterns.ts
index d7d6c343e282b..9a8e5c1b8ae78 100644
--- a/x-pack/plugins/ml/common/constants/index_patterns.ts
+++ b/x-pack/plugins/ml/common/constants/index_patterns.ts
@@ -7,7 +7,6 @@
export const ML_ANNOTATIONS_INDEX_ALIAS_READ = '.ml-annotations-read';
export const ML_ANNOTATIONS_INDEX_ALIAS_WRITE = '.ml-annotations-write';
-export const ML_ANNOTATIONS_INDEX_PATTERN = '.ml-annotations-6';
export const ML_RESULTS_INDEX_PATTERN = '.ml-anomalies-*';
export const ML_NOTIFICATION_INDEX_PATTERN = '.ml-notifications*';
diff --git a/x-pack/plugins/ml/server/lib/check_annotations/index.ts b/x-pack/plugins/ml/server/lib/check_annotations/index.ts
index a388a24d082a6..e64b4658588cb 100644
--- a/x-pack/plugins/ml/server/lib/check_annotations/index.ts
+++ b/x-pack/plugins/ml/server/lib/check_annotations/index.ts
@@ -11,22 +11,15 @@ import { mlLog } from '../../lib/log';
import {
ML_ANNOTATIONS_INDEX_ALIAS_READ,
ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
- ML_ANNOTATIONS_INDEX_PATTERN,
} from '../../../common/constants/index_patterns';
// Annotations Feature is available if:
-// - ML_ANNOTATIONS_INDEX_PATTERN index is present
// - ML_ANNOTATIONS_INDEX_ALIAS_READ alias is present
// - ML_ANNOTATIONS_INDEX_ALIAS_WRITE alias is present
+// Note there is no need to check for the existence of the indices themselves as aliases are stored
+// in the metadata of the indices they point to, so it's impossible to have an alias that doesn't point to any index.
export async function isAnnotationsFeatureAvailable({ asInternalUser }: IScopedClusterClient) {
try {
- const indexParams = { index: ML_ANNOTATIONS_INDEX_PATTERN };
-
- const { body: annotationsIndexExists } = await asInternalUser.indices.exists(indexParams);
- if (!annotationsIndexExists) {
- return false;
- }
-
const { body: annotationsReadAliasExists } = await asInternalUser.indices.existsAlias({
index: ML_ANNOTATIONS_INDEX_ALIAS_READ,
name: ML_ANNOTATIONS_INDEX_ALIAS_READ,
diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts
index 725e0ac494944..975070e92a7ec 100644
--- a/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts
+++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts
@@ -9,7 +9,6 @@ import getAnnotationsRequestMock from './__mocks__/get_annotations_request.json'
import getAnnotationsResponseMock from './__mocks__/get_annotations_response.json';
import { ANNOTATION_TYPE } from '../../../common/constants/annotations';
-import { ML_ANNOTATIONS_INDEX_ALIAS_WRITE } from '../../../common/constants/index_patterns';
import { Annotation, isAnnotations } from '../../../common/types/annotations';
import { DeleteParams, GetResponse, IndexAnnotationArgs } from './annotation';
@@ -42,7 +41,7 @@ describe('annotation_service', () => {
const annotationMockId = 'mockId';
const deleteParamsMock: DeleteParams = {
- index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
+ index: '.ml-annotations-6',
id: annotationMockId,
refresh: 'wait_for',
};
diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts
index c6ed72de18d05..5807d181cc566 100644
--- a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts
+++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts
@@ -71,6 +71,7 @@ export interface IndexParams {
index: string;
body: Annotation;
refresh: boolean | 'wait_for' | undefined;
+ require_alias?: boolean;
id?: string;
}
@@ -99,6 +100,7 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) {
index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
body: annotation,
refresh: 'wait_for',
+ require_alias: true,
};
if (typeof annotation._id !== 'undefined') {
@@ -407,14 +409,37 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) {
}
async function deleteAnnotation(id: string) {
- const params: DeleteParams = {
- index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
+ // Find the index the annotation is stored in.
+ const searchParams: estypes.SearchRequest = {
+ index: ML_ANNOTATIONS_INDEX_ALIAS_READ,
+ size: 1,
+ body: {
+ query: {
+ ids: {
+ values: [id],
+ },
+ },
+ },
+ };
+
+ const { body } = await asInternalUser.search(searchParams);
+ const totalCount =
+ typeof body.hits.total === 'number' ? body.hits.total : body.hits.total.value;
+
+ if (totalCount === 0) {
+ throw Boom.notFound(`Cannot find annotation with ID ${id}`);
+ }
+
+ const index = body.hits.hits[0]._index;
+
+ const deleteParams: DeleteParams = {
+ index,
id,
refresh: 'wait_for',
};
- const { body } = await asInternalUser.delete(params);
- return body;
+ const { body: deleteResponse } = await asInternalUser.delete(deleteParams);
+ return deleteResponse;
}
return {
From 4317d066ffb0432c045d826ef6eb119e376d45a8 Mon Sep 17 00:00:00 2001
From: Pierre Gayvallet
Date: Tue, 19 Oct 2021 15:52:00 +0200
Subject: [PATCH 12/36] [7.x] Explicitly set `level` for core deprecations
(#115501)
* Explicitly set `level` for core deprecations
* Set for internal deprecations
* fix unit tests
---
.../config/deprecation/core_deprecations.ts | 93 ++++++++++++-------
src/core/server/kibana_config.ts | 1 +
src/plugins/home/server/index.ts | 4 +-
src/plugins/newsfeed/server/index.ts | 2 +-
.../server/config/deprecations.test.ts | 2 +
.../telemetry/server/config/deprecations.ts | 1 +
src/plugins/usage_collection/server/config.ts | 14 ++-
.../licensing/server/licensing_config.ts | 3 +-
8 files changed, 78 insertions(+), 42 deletions(-)
diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts
index 1cf67f479f9b3..b115ea25fc363 100644
--- a/src/core/server/config/deprecation/core_deprecations.ts
+++ b/src/core/server/config/deprecation/core_deprecations.ts
@@ -13,6 +13,7 @@ const kibanaPathConf: ConfigDeprecation = (settings, fromPath, addDeprecation) =
if (process.env?.KIBANA_PATH_CONF) {
addDeprecation({
configPath: 'env.KIBANA_PATH_CONF',
+ level: 'critical',
message: `Environment variable "KIBANA_PATH_CONF" is deprecated. It has been replaced with "KBN_PATH_CONF" pointing to a config folder`,
correctiveActions: {
manualSteps: [
@@ -27,6 +28,7 @@ const configPathDeprecation: ConfigDeprecation = (settings, fromPath, addDepreca
if (process.env?.CONFIG_PATH) {
addDeprecation({
configPath: 'env.CONFIG_PATH',
+ level: 'critical',
message: `Environment variable "CONFIG_PATH" is deprecated. It has been replaced with "KBN_PATH_CONF" pointing to a config folder`,
correctiveActions: {
manualSteps: ['Use "KBN_PATH_CONF" instead of "CONFIG_PATH" to point to a config folder.'],
@@ -39,6 +41,7 @@ const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati
if (process.env?.DATA_PATH) {
addDeprecation({
configPath: 'env.DATA_PATH',
+ level: 'critical',
message: `Environment variable "DATA_PATH" will be removed. It has been replaced with kibana.yml setting "path.data"`,
correctiveActions: {
manualSteps: [
@@ -53,13 +56,13 @@ const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, addDe
if (settings.server?.basePath && !settings.server?.rewriteBasePath) {
addDeprecation({
configPath: 'server.basePath',
+ level: 'warning',
title: 'Setting "server.rewriteBasePath" should be set when using "server.basePath"',
message:
'You should set server.basePath along with server.rewriteBasePath. Starting in 7.0, Kibana ' +
'will expect that all requests start with server.basePath rather than expecting you to rewrite ' +
'the requests in your reverse proxy. Set server.rewriteBasePath to false to preserve the ' +
'current behavior and silence this warning.',
- level: 'warning',
correctiveActions: {
manualSteps: [
`Set 'server.rewriteBasePath' in the config file, CLI flag, or environment variable (in Docker only).`,
@@ -75,6 +78,7 @@ const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, addDeprecati
if (typeof corsSettings === 'boolean') {
addDeprecation({
configPath: 'server.cors',
+ level: 'warning',
title: 'Setting "server.cors" is deprecated',
message: '"server.cors" is deprecated and has been replaced by "server.cors.enabled"',
correctiveActions: {
@@ -113,6 +117,7 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati
if (sourceList.find((source) => source.includes(NONCE_STRING))) {
addDeprecation({
configPath: 'csp.rules',
+ level: 'critical',
message: `csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`,
correctiveActions: {
manualSteps: [`Replace {nonce} syntax with 'self' in ${policy}`],
@@ -132,6 +137,7 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati
) {
addDeprecation({
configPath: 'csp.rules',
+ level: 'critical',
message: `csp.rules must contain the 'self' source. Automatically adding to ${policy}.`,
correctiveActions: {
manualSteps: [`Add 'self' source to ${policy}.`],
@@ -156,6 +162,7 @@ const mapManifestServiceUrlDeprecation: ConfigDeprecation = (
if (settings.map?.manifestServiceUrl) {
addDeprecation({
configPath: 'map.manifestServiceUrl',
+ level: 'critical',
message:
'You should no longer use the map.manifestServiceUrl setting in kibana.yml to configure the location ' +
'of the Elastic Maps Service settings. These settings have moved to the "map.emsTileApiUrl" and ' +
@@ -175,6 +182,7 @@ const serverHostZeroDeprecation: ConfigDeprecation = (settings, fromPath, addDep
if (settings.server?.host === '0') {
addDeprecation({
configPath: 'server.host',
+ level: 'critical',
message:
'Support for setting server.host to "0" in kibana.yml is deprecated and will be removed in Kibana version 8.0.0. ' +
'Instead use "0.0.0.0" to bind to all interfaces.',
@@ -206,6 +214,7 @@ const opsLoggingEventDeprecation: ConfigDeprecation = (
if (settings.logging?.events?.ops) {
addDeprecation({
configPath: 'logging.events.ops',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`,
title: i18n.translate('core.deprecations.loggingEventsOps.deprecationTitle', {
defaultMessage: `Setting "logging.events.ops" is deprecated`,
@@ -237,6 +246,7 @@ const requestLoggingEventDeprecation: ConfigDeprecation = (
if (settings.logging?.events?.request) {
addDeprecation({
configPath: 'logging.events.request',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`,
title: i18n.translate('core.deprecations.loggingEventsRequest.deprecationTitle', {
defaultMessage: `Setting "logging.events.request" is deprecated`,
@@ -268,6 +278,7 @@ const responseLoggingEventDeprecation: ConfigDeprecation = (
if (settings.logging?.events?.response) {
addDeprecation({
configPath: 'logging.events.response',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`,
title: i18n.translate('core.deprecations.loggingEventsResponse.deprecationTitle', {
defaultMessage: `Setting "logging.events.response" is deprecated`,
@@ -299,6 +310,7 @@ const timezoneLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.timezone) {
addDeprecation({
configPath: 'logging.timezone',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingtimezone`,
title: i18n.translate('core.deprecations.loggingTimezone.deprecationTitle', {
defaultMessage: `Setting "logging.timezone" is deprecated`,
@@ -330,6 +342,7 @@ const destLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.dest) {
addDeprecation({
configPath: 'logging.dest',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingdest`,
title: i18n.translate('core.deprecations.loggingDest.deprecationTitle', {
defaultMessage: `Setting "logging.dest" is deprecated`,
@@ -361,6 +374,7 @@ const quietLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.quiet) {
addDeprecation({
configPath: 'logging.quiet',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingquiet`,
title: i18n.translate('core.deprecations.loggingQuiet.deprecationTitle', {
defaultMessage: `Setting "logging.quiet" is deprecated`,
@@ -391,6 +405,7 @@ const silentLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.silent) {
addDeprecation({
configPath: 'logging.silent',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingsilent`,
title: i18n.translate('core.deprecations.loggingSilent.deprecationTitle', {
defaultMessage: `Setting "logging.silent" is deprecated`,
@@ -421,6 +436,7 @@ const verboseLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.verbose) {
addDeprecation({
configPath: 'logging.verbose',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingverbose`,
title: i18n.translate('core.deprecations.loggingVerbose.deprecationTitle', {
defaultMessage: `Setting "logging.verbose" is deprecated`,
@@ -455,6 +471,7 @@ const jsonLoggingDeprecation: ConfigDeprecation = (
if (settings.logging?.json && settings.env !== 'development') {
addDeprecation({
configPath: 'logging.json',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx`,
title: i18n.translate('core.deprecations.loggingJson.deprecationTitle', {
defaultMessage: `Setting "logging.json" is deprecated`,
@@ -487,6 +504,7 @@ const logRotateDeprecation: ConfigDeprecation = (
if (settings.logging?.rotate) {
addDeprecation({
configPath: 'logging.rotate',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#rolling-file-appender`,
title: i18n.translate('core.deprecations.loggingRotate.deprecationTitle', {
defaultMessage: `Setting "logging.rotate" is deprecated`,
@@ -518,6 +536,7 @@ const logEventsLogDeprecation: ConfigDeprecation = (
if (settings.logging?.events?.log) {
addDeprecation({
configPath: 'logging.events.log',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`,
title: i18n.translate('core.deprecations.loggingEventsLog.deprecationTitle', {
defaultMessage: `Setting "logging.events.log" is deprecated`,
@@ -548,6 +567,7 @@ const logEventsErrorDeprecation: ConfigDeprecation = (
if (settings.logging?.events?.error) {
addDeprecation({
configPath: 'logging.events.error',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`,
title: i18n.translate('core.deprecations.loggingEventsError.deprecationTitle', {
defaultMessage: `Setting "logging.events.error" is deprecated`,
@@ -578,6 +598,7 @@ const logFilterDeprecation: ConfigDeprecation = (
if (settings.logging?.filter) {
addDeprecation({
configPath: 'logging.filter',
+ level: 'critical',
documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingfilter`,
title: i18n.translate('core.deprecations.loggingFilter.deprecationTitle', {
defaultMessage: `Setting "logging.filter" is deprecated`,
@@ -596,40 +617,42 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({
unusedFromRoot,
renameFromRoot,
}) => [
- unusedFromRoot('savedObjects.indexCheckTimeout'),
- unusedFromRoot('server.xsrf.token'),
- unusedFromRoot('maps.manifestServiceUrl'),
- unusedFromRoot('optimize.lazy'),
- unusedFromRoot('optimize.lazyPort'),
- unusedFromRoot('optimize.lazyHost'),
- unusedFromRoot('optimize.lazyPrebuild'),
- unusedFromRoot('optimize.lazyProxyTimeout'),
- unusedFromRoot('optimize.enabled'),
- unusedFromRoot('optimize.bundleFilter'),
- unusedFromRoot('optimize.bundleDir'),
- unusedFromRoot('optimize.viewCaching'),
- unusedFromRoot('optimize.watch'),
- unusedFromRoot('optimize.watchPort'),
- unusedFromRoot('optimize.watchHost'),
- unusedFromRoot('optimize.watchPrebuild'),
- unusedFromRoot('optimize.watchProxyTimeout'),
- unusedFromRoot('optimize.useBundleCache'),
- unusedFromRoot('optimize.sourceMaps'),
- unusedFromRoot('optimize.workers'),
- unusedFromRoot('optimize.profile'),
- unusedFromRoot('optimize.validateSyntaxOfNodeModules'),
- renameFromRoot('xpack.xpack_main.telemetry.config', 'telemetry.config'),
- renameFromRoot('xpack.xpack_main.telemetry.url', 'telemetry.url'),
- renameFromRoot('xpack.xpack_main.telemetry.enabled', 'telemetry.enabled'),
- renameFromRoot('xpack.telemetry.enabled', 'telemetry.enabled'),
- renameFromRoot('xpack.telemetry.config', 'telemetry.config'),
- renameFromRoot('xpack.telemetry.banner', 'telemetry.banner'),
- renameFromRoot('xpack.telemetry.url', 'telemetry.url'),
- renameFromRoot('cpu.cgroup.path.override', 'ops.cGroupOverrides.cpuPath'),
- renameFromRoot('cpuacct.cgroup.path.override', 'ops.cGroupOverrides.cpuAcctPath'),
- renameFromRoot('server.xsrf.whitelist', 'server.xsrf.allowlist'),
- unusedFromRoot('elasticsearch.preserveHost'),
- unusedFromRoot('elasticsearch.startupTimeout'),
+ unusedFromRoot('savedObjects.indexCheckTimeout', { level: 'critical' }),
+ unusedFromRoot('server.xsrf.token', { level: 'critical' }),
+ unusedFromRoot('maps.manifestServiceUrl', { level: 'critical' }),
+ unusedFromRoot('optimize.lazy', { level: 'critical' }),
+ unusedFromRoot('optimize.lazyPort', { level: 'critical' }),
+ unusedFromRoot('optimize.lazyHost', { level: 'critical' }),
+ unusedFromRoot('optimize.lazyPrebuild', { level: 'critical' }),
+ unusedFromRoot('optimize.lazyProxyTimeout', { level: 'critical' }),
+ unusedFromRoot('optimize.enabled', { level: 'critical' }),
+ unusedFromRoot('optimize.bundleFilter', { level: 'critical' }),
+ unusedFromRoot('optimize.bundleDir', { level: 'critical' }),
+ unusedFromRoot('optimize.viewCaching', { level: 'critical' }),
+ unusedFromRoot('optimize.watch', { level: 'critical' }),
+ unusedFromRoot('optimize.watchPort', { level: 'critical' }),
+ unusedFromRoot('optimize.watchHost', { level: 'critical' }),
+ unusedFromRoot('optimize.watchPrebuild', { level: 'critical' }),
+ unusedFromRoot('optimize.watchProxyTimeout', { level: 'critical' }),
+ unusedFromRoot('optimize.useBundleCache', { level: 'critical' }),
+ unusedFromRoot('optimize.sourceMaps', { level: 'critical' }),
+ unusedFromRoot('optimize.workers', { level: 'critical' }),
+ unusedFromRoot('optimize.profile', { level: 'critical' }),
+ unusedFromRoot('optimize.validateSyntaxOfNodeModules', { level: 'critical' }),
+ renameFromRoot('xpack.xpack_main.telemetry.config', 'telemetry.config', { level: 'critical' }),
+ renameFromRoot('xpack.xpack_main.telemetry.url', 'telemetry.url', { level: 'critical' }),
+ renameFromRoot('xpack.xpack_main.telemetry.enabled', 'telemetry.enabled', { level: 'critical' }),
+ renameFromRoot('xpack.telemetry.enabled', 'telemetry.enabled', { level: 'critical' }),
+ renameFromRoot('xpack.telemetry.config', 'telemetry.config', { level: 'critical' }),
+ renameFromRoot('xpack.telemetry.banner', 'telemetry.banner', { level: 'critical' }),
+ renameFromRoot('xpack.telemetry.url', 'telemetry.url', { level: 'critical' }),
+ renameFromRoot('cpu.cgroup.path.override', 'ops.cGroupOverrides.cpuPath', { level: 'critical' }),
+ renameFromRoot('cpuacct.cgroup.path.override', 'ops.cGroupOverrides.cpuAcctPath', {
+ level: 'critical',
+ }),
+ renameFromRoot('server.xsrf.whitelist', 'server.xsrf.allowlist', { level: 'critical' }),
+ unusedFromRoot('elasticsearch.preserveHost', { level: 'critical' }),
+ unusedFromRoot('elasticsearch.startupTimeout', { level: 'critical' }),
rewriteCorsSettings,
configPathDeprecation,
kibanaPathConf,
diff --git a/src/core/server/kibana_config.ts b/src/core/server/kibana_config.ts
index 859f25d7082f1..a0476a54ae744 100644
--- a/src/core/server/kibana_config.ts
+++ b/src/core/server/kibana_config.ts
@@ -18,6 +18,7 @@ const deprecations: ConfigDeprecationProvider = () => [
if (kibana?.index) {
addDeprecation({
configPath: 'kibana.index',
+ level: 'critical',
title: i18n.translate('core.kibana.index.deprecationTitle', {
defaultMessage: `Setting "kibana.index" is deprecated`,
}),
diff --git a/src/plugins/home/server/index.ts b/src/plugins/home/server/index.ts
index 9523766596fed..db72af9d78a39 100644
--- a/src/plugins/home/server/index.ts
+++ b/src/plugins/home/server/index.ts
@@ -19,7 +19,9 @@ export const config: PluginConfigDescriptor = {
},
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
- renameFromRoot('kibana.disableWelcomeScreen', 'home.disableWelcomeScreen'),
+ renameFromRoot('kibana.disableWelcomeScreen', 'home.disableWelcomeScreen', {
+ level: 'critical',
+ }),
],
};
diff --git a/src/plugins/newsfeed/server/index.ts b/src/plugins/newsfeed/server/index.ts
index 460d48622af69..30bd27f027ccd 100644
--- a/src/plugins/newsfeed/server/index.ts
+++ b/src/plugins/newsfeed/server/index.ts
@@ -17,7 +17,7 @@ export const config: PluginConfigDescriptor = {
mainInterval: true,
fetchInterval: true,
},
- deprecations: ({ unused }) => [unused('defaultLanguage')],
+ deprecations: ({ unused }) => [unused('defaultLanguage', { level: 'critical' })],
};
export function plugin() {
diff --git a/src/plugins/telemetry/server/config/deprecations.test.ts b/src/plugins/telemetry/server/config/deprecations.test.ts
index 567ef69e8991c..1243ae6712057 100644
--- a/src/plugins/telemetry/server/config/deprecations.test.ts
+++ b/src/plugins/telemetry/server/config/deprecations.test.ts
@@ -165,6 +165,7 @@ describe('deprecateEndpointConfigs', () => {
"To send usage to the staging endpoint add \\"telemetry.sendUsageTo: staging\\" to the Kibana configuration.",
],
},
+ "level": "critical",
"message": "\\"telemetry.url\\" has been deprecated. Set \\"telemetry.sendUsageTo: staging\\" to the Kibana configurations to send usage to the staging endpoint.",
"title": "Setting \\"telemetry.url\\" is deprecated",
},
@@ -188,6 +189,7 @@ describe('deprecateEndpointConfigs', () => {
"To send usage to the staging endpoint add \\"telemetry.sendUsageTo: staging\\" to the Kibana configuration.",
],
},
+ "level": "critical",
"message": "\\"telemetry.optInStatusUrl\\" has been deprecated. Set \\"telemetry.sendUsageTo: staging\\" to the Kibana configurations to send usage to the staging endpoint.",
"title": "Setting \\"telemetry.optInStatusUrl\\" is deprecated",
},
diff --git a/src/plugins/telemetry/server/config/deprecations.ts b/src/plugins/telemetry/server/config/deprecations.ts
index 38553be7d5774..fa939482a5f57 100644
--- a/src/plugins/telemetry/server/config/deprecations.ts
+++ b/src/plugins/telemetry/server/config/deprecations.ts
@@ -36,6 +36,7 @@ export const deprecateEndpointConfigs: ConfigDeprecation = (
addDeprecation({
configPath: fullConfigPath,
+ level: 'critical',
title: i18n.translate('telemetry.endpointConfigs.deprecationTitle', {
defaultMessage: 'Setting "{configPath}" is deprecated',
values: { configPath: fullConfigPath },
diff --git a/src/plugins/usage_collection/server/config.ts b/src/plugins/usage_collection/server/config.ts
index faf8ce7535e8a..0c52f4525c134 100644
--- a/src/plugins/usage_collection/server/config.ts
+++ b/src/plugins/usage_collection/server/config.ts
@@ -30,10 +30,16 @@ export type ConfigType = TypeOf;
export const config: PluginConfigDescriptor = {
schema: configSchema,
deprecations: ({ renameFromRoot }) => [
- renameFromRoot('ui_metric.enabled', 'usageCollection.uiCounters.enabled'),
- renameFromRoot('ui_metric.debug', 'usageCollection.uiCounters.debug'),
- renameFromRoot('usageCollection.uiMetric.enabled', 'usageCollection.uiCounters.enabled'),
- renameFromRoot('usageCollection.uiMetric.debug', 'usageCollection.uiCounters.debug'),
+ renameFromRoot('ui_metric.enabled', 'usageCollection.uiCounters.enabled', {
+ level: 'critical',
+ }),
+ renameFromRoot('ui_metric.debug', 'usageCollection.uiCounters.debug', { level: 'critical' }),
+ renameFromRoot('usageCollection.uiMetric.enabled', 'usageCollection.uiCounters.enabled', {
+ level: 'critical',
+ }),
+ renameFromRoot('usageCollection.uiMetric.debug', 'usageCollection.uiCounters.debug', {
+ level: 'critical',
+ }),
],
exposeToBrowser: {
uiCounters: true,
diff --git a/x-pack/plugins/licensing/server/licensing_config.ts b/x-pack/plugins/licensing/server/licensing_config.ts
index a27eaba56df50..485351ec22048 100644
--- a/x-pack/plugins/licensing/server/licensing_config.ts
+++ b/x-pack/plugins/licensing/server/licensing_config.ts
@@ -21,7 +21,8 @@ export const config: PluginConfigDescriptor = {
deprecations: ({ renameFromRoot }) => [
renameFromRoot(
'xpack.xpack_main.xpack_api_polling_frequency_millis',
- 'xpack.licensing.api_polling_frequency'
+ 'xpack.licensing.api_polling_frequency',
+ { level: 'critical' }
),
],
};
From de80e69b272cfd45e4b0afa1d9467d497481dd08 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 09:56:00 -0400
Subject: [PATCH 13/36] [Security Solution] Generate host isolation exceptions
artifact (#115160) (#115512)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Esteban Beltran
---
.../server/endpoint/lib/artifacts/common.ts | 3 +
.../server/endpoint/lib/artifacts/lists.ts | 20 +++
.../manifest_manager/manifest_manager.test.ts | 137 ++++++++++--------
.../manifest_manager/manifest_manager.ts | 42 ++++++
4 files changed, 140 insertions(+), 62 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts
index af5e386464305..60f91330d4558 100644
--- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts
@@ -25,6 +25,9 @@ export const ArtifactConstants = {
SUPPORTED_EVENT_FILTERS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'],
GLOBAL_EVENT_FILTERS_NAME: 'endpoint-eventfilterlist',
+
+ SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'],
+ GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME: 'endpoint-hostisolationexceptionlist',
};
export const ManifestConstants = {
diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts
index e27a09efd9710..e26a2c7f4b4bc 100644
--- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts
@@ -15,6 +15,7 @@ import { validate } from '@kbn/securitysolution-io-ts-utils';
import {
ENDPOINT_EVENT_FILTERS_LIST_ID,
+ ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
ENDPOINT_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
@@ -65,6 +66,7 @@ export async function getFilteredEndpointExceptionList(
| typeof ENDPOINT_LIST_ID
| typeof ENDPOINT_TRUSTED_APPS_LIST_ID
| typeof ENDPOINT_EVENT_FILTERS_LIST_ID
+ | typeof ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID
): Promise {
const exceptions: WrappedTranslatedExceptionList = { entries: [] };
let page = 1;
@@ -148,6 +150,24 @@ export async function getEndpointEventFiltersList(
);
}
+export async function getHostIsolationExceptionsList(
+ eClient: ExceptionListClient,
+ schemaVersion: string,
+ os: string,
+ policyId?: string
+): Promise {
+ const osFilter = `exception-list-agnostic.attributes.os_types:\"${os}\"`;
+ const policyFilter = `(exception-list-agnostic.attributes.tags:\"policy:all\"${
+ policyId ? ` or exception-list-agnostic.attributes.tags:\"policy:${policyId}\"` : ''
+ })`;
+
+ return getFilteredEndpointExceptionList(
+ eClient,
+ schemaVersion,
+ `${osFilter} and ${policyFilter}`,
+ ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID
+ );
+}
/**
* Translates Exception list items to Exceptions the endpoint can understand
* @param exceptions
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts
index d75e347b86bd5..0ef2abd5f50aa 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts
@@ -7,6 +7,7 @@
import { savedObjectsClientMock } from 'src/core/server/mocks';
import {
+ ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
ENDPOINT_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
@@ -66,6 +67,12 @@ describe('ManifestManager', () => {
const ARTIFACT_NAME_EVENT_FILTERS_MACOS = 'endpoint-eventfilterlist-macos-v1';
const ARTIFACT_NAME_EVENT_FILTERS_WINDOWS = 'endpoint-eventfilterlist-windows-v1';
const ARTIFACT_NAME_EVENT_FILTERS_LINUX = 'endpoint-eventfilterlist-linux-v1';
+ const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS =
+ 'endpoint-hostisolationexceptionlist-macos-v1';
+ const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS =
+ 'endpoint-hostisolationexceptionlist-windows-v1';
+ const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX =
+ 'endpoint-hostisolationexceptionlist-linux-v1';
let ARTIFACTS: InternalArtifactCompleteSchema[] = [];
let ARTIFACTS_BY_ID: { [K: string]: InternalArtifactCompleteSchema } = {};
@@ -157,31 +164,29 @@ describe('ManifestManager', () => {
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);
- savedObjectsClient.get = jest
- .fn()
- .mockImplementation(async (objectType: string, id: string) => {
- if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
- return {
- attributes: {
- created: '20-01-2020 10:00:00.000Z',
- schemaVersion: 'v2',
- semanticVersion: '1.0.0',
- artifacts: [
- { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
- ],
- },
- version: '2.0.0',
- };
- } else {
- return null;
- }
- });
+ savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
+ if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
+ return {
+ attributes: {
+ created: '20-01-2020 10:00:00.000Z',
+ schemaVersion: 'v2',
+ semanticVersion: '1.0.0',
+ artifacts: [
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
+ ],
+ },
+ version: '2.0.0',
+ };
+ } else {
+ return null;
+ }
+ });
(
manifestManagerContext.artifactClient as jest.Mocked
@@ -218,31 +223,29 @@ describe('ManifestManager', () => {
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);
- savedObjectsClient.get = jest
- .fn()
- .mockImplementation(async (objectType: string, id: string) => {
- if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
- return {
- attributes: {
- created: '20-01-2020 10:00:00.000Z',
- schemaVersion: 'v2',
- semanticVersion: '1.0.0',
- artifacts: [
- { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
- { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
- { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
- ],
- },
- version: '2.0.0',
- };
- } else {
- return null;
- }
- });
+ savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
+ if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
+ return {
+ attributes: {
+ created: '20-01-2020 10:00:00.000Z',
+ schemaVersion: 'v2',
+ semanticVersion: '1.0.0',
+ artifacts: [
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
+ { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
+ { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
+ ],
+ },
+ version: '2.0.0',
+ };
+ } else {
+ return null;
+ }
+ });
(
manifestManagerContext.artifactClient as jest.Mocked
@@ -278,6 +281,9 @@ describe('ManifestManager', () => {
ARTIFACT_NAME_EVENT_FILTERS_MACOS,
ARTIFACT_NAME_EVENT_FILTERS_WINDOWS,
ARTIFACT_NAME_EVENT_FILTERS_LINUX,
+ ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS,
+ ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS,
+ ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX,
];
const getArtifactIds = (artifacts: InternalArtifactSchema[]) => [
@@ -310,7 +316,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const manifest = await manifestManager.buildNewManifest();
@@ -321,7 +327,7 @@ describe('ManifestManager', () => {
const artifacts = manifest.getAllArtifacts();
- expect(artifacts.length).toBe(9);
+ expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
for (const artifact of artifacts) {
@@ -336,16 +342,18 @@ describe('ManifestManager', () => {
test('Builds fully new manifest if no baseline parameter passed and present exception list items', async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'] });
+ const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({ os_types: ['linux'] });
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
[ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] },
+ [ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] },
});
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
@@ -358,7 +366,7 @@ describe('ManifestManager', () => {
const artifacts = manifest.getAllArtifacts();
- expect(artifacts.length).toBe(9);
+ expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
expect(getArtifactObject(artifacts[0])).toStrictEqual({
@@ -374,6 +382,11 @@ describe('ManifestManager', () => {
expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] });
+ expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] });
+ expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] });
+ expect(getArtifactObject(artifacts[11])).toStrictEqual({
+ entries: translateToEndpointExceptions([hostIsolationExceptionsItem], 'v1'),
+ });
for (const artifact of artifacts) {
expect(manifest.isDefaultArtifact(artifact)).toBe(true);
@@ -395,7 +408,7 @@ describe('ManifestManager', () => {
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const oldManifest = await manifestManager.buildNewManifest();
@@ -413,7 +426,7 @@ describe('ManifestManager', () => {
const artifacts = manifest.getAllArtifacts();
- expect(artifacts.length).toBe(9);
+ expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
expect(artifacts[0]).toStrictEqual(oldManifest.getAllArtifacts()[0]);
@@ -462,7 +475,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
@@ -474,7 +487,7 @@ describe('ManifestManager', () => {
const artifacts = manifest.getAllArtifacts();
- expect(artifacts.length).toBe(10);
+ expect(artifacts.length).toBe(13);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
expect(getArtifactObject(artifacts[0])).toStrictEqual({
@@ -653,7 +666,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => object);
+ .mockImplementation((_type: string, object: InternalManifestSchema) => object);
await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();
@@ -690,7 +703,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.update = jest
.fn()
- .mockImplementation((type: string, id: string, object: InternalManifestSchema) => object);
+ .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object);
await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();
@@ -1023,7 +1036,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const manifest = await manifestManager.buildNewManifest();
@@ -1046,7 +1059,7 @@ describe('ManifestManager', () => {
context.savedObjectsClient.create = jest
.fn()
- .mockImplementation((type: string, object: InternalManifestSchema) => ({
+ .mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
index 5c1d327b1b892..736bf1c58cb90 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
@@ -26,6 +26,7 @@ import {
getEndpointEventFiltersList,
getEndpointExceptionList,
getEndpointTrustedAppsList,
+ getHostIsolationExceptionsList,
Manifest,
} from '../../../lib/artifacts';
import {
@@ -237,6 +238,46 @@ export class ManifestManager {
);
}
+ protected async buildHostIsolationExceptionsArtifacts(): Promise {
+ const defaultArtifacts: InternalArtifactCompleteSchema[] = [];
+ const policySpecificArtifacts: Record = {};
+
+ for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) {
+ defaultArtifacts.push(await this.buildHostIsolationExceptionForOs(os));
+ }
+
+ await iterateAllListItems(
+ (page) => this.listEndpointPolicyIds(page),
+ async (policyId) => {
+ for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) {
+ policySpecificArtifacts[policyId] = policySpecificArtifacts[policyId] || [];
+ policySpecificArtifacts[policyId].push(
+ await this.buildHostIsolationExceptionForOs(os, policyId)
+ );
+ }
+ }
+ );
+
+ return { defaultArtifacts, policySpecificArtifacts };
+ }
+
+ protected async buildHostIsolationExceptionForOs(
+ os: string,
+ policyId?: string
+ ): Promise {
+ return buildArtifact(
+ await getHostIsolationExceptionsList(
+ this.exceptionListClient,
+ this.schemaVersion,
+ os,
+ policyId
+ ),
+ this.schemaVersion,
+ os,
+ ArtifactConstants.GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME
+ );
+ }
+
/**
* Writes new artifact SO.
*
@@ -381,6 +422,7 @@ export class ManifestManager {
this.buildExceptionListArtifacts(),
this.buildTrustedAppsArtifacts(),
this.buildEventFiltersArtifacts(),
+ this.buildHostIsolationExceptionsArtifacts(),
]);
const manifest = new Manifest({
From 8e6ba2736d1c78460864e7e408d1597b8f82de58 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 10:17:41 -0400
Subject: [PATCH 14/36] Update heading on telemetry management section.
(#115425) (#115508)
Co-authored-by: Pete Hampton
---
.../telemetry_management_section.test.tsx.snap | 4 ++--
.../public/components/telemetry_management_section.tsx | 8 ++++----
x-pack/plugins/translations/translations/ja-JP.json | 2 --
x-pack/plugins/translations/translations/zh-CN.json | 2 --
4 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
index 758ecf54f4bf0..72947b1514911 100644
--- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
+++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
@@ -53,7 +53,7 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = `
loading={false}
setting={
Object {
- "ariaName": "Provide usage statistics",
+ "ariaName": "Provide usage data",
"category": Array [],
"defVal": true,
"description":
@@ -109,7 +109,7 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = `
/>
,
- "displayName": "Provide usage statistics",
+ "displayName": "Provide usage data",
"isCustom": true,
"isOverridden": false,
"name": "telemetry:enabled",
diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
index 3686cb10706bf..037603cb165d9 100644
--- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
+++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
@@ -116,14 +116,14 @@ export class TelemetryManagementSection extends Component {
setting={{
type: 'boolean',
name: 'telemetry:enabled',
- displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', {
- defaultMessage: 'Provide usage statistics',
+ displayName: i18n.translate('telemetry.provideUsageDataTitle', {
+ defaultMessage: 'Provide usage data',
}),
value: enabled,
description: this.renderDescription(),
defVal: true,
- ariaName: i18n.translate('telemetry.provideUsageStatisticsAriaName', {
- defaultMessage: 'Provide usage statistics',
+ ariaName: i18n.translate('telemetry.provideUsageDataAriaName', {
+ defaultMessage: 'Provide usage data',
}),
requiresPageReload: false,
category: [],
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index f86167046c4c2..ea98d0554d58b 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -4605,8 +4605,6 @@
"telemetry.optInNoticeSeenErrorToastText": "通知の消去中にエラーが発生しました",
"telemetry.optInSuccessOff": "使用状況データ収集がオフです。",
"telemetry.optInSuccessOn": "使用状況データ収集がオンです。",
- "telemetry.provideUsageStatisticsAriaName": "使用統計を提供",
- "telemetry.provideUsageStatisticsTitle": "使用統計を提供",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "プライバシーポリシー",
"telemetry.securityData": "Endpoint Security データ",
"telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 6cfdc69c9e897..2c8bc37c32208 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -4651,8 +4651,6 @@
"telemetry.optInNoticeSeenErrorToastText": "关闭声明时发生错误",
"telemetry.optInSuccessOff": "使用情况数据收集已关闭。",
"telemetry.optInSuccessOn": "使用情况数据收集已打开。",
- "telemetry.provideUsageStatisticsAriaName": "提供使用情况统计",
- "telemetry.provideUsageStatisticsTitle": "提供使用情况统计",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "隐私声明",
"telemetry.securityData": "终端安全数据",
"telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用使用情况数据收集可帮助我们管理并改善产品和服务。有关更多详情,请参阅我们的{privacyStatementLink}。",
From 00c6f114fc1ff0c05172f7b5d6dbf8c42f3b77f2 Mon Sep 17 00:00:00 2001
From: Tre
Date: Tue, 19 Oct 2021 10:42:52 -0400
Subject: [PATCH 15/36] [QA][refactor] Use ui settings - sample data (#114530)
(#115549)
# Conflicts:
# test/functional/page_objects/common_page.ts
---
test/functional/apps/home/_sample_data.ts | 21 ++++++++-----------
test/functional/page_objects/common_page.ts | 23 +++++++++++++++++----
2 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts
index 3cf387133bc9c..e0a96940337e2 100644
--- a/test/functional/apps/home/_sample_data.ts
+++ b/test/functional/apps/home/_sample_data.ts
@@ -31,6 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
after(async () => {
await security.testUser.restoreDefaults();
+ await PageObjects.common.unsetTime();
});
it('should display registered flights sample data sets', async () => {
@@ -74,6 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('dashboard', () => {
beforeEach(async () => {
+ await time();
await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
useActualUrl: true,
});
@@ -84,10 +86,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.home.launchSampleDashboard('flights');
await PageObjects.header.waitUntilLoadingHasFinished();
await renderable.waitForRender();
- const todayYearMonthDay = moment().format('MMM D, YYYY');
- const fromTime = `${todayYearMonthDay} @ 00:00:00.000`;
- const toTime = `${todayYearMonthDay} @ 23:59:59.999`;
- await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(17);
});
@@ -112,10 +110,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.home.launchSampleDashboard('logs');
await PageObjects.header.waitUntilLoadingHasFinished();
await renderable.waitForRender();
- const todayYearMonthDay = moment().format('MMM D, YYYY');
- const fromTime = `${todayYearMonthDay} @ 00:00:00.000`;
- const toTime = `${todayYearMonthDay} @ 23:59:59.999`;
- await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(13);
});
@@ -124,10 +118,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.home.launchSampleDashboard('ecommerce');
await PageObjects.header.waitUntilLoadingHasFinished();
await renderable.waitForRender();
- const todayYearMonthDay = moment().format('MMM D, YYYY');
- const fromTime = `${todayYearMonthDay} @ 00:00:00.000`;
- const toTime = `${todayYearMonthDay} @ 23:59:59.999`;
- await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(15);
});
@@ -160,5 +150,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(isInstalled).to.be(false);
});
});
+
+ async function time() {
+ const today = moment().format('MMM D, YYYY');
+ const from = `${today} @ 00:00:00.000`;
+ const to = `${today} @ 23:59:59.999`;
+ await PageObjects.common.setTime({ from, to });
+ }
});
}
diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts
index 03e49f1642f55..5e5c3abcb9607 100644
--- a/test/functional/page_objects/common_page.ts
+++ b/test/functional/page_objects/common_page.ts
@@ -30,7 +30,8 @@ export class CommonPageObject extends FtrService {
private readonly find = this.ctx.getService('find');
private readonly globalNav = this.ctx.getService('globalNav');
private readonly testSubjects = this.ctx.getService('testSubjects');
- private readonly login = this.ctx.getPageObject('login');
+ private readonly loginPage = this.ctx.getPageObject('login');
+ private readonly kibanaServer = this.ctx.getService('kibanaServer');
private readonly defaultTryTimeout = this.config.get('timeouts.try');
private readonly defaultFindTimeout = this.config.get('timeouts.find');
@@ -60,12 +61,12 @@ export class CommonPageObject extends FtrService {
if (loginPage && !wantedLoginPage) {
this.log.debug('Found login page');
if (this.config.get('security.disableTestUser')) {
- await this.login.login(
+ await this.loginPage.login(
this.config.get('servers.kibana.username'),
this.config.get('servers.kibana.password')
);
} else {
- await this.login.login('test_user', 'changeme');
+ await this.loginPage.login('test_user', 'changeme');
}
if (appUrl.includes('/status')) {
@@ -344,6 +345,12 @@ export class CommonPageObject extends FtrService {
await this.browser.pressKeys(this.browser.keys.TAB);
}
+ // Pause the browser at a certain place for debugging
+ // Not meant for usage in CI, only for dev-usage
+ async pause() {
+ return this.browser.pause();
+ }
+
/**
* Clicks cancel button on modal
* @param overlayWillStay pass in true if your test will show multiple modals in succession
@@ -402,7 +409,7 @@ export class CommonPageObject extends FtrService {
const toastShown = await this.find.existsByCssSelector('.euiToast');
if (toastShown) {
try {
- await this.closeToast();
+ await this.find.clickByCssSelector('.euiToast__closeButton');
} catch (err) {
// ignore errors, toast clear themselves after timeout
}
@@ -495,4 +502,12 @@ export class CommonPageObject extends FtrService {
await this.testSubjects.exists(validator);
}
}
+
+ async setTime(time: { from: string; to: string }) {
+ await this.kibanaServer.uiSettings.replace({ 'timepicker:timeDefaults': JSON.stringify(time) });
+ }
+
+ async unsetTime() {
+ await this.kibanaServer.uiSettings.unset('timepicker:timeDefaults');
+ }
}
From 56d1c9b7c98d39642c5d1ccd30743342090d9221 Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
<54894989+DziyanaDzeraviankina@users.noreply.github.com>
Date: Tue, 19 Oct 2021 17:43:28 +0300
Subject: [PATCH 16/36] Respect external URL allow list in TSVB (#114093)
(#115550)
* Respect external URL allow list in TSVB
* Remove showExternalUrlErrorModal and onContextMenu handler for table
* Update modal message
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../lib/external_url_error_modal.tsx | 60 +++++++++++++++++++
.../components/vis_types/table/vis.js | 57 ++++++++++++++----
.../components/vis_types/top_n/vis.js | 21 ++++++-
3 files changed, 125 insertions(+), 13 deletions(-)
create mode 100644 src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx
diff --git a/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx
new file mode 100644
index 0000000000000..ebb806387d9cf
--- /dev/null
+++ b/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiButton,
+ EuiModal,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiTextColor,
+} from '@elastic/eui';
+
+interface ExternalUrlErrorModalProps {
+ url: string;
+ handleClose: () => void;
+}
+
+export const ExternalUrlErrorModal = ({ url, handleClose }: ExternalUrlErrorModalProps) => (
+
+
+
+
+
+
+
+
+ {url}
+
+ ),
+ externalUrlPolicy: 'externalUrl.policy',
+ kibanaConfigFileName: 'kibana.yml',
+ }}
+ />
+
+
+
+
+
+
+
+);
diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js
index 7b1db4b362647..b3a48a997b301 100644
--- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js
+++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js
@@ -17,6 +17,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter';
import { isSortable } from './is_sortable';
import { EuiToolTip, EuiIcon } from '@elastic/eui';
import { replaceVars } from '../../lib/replace_vars';
+import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal';
import { FIELD_FORMAT_IDS } from '../../../../../../../../plugins/field_formats/common';
import { FormattedMessage } from '@kbn/i18n/react';
import { getFieldFormats, getCoreStart } from '../../../../services';
@@ -53,12 +54,26 @@ class TableVis extends Component {
const DateFormat = fieldFormatsService.getType(FIELD_FORMAT_IDS.DATE);
this.dateFormatter = new DateFormat({}, this.props.getConfig);
+
+ this.state = {
+ accessDeniedDrilldownUrl: null,
+ };
}
get visibleSeries() {
return get(this.props, 'model.series', []).filter((series) => !series.hidden);
}
+ createDrilldownUrlClickHandler = (url) => (event) => {
+ const validatedUrl = getCoreStart().http.externalUrl.validateUrl(url);
+ if (validatedUrl) {
+ this.setState({ accessDeniedDrilldownUrl: null });
+ } else {
+ event.preventDefault();
+ this.setState({ accessDeniedDrilldownUrl: url });
+ }
+ };
+
renderRow = (row) => {
const { model, fieldFormatMap, getConfig } = this.props;
@@ -74,7 +89,16 @@ class TableVis extends Component {
if (model.drilldown_url) {
const url = replaceVars(model.drilldown_url, {}, { key: row.key });
- rowDisplay = {rowDisplay};
+ const handleDrilldownUrlClick = this.createDrilldownUrlClickHandler(url);
+ rowDisplay = (
+
+ {rowDisplay}
+
+ );
}
const columns = row.series
@@ -213,8 +237,11 @@ class TableVis extends Component {
);
}
+ closeExternalUrlErrorModal = () => this.setState({ accessDeniedDrilldownUrl: null });
+
render() {
const { visData, model } = this.props;
+ const { accessDeniedDrilldownUrl } = this.state;
const header = this.renderHeader();
let rows;
@@ -239,16 +266,24 @@ class TableVis extends Component {
);
}
return (
-
-
-
+ <>
+
+
+
+ {accessDeniedDrilldownUrl && (
+
+ )}
+ >
);
}
}
diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js
index 8176f6ece2805..5eb850a753384 100644
--- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js
+++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js
@@ -15,10 +15,11 @@ import { getLastValue } from '../../../../../common/last_value_utils';
import { isBackgroundInverted } from '../../../lib/set_is_reversed';
import { replaceVars } from '../../lib/replace_vars';
import PropTypes from 'prop-types';
-import React from 'react';
+import React, { useState, useCallback } from 'react';
import { sortBy, first, get } from 'lodash';
import { DATA_FORMATTERS } from '../../../../../common/enums';
import { getOperator, shouldOperate } from '../../../../../common/operators_utils';
+import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal';
function sortByDirection(data, direction, fn) {
if (direction === 'desc') {
@@ -41,6 +42,8 @@ function sortSeries(visData, model) {
}
function TopNVisualization(props) {
+ const [accessDeniedDrilldownUrl, setAccessDeniedDrilldownUrl] = useState(null);
+ const coreStart = getCoreStart();
const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props;
const series = sortSeries(visData, model).map((item) => {
@@ -83,13 +86,27 @@ function TopNVisualization(props) {
if (model.drilldown_url) {
params.onClick = (item) => {
const url = replaceVars(model.drilldown_url, {}, { key: item.label });
- getCoreStart().application.navigateToUrl(url);
+ const validatedUrl = coreStart.http.externalUrl.validateUrl(url);
+ if (validatedUrl) {
+ setAccessDeniedDrilldownUrl(null);
+ coreStart.application.navigateToUrl(url);
+ } else {
+ setAccessDeniedDrilldownUrl(url);
+ }
};
}
+ const closeExternalUrlErrorModal = useCallback(() => setAccessDeniedDrilldownUrl(null), []);
+
return (
+ {accessDeniedDrilldownUrl && (
+
+ )}
);
}
From b4f1db42d1e835be5e7a134c31a874d984d680bc Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:19:27 -0400
Subject: [PATCH 17/36] Fixes console errors seen (#115448) (#115494)
## Summary
During testing I encountered this error message:
```
[2021-10-18T13:19:07.053-06:00][ERROR][plugins.securitySolution] The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-null "to": 2021-10-18T19:19:00.835Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected.
```
This error was happening whenever I had a rule that was using an immediately invoked action and was encountering an error such as a non ECS compliant signal. The root cause is that I was not checking everywhere to ensure we had a throttle rule to ensure scheduling.
This fixes that by adding an `if` statement/guard around the areas of code.
I also improve the error message by adding which ruleId the error is coming from.
### Checklist
- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
Co-authored-by: Frank Hassanabad
---
...dule_throttle_notification_actions.test.ts | 2 +-
.../schedule_throttle_notification_actions.ts | 1 +
.../create_security_rule_type_wrapper.ts | 60 +++++++++--------
.../legacy_notifications/one_action.json | 2 +-
.../signals/signal_rule_alert_type.test.ts | 64 ++++++++++++++++++-
.../signals/signal_rule_alert_type.ts | 61 +++++++++---------
6 files changed, 129 insertions(+), 61 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts
index 81f229c636bd8..964df3c91eb08 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts
@@ -278,7 +278,7 @@ describe('schedule_throttle_notification_actions', () => {
});
expect(logger.error).toHaveBeenCalledWith(
- 'The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-invalid "to": 2021-08-24T19:19:22.094Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected.'
+ 'The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-invalid "to": 2021-08-24T19:19:22.094Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected. Check your rule with ruleId: "rule-123" for data integrity issues'
);
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts
index 5bf18496e6375..7b4b314cc8911 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts
@@ -145,6 +145,7 @@ export const scheduleThrottledNotificationActions = async ({
` "from": now-${throttle}`,
` "to": ${startedAt.toISOString()}.`,
' This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected.',
+ ` Check your rule with ruleId: "${ruleId}" for data integrity issues`,
].join('')
);
}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts
index 0ad416e86e31a..0fe7cbdc9bd9f 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts
@@ -375,20 +375,22 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
);
} else {
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
- await scheduleThrottledNotificationActions({
- alertInstance: services.alertInstanceFactory(alertId),
- throttle: ruleSO.attributes.throttle,
- startedAt,
- id: ruleSO.id,
- kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
- ?.kibana_siem_app_url,
- outputIndex: ruleDataClient.indexName,
- ruleId,
- esClient: services.scopedClusterClient.asCurrentUser,
- notificationRuleParams,
- signals: result.createdSignals,
- logger,
- });
+ if (ruleSO.attributes.throttle != null) {
+ await scheduleThrottledNotificationActions({
+ alertInstance: services.alertInstanceFactory(alertId),
+ throttle: ruleSO.attributes.throttle,
+ startedAt,
+ id: ruleSO.id,
+ kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
+ ?.kibana_siem_app_url,
+ outputIndex: ruleDataClient.indexName,
+ ruleId,
+ esClient: services.scopedClusterClient.asCurrentUser,
+ notificationRuleParams,
+ signals: result.createdSignals,
+ logger,
+ });
+ }
const errorMessage = buildRuleMessage(
'Bulk Indexing of signals failed:',
truncateMessageList(result.errors).join()
@@ -407,20 +409,22 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
}
} catch (error) {
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
- await scheduleThrottledNotificationActions({
- alertInstance: services.alertInstanceFactory(alertId),
- throttle: ruleSO.attributes.throttle,
- startedAt,
- id: ruleSO.id,
- kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
- ?.kibana_siem_app_url,
- outputIndex: ruleDataClient.indexName,
- ruleId,
- esClient: services.scopedClusterClient.asCurrentUser,
- notificationRuleParams,
- signals: result.createdSignals,
- logger,
- });
+ if (ruleSO.attributes.throttle != null) {
+ await scheduleThrottledNotificationActions({
+ alertInstance: services.alertInstanceFactory(alertId),
+ throttle: ruleSO.attributes.throttle,
+ startedAt,
+ id: ruleSO.id,
+ kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
+ ?.kibana_siem_app_url,
+ outputIndex: ruleDataClient.indexName,
+ ruleId,
+ esClient: services.scopedClusterClient.asCurrentUser,
+ notificationRuleParams,
+ signals: result.createdSignals,
+ logger,
+ });
+ }
const errorMessage = error.message ?? '(no error message given)';
const message = buildRuleMessage(
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json
index 1966dcf5ff53c..bf980e370e3a3 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json
@@ -3,7 +3,7 @@
"interval": "1m",
"actions": [
{
- "id": "42534430-2092-11ec-99a6-05d79563c01a",
+ "id": "1fa31c30-3046-11ec-8971-1f3f7bae65af",
"group": "default",
"params": {
"message": "Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts"
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
index 88b276358a705..6a84776ccee5d 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
@@ -536,14 +536,74 @@ describe('signal_rule_alert_type', () => {
errors: ['Error that bubbled up.'],
};
(queryExecutor as jest.Mock).mockResolvedValue(result);
- await alert.executor(payload);
+ const ruleAlert = getAlertMock(false, getQueryRuleParams());
+ ruleAlert.throttle = '1h';
+ const payLoadWithThrottle = getPayload(
+ ruleAlert,
+ alertServices
+ ) as jest.Mocked;
+ payLoadWithThrottle.rule.throttle = '1h';
+ alertServices.savedObjectsClient.get.mockResolvedValue({
+ id: 'id',
+ type: 'type',
+ references: [],
+ attributes: ruleAlert,
+ });
+ await alert.executor(payLoadWithThrottle);
expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(1);
});
+ it('should NOT call scheduleThrottledNotificationActions if result is false and the throttle is not set', async () => {
+ const result: SearchAfterAndBulkCreateReturnType = {
+ success: false,
+ warning: false,
+ searchAfterTimes: [],
+ bulkCreateTimes: [],
+ lastLookBackDate: null,
+ createdSignalsCount: 0,
+ createdSignals: [],
+ warningMessages: [],
+ errors: ['Error that bubbled up.'],
+ };
+ (queryExecutor as jest.Mock).mockResolvedValue(result);
+ await alert.executor(payload);
+ expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(0);
+ });
+
it('should call scheduleThrottledNotificationActions if an error was thrown to prevent the throttle from being reset', async () => {
(queryExecutor as jest.Mock).mockRejectedValue({});
- await alert.executor(payload);
+ const ruleAlert = getAlertMock(false, getQueryRuleParams());
+ ruleAlert.throttle = '1h';
+ const payLoadWithThrottle = getPayload(
+ ruleAlert,
+ alertServices
+ ) as jest.Mocked;
+ payLoadWithThrottle.rule.throttle = '1h';
+ alertServices.savedObjectsClient.get.mockResolvedValue({
+ id: 'id',
+ type: 'type',
+ references: [],
+ attributes: ruleAlert,
+ });
+ await alert.executor(payLoadWithThrottle);
expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(1);
});
+
+ it('should NOT call scheduleThrottledNotificationActions if an error was thrown to prevent the throttle from being reset if throttle is not defined', async () => {
+ const result: SearchAfterAndBulkCreateReturnType = {
+ success: false,
+ warning: false,
+ searchAfterTimes: [],
+ bulkCreateTimes: [],
+ lastLookBackDate: null,
+ createdSignalsCount: 0,
+ createdSignals: [],
+ warningMessages: [],
+ errors: ['Error that bubbled up.'],
+ };
+ (queryExecutor as jest.Mock).mockRejectedValue(result);
+ await alert.executor(payload);
+ expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(0);
+ });
});
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts
index 4e98bee83aeb5..90220814fb928 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts
@@ -474,21 +474,22 @@ export const signalRulesAlertType = ({
);
} else {
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
- await scheduleThrottledNotificationActions({
- alertInstance: services.alertInstanceFactory(alertId),
- throttle: savedObject.attributes.throttle,
- startedAt,
- id: savedObject.id,
- kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
- ?.kibana_siem_app_url,
- outputIndex,
- ruleId,
- signals: result.createdSignals,
- esClient: services.scopedClusterClient.asCurrentUser,
- notificationRuleParams,
- logger,
- });
-
+ if (savedObject.attributes.throttle != null) {
+ await scheduleThrottledNotificationActions({
+ alertInstance: services.alertInstanceFactory(alertId),
+ throttle: savedObject.attributes.throttle,
+ startedAt,
+ id: savedObject.id,
+ kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
+ ?.kibana_siem_app_url,
+ outputIndex,
+ ruleId,
+ signals: result.createdSignals,
+ esClient: services.scopedClusterClient.asCurrentUser,
+ notificationRuleParams,
+ logger,
+ });
+ }
const errorMessage = buildRuleMessage(
'Bulk Indexing of signals failed:',
truncateMessageList(result.errors).join()
@@ -507,20 +508,22 @@ export const signalRulesAlertType = ({
}
} catch (error) {
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
- await scheduleThrottledNotificationActions({
- alertInstance: services.alertInstanceFactory(alertId),
- throttle: savedObject.attributes.throttle,
- startedAt,
- id: savedObject.id,
- kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
- ?.kibana_siem_app_url,
- outputIndex,
- ruleId,
- signals: result.createdSignals,
- esClient: services.scopedClusterClient.asCurrentUser,
- notificationRuleParams,
- logger,
- });
+ if (savedObject.attributes.throttle != null) {
+ await scheduleThrottledNotificationActions({
+ alertInstance: services.alertInstanceFactory(alertId),
+ throttle: savedObject.attributes.throttle,
+ startedAt,
+ id: savedObject.id,
+ kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
+ ?.kibana_siem_app_url,
+ outputIndex,
+ ruleId,
+ signals: result.createdSignals,
+ esClient: services.scopedClusterClient.asCurrentUser,
+ notificationRuleParams,
+ logger,
+ });
+ }
const errorMessage = error.message ?? '(no error message given)';
const message = buildRuleMessage(
'An error occurred during rule execution:',
From 635859f9ff519f0508189b99d946e2127741fe3a Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:19:40 -0400
Subject: [PATCH 18/36] [APM] Ensure APM deprecation documentationUrl point to
correct doc branch (#115401) (#115482)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* using branch in the url
* fixing TS
Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
---
x-pack/plugins/apm/server/deprecations/deprecations.test.ts | 5 ++++-
x-pack/plugins/apm/server/deprecations/index.ts | 5 +++--
x-pack/plugins/apm/server/plugin.ts | 2 ++
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/apm/server/deprecations/deprecations.test.ts b/x-pack/plugins/apm/server/deprecations/deprecations.test.ts
index d706146faf212..43e8140fb9b3c 100644
--- a/x-pack/plugins/apm/server/deprecations/deprecations.test.ts
+++ b/x-pack/plugins/apm/server/deprecations/deprecations.test.ts
@@ -19,7 +19,7 @@ const deprecationContext = {
describe('getDeprecations', () => {
describe('when fleet is disabled', () => {
it('returns no deprecations', async () => {
- const deprecationsCallback = getDeprecations({});
+ const deprecationsCallback = getDeprecations({ branch: 'master' });
const deprecations = await deprecationsCallback(deprecationContext);
expect(deprecations).toEqual([]);
});
@@ -28,6 +28,7 @@ describe('getDeprecations', () => {
describe('when running on cloud with legacy apm-server', () => {
it('returns deprecations', async () => {
const deprecationsCallback = getDeprecations({
+ branch: 'master',
cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup,
fleet: {
start: () => ({
@@ -43,6 +44,7 @@ describe('getDeprecations', () => {
describe('when running on cloud with fleet', () => {
it('returns no deprecations', async () => {
const deprecationsCallback = getDeprecations({
+ branch: 'master',
cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup,
fleet: {
start: () => ({
@@ -58,6 +60,7 @@ describe('getDeprecations', () => {
describe('when running on prem', () => {
it('returns no deprecations', async () => {
const deprecationsCallback = getDeprecations({
+ branch: 'master',
cloudSetup: { isCloudEnabled: false } as unknown as CloudSetup,
fleet: {
start: () => ({ agentPolicyService: { get: () => undefined } }),
diff --git a/x-pack/plugins/apm/server/deprecations/index.ts b/x-pack/plugins/apm/server/deprecations/index.ts
index b592a2bf13268..76c90270abb8f 100644
--- a/x-pack/plugins/apm/server/deprecations/index.ts
+++ b/x-pack/plugins/apm/server/deprecations/index.ts
@@ -15,9 +15,11 @@ import { APMRouteHandlerResources } from '../';
export function getDeprecations({
cloudSetup,
fleet,
+ branch,
}: {
cloudSetup?: CloudSetup;
fleet?: APMRouteHandlerResources['plugins']['fleet'];
+ branch: string;
}) {
return async ({
savedObjectsClient,
@@ -46,8 +48,7 @@ export function getDeprecations({
defaultMessage:
'Running the APM Server binary directly is considered a legacy option and is deprecated since 7.16. Switch to APM Server managed by an Elastic Agent instead. Read our documentation to learn more.',
}),
- documentationUrl:
- 'https://www.elastic.co/guide/en/apm/server/current/apm-integration.html',
+ documentationUrl: `https://www.elastic.co/guide/en/apm/server/${branch}/apm-integration.html`,
level: 'warning',
correctiveActions: {
manualSteps: [
diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts
index d2d8dbf602364..72a1bc483015e 100644
--- a/x-pack/plugins/apm/server/plugin.ts
+++ b/x-pack/plugins/apm/server/plugin.ts
@@ -215,10 +215,12 @@ export class APMPlugin
);
})();
});
+
core.deprecations.registerDeprecations({
getDeprecations: getDeprecations({
cloudSetup: plugins.cloud,
fleet: resourcePlugins.fleet,
+ branch: this.initContext.env.packageInfo.branch,
}),
});
From c63c2c51569ffa3a2e30f11eac27edc6ef429e6d Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:32:16 -0400
Subject: [PATCH 19/36] [DataTable] Add rowHeightsOptions to table (#114637)
(#115431)
* Upgraded the version of EUI to 38.2.0 from 38.0.1
* Updated the i18n mappings required for EUI v.38.2.0
* Update i18n snapshots and resolve linting error
* Removed html_id_generator mocks.
Current mock was failing due to missing useGeneratedHtmlId export. This is safe to remove because EUI contains a .testenv that contains an mock for html_id_generator. More info at https://github.com/elastic/eui/blob/master/src/services/accessibility/html_id_generator.testenv.ts
* Resolve linting error in i18n mapping file
* Removed html_id_generator mocks.
Current mock was failing due to missing useGeneratedHtmlId export. This is safe to remove because EUI contains a .testenv that contains a mock for html_id_generator. More info at https://github.com/elastic/eui/blob/master/src/services/accessibility/html_id_generator.testenv.ts
* Update plugin snapshots
* Resolve merge conflict in license_checker config.ts file
* Upgrade EUI to version 39.0.0 from the original target (38.2.0) to handle an issue found with a functional test during the original upgrade
* Updated the i18n mapping for EUI v.39.0.0
* Update various snapshots to account for the an i18n translation token addition in EUI v. 39.0.0
* Updated test cases marked as obsolete by CI
* Update src/dev/license_checker/config.ts
Removing TODO comments from src/dev/license_checker/config.ts as they are no longer needed.
Co-authored-by: Constance
* Add option auto fit row to content
* Fix tests
* Fix tests
* Add temp fix for correct rendering grid with auto-height when changing data or setting
* Fix lint
* Fix lint and tests
* Adds new dependency for temp fix
Co-authored-by: Brianna Hall
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Bree Hall <40739624+breehall@users.noreply.github.com>
Co-authored-by: Constance
Co-authored-by: Uladzislau Lasitsa
Co-authored-by: Brianna Hall
Co-authored-by: Bree Hall <40739624+breehall@users.noreply.github.com>
Co-authored-by: Constance
---
src/plugins/vis_types/table/common/types.ts | 1 +
.../__snapshots__/table_vis_fn.test.ts.snap | 1 +
.../table_vis_basic.test.tsx.snap | 3 ++
.../components/table_vis_basic.test.tsx | 3 +-
.../public/components/table_vis_basic.tsx | 36 +++++++++++++++++--
.../public/components/table_vis_cell.tsx | 4 +--
.../public/components/table_vis_options.tsx | 10 ++++++
.../table/public/table_vis_fn.test.ts | 1 +
.../vis_types/table/public/table_vis_fn.ts | 5 +++
.../vis_types/table/public/table_vis_type.ts | 1 +
src/plugins/vis_types/table/public/to_ast.ts | 1 +
11 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/src/plugins/vis_types/table/common/types.ts b/src/plugins/vis_types/table/common/types.ts
index 015af80adf0dc..9f607a964977b 100644
--- a/src/plugins/vis_types/table/common/types.ts
+++ b/src/plugins/vis_types/table/common/types.ts
@@ -24,5 +24,6 @@ export interface TableVisParams {
showTotal: boolean;
totalFunc: AggTypes;
percentageCol: string;
+ autoFitRowToContent?: boolean;
row?: boolean;
}
diff --git a/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap
index be7e2822f128d..1a2badbd26634 100644
--- a/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap
+++ b/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap
@@ -26,6 +26,7 @@ Object {
"type": "render",
"value": Object {
"visConfig": Object {
+ "autoFitRowToContent": false,
"buckets": Array [],
"metrics": Array [
Object {
diff --git a/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap b/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap
index 85cf9422630d6..38e3dcbb7097c 100644
--- a/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap
+++ b/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap
@@ -17,6 +17,7 @@ exports[`TableVisBasic should init data grid 1`] = `
"header": "underline",
}
}
+ key="0"
minSizeForControls={1}
onColumnResize={[Function]}
renderCellValue={[Function]}
@@ -55,6 +56,7 @@ exports[`TableVisBasic should init data grid with title provided - for split mod
"header": "underline",
}
}
+ key="0"
minSizeForControls={1}
onColumnResize={[Function]}
renderCellValue={[Function]}
@@ -86,6 +88,7 @@ exports[`TableVisBasic should render the toolbar 1`] = `
"header": "underline",
}
}
+ key="0"
minSizeForControls={1}
onColumnResize={[Function]}
renderCellValue={[Function]}
diff --git a/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx b/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx
index 0fb74a41b5df0..0296f2ec1327a 100644
--- a/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx
+++ b/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx
@@ -67,6 +67,7 @@ describe('TableVisBasic', () => {
});
it('should sort rows by column and pass the sorted rows for consumers', () => {
+ (createTableVisCell as jest.Mock).mockClear();
const uiStateProps = {
...props.uiStateProps,
sort: {
@@ -96,7 +97,7 @@ describe('TableVisBasic', () => {
visConfig={{ ...props.visConfig, showToolbar: true }}
/>
);
- expect(createTableVisCell).toHaveBeenCalledWith(sortedRows, table.formattedColumns);
+ expect(createTableVisCell).toHaveBeenCalledWith(sortedRows, table.formattedColumns, undefined);
expect(createGridColumns).toHaveBeenCalledWith(
table.columns,
sortedRows,
diff --git a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx
index e627b9e7f92f2..cfe1ce5d40a1e 100644
--- a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx
+++ b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import React, { memo, useCallback, useMemo } from 'react';
+import React, { memo, useCallback, useMemo, useEffect, useState, useRef } from 'react';
import { EuiDataGrid, EuiDataGridProps, EuiDataGridSorting, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { orderBy } from 'lodash';
@@ -47,8 +47,16 @@ export const TableVisBasic = memo(
// renderCellValue is a component which renders a cell based on column and row indexes
const renderCellValue = useMemo(
- () => createTableVisCell(sortedRows, formattedColumns),
- [formattedColumns, sortedRows]
+ () => createTableVisCell(sortedRows, formattedColumns, visConfig.autoFitRowToContent),
+ [formattedColumns, sortedRows, visConfig.autoFitRowToContent]
+ );
+
+ const rowHeightsOptions = useMemo(
+ () =>
+ visConfig.autoFitRowToContent
+ ? ({ defaultHeight: 'auto' } as unknown as EuiDataGridProps['rowHeightsOptions'])
+ : undefined,
+ [visConfig.autoFitRowToContent]
);
// Columns config
@@ -103,6 +111,26 @@ export const TableVisBasic = memo(
[columns, setColumnsWidth]
);
+ const firstRender = useRef(true);
+ const [dataGridUpdateCounter, setDataGridUpdateCounter] = useState(0);
+
+ // key was added as temporary solution to force re-render if we change autoFitRowToContent or we get new data
+ // cause we have problem with correct updating height cache in EUI datagrid when we use auto-height
+ // will be removed as soon as fix problem on EUI side
+ useEffect(() => {
+ // skip first render
+ if (firstRender.current) {
+ firstRender.current = false;
+ return;
+ }
+ // skip if auto height was turned off
+ if (!visConfig.autoFitRowToContent) {
+ return;
+ }
+ // update counter to remount grid from scratch
+ setDataGridUpdateCounter((counter) => counter + 1);
+ }, [visConfig.autoFitRowToContent, table, sort, pagination, columnsWidth]);
+
return (
<>
{title && (
@@ -111,12 +139,14 @@ export const TableVisBasic = memo(
)}
id),
diff --git a/src/plugins/vis_types/table/public/components/table_vis_cell.tsx b/src/plugins/vis_types/table/public/components/table_vis_cell.tsx
index 9749cdcb5740c..7d7af447db489 100644
--- a/src/plugins/vis_types/table/public/components/table_vis_cell.tsx
+++ b/src/plugins/vis_types/table/public/components/table_vis_cell.tsx
@@ -13,7 +13,7 @@ import { DatatableRow } from 'src/plugins/expressions';
import { FormattedColumns } from '../types';
export const createTableVisCell =
- (rows: DatatableRow[], formattedColumns: FormattedColumns) =>
+ (rows: DatatableRow[], formattedColumns: FormattedColumns, autoFitRowToContent?: boolean) =>
({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => {
const rowValue = rows[rowIndex][columnId];
const column = formattedColumns[columnId];
@@ -28,7 +28,7 @@ export const createTableVisCell =
*/
dangerouslySetInnerHTML={{ __html: content }} // eslint-disable-line react/no-danger
data-test-subj="tbvChartCellContent"
- className="tbvChartCellContent"
+ className={autoFitRowToContent ? '' : 'tbvChartCellContent'}
/>
);
diff --git a/src/plugins/vis_types/table/public/components/table_vis_options.tsx b/src/plugins/vis_types/table/public/components/table_vis_options.tsx
index 8a6b8586fce7d..698aca6034a6b 100644
--- a/src/plugins/vis_types/table/public/components/table_vis_options.tsx
+++ b/src/plugins/vis_types/table/public/components/table_vis_options.tsx
@@ -93,6 +93,16 @@ function TableOptions({
data-test-subj="showMetricsAtAllLevels"
/>
+
+
{
splitColumn: undefined,
splitRow: undefined,
showMetricsAtAllLevels: false,
+ autoFitRowToContent: false,
sort: {
columnIndex: null,
direction: null,
diff --git a/src/plugins/vis_types/table/public/table_vis_fn.ts b/src/plugins/vis_types/table/public/table_vis_fn.ts
index ebddb0b4b7fef..861923ef5086e 100644
--- a/src/plugins/vis_types/table/public/table_vis_fn.ts
+++ b/src/plugins/vis_types/table/public/table_vis_fn.ts
@@ -118,6 +118,11 @@ export const createTableVisFn = (): TableExpressionFunctionDefinition => ({
defaultMessage: 'Specifies calculating function for the total row. Possible options are: ',
}),
},
+ autoFitRowToContent: {
+ types: ['boolean'],
+ help: '',
+ default: false,
+ },
},
fn(input, args, handlers) {
const convertedData = tableVisResponseHandler(input, args);
diff --git a/src/plugins/vis_types/table/public/table_vis_type.ts b/src/plugins/vis_types/table/public/table_vis_type.ts
index 4664e87cea79b..a641224e23f52 100644
--- a/src/plugins/vis_types/table/public/table_vis_type.ts
+++ b/src/plugins/vis_types/table/public/table_vis_type.ts
@@ -35,6 +35,7 @@ export const tableVisTypeDefinition: VisTypeDefinition = {
showToolbar: false,
totalFunc: 'sum',
percentageCol: '',
+ autoFitRowToContent: false,
},
},
editorConfig: {
diff --git a/src/plugins/vis_types/table/public/to_ast.ts b/src/plugins/vis_types/table/public/to_ast.ts
index 8e1c92c8dde4f..0268708f22dfe 100644
--- a/src/plugins/vis_types/table/public/to_ast.ts
+++ b/src/plugins/vis_types/table/public/to_ast.ts
@@ -64,6 +64,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params)
showMetricsAtAllLevels: vis.params.showMetricsAtAllLevels,
showToolbar: vis.params.showToolbar,
showTotal: vis.params.showTotal,
+ autoFitRowToContent: vis.params.autoFitRowToContent,
totalFunc: vis.params.totalFunc,
title: vis.title,
metrics: metrics.map(prepareDimension),
From 3c0b0c54996452ec69aca8cc2b1b2f24e38158f8 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:32:28 -0400
Subject: [PATCH 20/36] [Fleet] Add beta flag for custom integrations (#115447)
(#115487)
Co-authored-by: Thomas Neirynck
---
.../integrations/sections/epm/screens/home/index.tsx | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
index 2d1077f586a1a..4270d360b9294 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
@@ -66,6 +66,13 @@ export const mapToCard = (
uiInternalPathUrl = url;
}
+ let release: 'ga' | 'beta' | 'experimental' | undefined;
+ if ('release' in item) {
+ release = item.release;
+ } else if (item.isBeta === true) {
+ release = 'beta';
+ }
+
return {
id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}-${item.id}`,
description: item.description,
@@ -75,7 +82,7 @@ export const mapToCard = (
integration: 'integration' in item ? item.integration || '' : '',
name: 'name' in item ? item.name || '' : '',
version: 'version' in item ? item.version || '' : '',
- release: 'release' in item ? item.release : undefined,
+ release,
categories: ((item.categories || []) as string[]).filter((c: string) => !!c),
};
};
From 11b35f7156e03818371e94925d9f12bdd70e5650 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:34:19 -0400
Subject: [PATCH 21/36] [Fleet] Address Package Policy upgrade UX review
(#115414) (#115442)
* Fix package update button + icon
* Adjust order of modal of states based on UX review
* Clarify integration/agent policies in integration UI policy table
Co-authored-by: Kyle Pollich
---
.../integrations/sections/epm/screens/detail/index.tsx | 2 +-
.../epm/screens/detail/policies/package_policies.tsx | 2 +-
.../sections/epm/screens/detail/settings/update_button.tsx | 7 +++----
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
index 881fc566c932d..6e3eba19c52e3 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
@@ -452,7 +452,7 @@ export function Detail() {
name: (
),
isSelected: panel === 'policies',
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
index d6dc7e8440dae..69487454dcb94 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx
@@ -211,7 +211,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
{
field: 'packagePolicy.name',
name: i18n.translate('xpack.fleet.epm.packageDetails.integrationList.name', {
- defaultMessage: 'Integration',
+ defaultMessage: 'Integration Policy',
}),
render(_, { packagePolicy }) {
return ;
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx
index 2acd5634b1e5f..b5a8394fa2cb2 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx
@@ -154,6 +154,7 @@ export const UpdateButton: React.FunctionComponent = ({
return;
}
+ setIsUpdateModalVisible(false);
setIsUpgradingPackagePolicies(true);
await installPackage({ name, version, title });
@@ -166,7 +167,6 @@ export const UpdateButton: React.FunctionComponent = ({
);
setIsUpgradingPackagePolicies(false);
- setIsUpdateModalVisible(false);
notifications.toasts.addSuccess({
title: toMountPoint(
@@ -285,15 +285,14 @@ export const UpdateButton: React.FunctionComponent = ({
setIsUpdateModalVisible(true) : handleClickUpdate
}
>
From 51e773fad22e5b2222a31917395ab3cbfed07e39 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:37:03 -0400
Subject: [PATCH 22/36] [Security Solution] Improves the formatting of array
values and JSON in the Event and Alert Details panels (#115141) (#115532)
## [Security Solution] Improves the formatting of array values and JSON in the Event and Alert Details panels
This PR improves the formatting of array values and JSON in the Event and Alert details panels by:
- in the `Table` tab, formatting array values such that each value appears on a separate line, (instead of joining the values on a single line)
- in the `JSON` tab, displaying the raw search hit JSON, instead displaying a JSON representation based on the `Fields` API
### Table value formatting
In the Event and Alert details `Table` tab, array values were joined on a single line, as shown in the _before_ screenshot below:
![event-details-value-formatting-before](https://user-images.githubusercontent.com/4459398/137524968-6450cd73-3154-457d-b850-32a3e7faaab2.png)
_Above: (before) array values were joined on a single line_
Array values are now formatted such that each value appears on a separate line, as shown in the _after_ screenshot below:
![event-details-value-formatting-after](https://user-images.githubusercontent.com/4459398/137436705-b0bec735-5a83-402e-843a-2776e1c80da9.png)
_Above: (after) array values each appear on a separte line_
### JSON formatting
The `JSON` tab previously displayed a JSON representation based on the `Fields` API. Array values were previously represented as a joined string, as shown in the _before_ screenshot below:
![event-details-json-formatting-before](https://user-images.githubusercontent.com/4459398/137525039-d1b14f21-5f9c-4201-905e-8b08f00bb5a0.png)
_Above: (before) array values were previously represented as a joined string_
The `JSON` tab now displays the raw search hit JSON, per the _after_ screenshot below:
![event-details-json-formatting-after](https://user-images.githubusercontent.com/4459398/137437257-330c5b49-a4ad-418e-a976-923f7a35c0cf.png)
_Above: (after) the `JSON` tab displays the raw search hit_
CC @monina-n @paulewing
Co-authored-by: Andrew Goldstein
---
.../detection_alerts/alerts_details.spec.ts | 20 +-
.../detection_alerts/cti_enrichments.spec.ts | 49 ++-
.../cypress/screens/alerts_details.ts | 2 +
.../alert_summary_view.test.tsx.snap | 120 ++++--
.../__snapshots__/json_view.test.tsx.snap | 343 ++++++++++++++++--
.../event_details/event_details.test.tsx | 3 +-
.../event_details/event_details.tsx | 6 +-
.../event_details/json_view.test.tsx | 49 +--
.../components/event_details/json_view.tsx | 23 +-
.../table/field_value_cell.test.tsx | 193 ++++++++++
.../event_details/table/field_value_cell.tsx | 26 +-
.../public/common/mock/mock_detail_item.ts | 188 ++++++++++
.../event_details/expandable_event.tsx | 3 +
.../side_panel/event_details/index.tsx | 4 +-
.../timelines/containers/details/index.tsx | 7 +-
.../timeline/events/details/index.ts | 1 +
.../timeline/factory/events/details/index.ts | 4 +
17 files changed, 872 insertions(+), 169 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
index 8908adb42f202..d211a5914656b 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts
@@ -5,14 +5,14 @@
* 2.0.
*/
-import { ALERT_FLYOUT, CELL_TEXT, JSON_LINES, TABLE_ROWS } from '../../screens/alerts_details';
+import { ALERT_FLYOUT, CELL_TEXT, JSON_TEXT, TABLE_ROWS } from '../../screens/alerts_details';
import {
expandFirstAlert,
waitForAlertsIndexToBeCreated,
waitForAlertsPanelToBeLoaded,
} from '../../tasks/alerts';
-import { openJsonView, openTable, scrollJsonViewToBottom } from '../../tasks/alerts_details';
+import { openJsonView, openTable } from '../../tasks/alerts_details';
import { createCustomRuleActivated } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { esArchiverLoad } from '../../tasks/es_archiver';
@@ -36,20 +36,14 @@ describe('Alert details with unmapped fields', () => {
});
it('Displays the unmapped field on the JSON view', () => {
- const expectedUnmappedField = { line: 2, text: ' "unmapped": "This is the unmapped field"' };
+ const expectedUnmappedValue = 'This is the unmapped field';
openJsonView();
- scrollJsonViewToBottom();
- cy.get(ALERT_FLYOUT)
- .find(JSON_LINES)
- .then((elements) => {
- const length = elements.length;
- cy.wrap(elements)
- .eq(length - expectedUnmappedField.line)
- .invoke('text')
- .should('include', expectedUnmappedField.text);
- });
+ cy.get(JSON_TEXT).then((x) => {
+ const parsed = JSON.parse(x.text());
+ expect(parsed._source.unmapped).to.equal(expectedUnmappedValue);
+ });
});
it('Displays the unmapped field on the table', () => {
diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
index b3c6abcd8e426..f15e7adbbca44 100644
--- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts
@@ -10,7 +10,7 @@ import { cleanKibana, reload } from '../../tasks/common';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import {
- JSON_LINES,
+ JSON_TEXT,
TABLE_CELL,
TABLE_ROWS,
THREAT_DETAILS_VIEW,
@@ -28,11 +28,7 @@ import {
viewThreatIntelTab,
} from '../../tasks/alerts';
import { createCustomIndicatorRule } from '../../tasks/api_calls/rules';
-import {
- openJsonView,
- openThreatIndicatorDetails,
- scrollJsonViewToBottom,
-} from '../../tasks/alerts_details';
+import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_details';
import { ALERTS_URL } from '../../urls/navigation';
import { addsFieldsToTimeline } from '../../tasks/rule_details';
@@ -76,26 +72,39 @@ describe('CTI Enrichment', () => {
it('Displays persisted enrichments on the JSON view', () => {
const expectedEnrichment = [
- { line: 4, text: ' "threat": {' },
{
- line: 3,
- text: ' "enrichments": "{\\"indicator\\":{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\"},\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"logs-ti_abusech.malware\\",\\"type\\":\\"indicator_match_rule\\"}}"',
+ indicator: {
+ first_seen: '2021-03-10T08:02:14.000Z',
+ file: {
+ size: 80280,
+ pe: {},
+ type: 'elf',
+ hash: {
+ sha256: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
+ tlsh: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
+ ssdeep:
+ '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
+ md5: '9b6c3518a91d23ed77504b5416bfb5b3',
+ },
+ },
+ type: 'file',
+ },
+ matched: {
+ atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
+ field: 'myhash.mysha256',
+ id: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f',
+ index: 'logs-ti_abusech.malware',
+ type: 'indicator_match_rule',
+ },
},
- { line: 2, text: ' }' },
];
expandFirstAlert();
openJsonView();
- scrollJsonViewToBottom();
-
- cy.get(JSON_LINES).then((elements) => {
- const length = elements.length;
- expectedEnrichment.forEach((enrichment) => {
- cy.wrap(elements)
- .eq(length - enrichment.line)
- .invoke('text')
- .should('include', enrichment.text);
- });
+
+ cy.get(JSON_TEXT).then((x) => {
+ const parsed = JSON.parse(x.text());
+ expect(parsed._source.threat.enrichments).to.deep.equal(expectedEnrichment);
});
});
diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts
index c740a669d059a..584fba05452f0 100644
--- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts
@@ -28,6 +28,8 @@ export const JSON_LINES = '.euiCodeBlock__line';
export const JSON_VIEW_TAB = '[data-test-subj="jsonViewTab"]';
+export const JSON_TEXT = '[data-test-subj="jsonView"]';
+
export const TABLE_CELL = '.euiTableRowCell';
export const TABLE_TAB = '[data-test-subj="tableTab"]';
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap
index d367c68586be1..930e1282ebca5 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap
@@ -138,12 +138,17 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1`
class="euiTableCellContent flyoutOverviewDescription euiTableCellContent--overflowingContent"
>
{
- "_id": "pEMaMmkBUV60JmNWmWVi",
- "_index": "filebeat-8.0.0-2019.02.19-000001",
+ "_index": ".ds-logs-endpoint.events.network-default-2021.09.28-000001",
+ "_id": "TUWyf3wBFCFU0qRJTauW",
"_score": 1,
- "_type": "_doc",
- "@timestamp": "2019-02-28T16:50:54.621Z",
- "agent": {
- "ephemeral_id": "9d391ef2-a734-4787-8891-67031178c641",
- "hostname": "siem-kibana",
- "id": "5de03d5f-52f3-482e-91d4-853c7de073c3",
- "type": "filebeat",
- "version": "8.0.0"
- },
- "cloud": {
- "availability_zone": "projects/189716325846/zones/us-east1-b",
- "instance": {
- "id": "5412578377715150143",
- "name": "siem-kibana"
+ "_source": {
+ "agent": {
+ "id": "2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624",
+ "type": "endpoint",
+ "version": "8.0.0-SNAPSHOT"
},
- "machine": {
- "type": "projects/189716325846/machineTypes/n1-standard-1"
+ "process": {
+ "Ext": {
+ "ancestry": [
+ "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=",
+ "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA=="
+ ]
+ },
+ "name": "filebeat",
+ "pid": 22535,
+ "entity_id": "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=",
+ "executable": "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat"
},
- "project": {
- "id": "elastic-beats"
+ "destination": {
+ "address": "127.0.0.1",
+ "port": 9200,
+ "ip": "127.0.0.1"
},
- "provider": "gce"
- },
- "destination": {
- "bytes": 584,
- "ip": "10.47.8.200",
- "packets": 4,
- "port": 902
+ "source": {
+ "address": "127.0.0.1",
+ "port": 54146,
+ "ip": "127.0.0.1"
+ },
+ "message": "Endpoint network event",
+ "network": {
+ "transport": "tcp",
+ "type": "ipv4"
+ },
+ "@timestamp": "2021-10-14T16:45:58.0310772Z",
+ "ecs": {
+ "version": "1.11.0"
+ },
+ "data_stream": {
+ "namespace": "default",
+ "type": "logs",
+ "dataset": "endpoint.events.network"
+ },
+ "elastic": {
+ "agent": {
+ "id": "12345"
+ }
+ },
+ "host": {
+ "hostname": "test-linux-1",
+ "os": {
+ "Ext": {
+ "variant": "Debian"
+ },
+ "kernel": "4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)",
+ "name": "Linux",
+ "family": "debian",
+ "type": "linux",
+ "version": "10",
+ "platform": "debian",
+ "full": "Debian 10"
+ },
+ "ip": [
+ "127.0.0.1",
+ "::1",
+ "10.1.2.3",
+ "2001:0DB8:AC10:FE01::"
+ ],
+ "name": "test-linux-1",
+ "id": "76ea303129f249aa7382338e4263eac1",
+ "mac": [
+ "aa:bb:cc:dd:ee:ff"
+ ],
+ "architecture": "x86_64"
+ },
+ "event": {
+ "agent_id_status": "verified",
+ "sequence": 44872,
+ "ingested": "2021-10-14T16:46:04Z",
+ "created": "2021-10-14T16:45:58.0310772Z",
+ "kind": "event",
+ "module": "endpoint",
+ "action": "connection_attempted",
+ "id": "MKPXftjGeHiQzUNj++++nn6R",
+ "category": [
+ "network"
+ ],
+ "type": [
+ "start"
+ ],
+ "dataset": "endpoint.events.network",
+ "outcome": "unknown"
+ },
+ "user": {
+ "Ext": {
+ "real": {
+ "name": "root",
+ "id": 0
+ }
+ },
+ "name": "root",
+ "id": 0
+ },
+ "group": {
+ "Ext": {
+ "real": {
+ "name": "root",
+ "id": 0
+ }
+ },
+ "name": "root",
+ "id": 0
+ }
},
- "event": {
- "kind": "event"
+ "fields": {
+ "host.os.full.text": [
+ "Debian 10"
+ ],
+ "event.category": [
+ "network"
+ ],
+ "process.name.text": [
+ "filebeat"
+ ],
+ "host.os.name.text": [
+ "Linux"
+ ],
+ "host.os.full": [
+ "Debian 10"
+ ],
+ "host.hostname": [
+ "test-linux-1"
+ ],
+ "process.pid": [
+ 22535
+ ],
+ "host.mac": [
+ "42:01:0a:c8:00:32"
+ ],
+ "elastic.agent.id": [
+ "abcdefg-f6d5-4ce6-915d-8f1f8f413624"
+ ],
+ "host.os.version": [
+ "10"
+ ],
+ "host.os.name": [
+ "Linux"
+ ],
+ "source.ip": [
+ "127.0.0.1"
+ ],
+ "destination.address": [
+ "127.0.0.1"
+ ],
+ "host.name": [
+ "test-linux-1"
+ ],
+ "event.agent_id_status": [
+ "verified"
+ ],
+ "event.kind": [
+ "event"
+ ],
+ "event.outcome": [
+ "unknown"
+ ],
+ "group.name": [
+ "root"
+ ],
+ "user.id": [
+ "0"
+ ],
+ "host.os.type": [
+ "linux"
+ ],
+ "process.Ext.ancestry": [
+ "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=",
+ "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA=="
+ ],
+ "user.Ext.real.id": [
+ "0"
+ ],
+ "data_stream.type": [
+ "logs"
+ ],
+ "host.architecture": [
+ "x86_64"
+ ],
+ "process.name": [
+ "filebeat"
+ ],
+ "agent.id": [
+ "2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624"
+ ],
+ "source.port": [
+ 54146
+ ],
+ "ecs.version": [
+ "1.11.0"
+ ],
+ "event.created": [
+ "2021-10-14T16:45:58.031Z"
+ ],
+ "agent.version": [
+ "8.0.0-SNAPSHOT"
+ ],
+ "host.os.family": [
+ "debian"
+ ],
+ "destination.port": [
+ 9200
+ ],
+ "group.id": [
+ "0"
+ ],
+ "user.name": [
+ "root"
+ ],
+ "source.address": [
+ "127.0.0.1"
+ ],
+ "process.entity_id": [
+ "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA="
+ ],
+ "host.ip": [
+ "127.0.0.1",
+ "::1",
+ "10.1.2.3",
+ "2001:0DB8:AC10:FE01::"
+ ],
+ "process.executable.caseless": [
+ "/opt/elastic/agent/data/elastic-agent-058c40/install/filebeat-8.0.0-snapshot-linux-x86_64/filebeat"
+ ],
+ "event.sequence": [
+ 44872
+ ],
+ "agent.type": [
+ "endpoint"
+ ],
+ "process.executable.text": [
+ "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat"
+ ],
+ "group.Ext.real.name": [
+ "root"
+ ],
+ "event.module": [
+ "endpoint"
+ ],
+ "host.os.kernel": [
+ "4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)"
+ ],
+ "host.os.full.caseless": [
+ "debian 10"
+ ],
+ "host.id": [
+ "76ea303129f249aa7382338e4263eac1"
+ ],
+ "process.name.caseless": [
+ "filebeat"
+ ],
+ "network.type": [
+ "ipv4"
+ ],
+ "process.executable": [
+ "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat"
+ ],
+ "user.Ext.real.name": [
+ "root"
+ ],
+ "data_stream.namespace": [
+ "default"
+ ],
+ "message": [
+ "Endpoint network event"
+ ],
+ "destination.ip": [
+ "127.0.0.1"
+ ],
+ "network.transport": [
+ "tcp"
+ ],
+ "host.os.Ext.variant": [
+ "Debian"
+ ],
+ "group.Ext.real.id": [
+ "0"
+ ],
+ "event.ingested": [
+ "2021-10-14T16:46:04.000Z"
+ ],
+ "event.action": [
+ "connection_attempted"
+ ],
+ "@timestamp": [
+ "2021-10-14T16:45:58.031Z"
+ ],
+ "host.os.platform": [
+ "debian"
+ ],
+ "data_stream.dataset": [
+ "endpoint.events.network"
+ ],
+ "event.type": [
+ "start"
+ ],
+ "event.id": [
+ "MKPXftjGeHiQzUNj++++nn6R"
+ ],
+ "host.os.name.caseless": [
+ "linux"
+ ],
+ "event.dataset": [
+ "endpoint.events.network"
+ ],
+ "user.name.text": [
+ "root"
+ ]
}
}
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx
index a8ba536a75541..37ca3b0b897a6 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx
@@ -11,7 +11,7 @@ import React from 'react';
import '../../mock/match_media';
import '../../mock/react_beautiful_dnd';
-import { mockDetailItemData, mockDetailItemDataId, TestProviders } from '../../mock';
+import { mockDetailItemData, mockDetailItemDataId, rawEventData, TestProviders } from '../../mock';
import { EventDetails, EventsViewType } from './event_details';
import { mockBrowserFields } from '../../containers/source/mock';
@@ -48,6 +48,7 @@ describe('EventDetails', () => {
timelineId: 'test',
eventView: EventsViewType.summaryView,
hostRisk: { fields: [], loading: true },
+ rawEventData,
};
const alertsProps = {
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
index e7092d9d6f466..a8305a635f157 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
@@ -61,6 +61,7 @@ interface Props {
id: string;
isAlert: boolean;
isDraggable?: boolean;
+ rawEventData: object | undefined;
timelineTabType: TimelineTabs | 'flyout';
timelineId: string;
hostRisk: HostRisk | null;
@@ -106,6 +107,7 @@ const EventDetailsComponent: React.FC = ({
id,
isAlert,
isDraggable,
+ rawEventData,
timelineId,
timelineTabType,
hostRisk,
@@ -278,12 +280,12 @@ const EventDetailsComponent: React.FC = ({
<>
-
+
>
),
}),
- [data]
+ [rawEventData]
);
const tabs = useMemo(() => {
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx
index 696fac6016603..b20270266602d 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx
@@ -8,58 +8,15 @@
import { shallow } from 'enzyme';
import React from 'react';
-import { mockDetailItemData } from '../../mock';
+import { rawEventData } from '../../mock';
-import { buildJsonView, JsonView } from './json_view';
+import { JsonView } from './json_view';
describe('JSON View', () => {
describe('rendering', () => {
test('should match snapshot', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
expect(wrapper).toMatchSnapshot();
});
});
-
- describe('buildJsonView', () => {
- test('should match a json', () => {
- const expectedData = {
- '@timestamp': '2019-02-28T16:50:54.621Z',
- _id: 'pEMaMmkBUV60JmNWmWVi',
- _index: 'filebeat-8.0.0-2019.02.19-000001',
- _score: 1,
- _type: '_doc',
- agent: {
- ephemeral_id: '9d391ef2-a734-4787-8891-67031178c641',
- hostname: 'siem-kibana',
- id: '5de03d5f-52f3-482e-91d4-853c7de073c3',
- type: 'filebeat',
- version: '8.0.0',
- },
- cloud: {
- availability_zone: 'projects/189716325846/zones/us-east1-b',
- instance: {
- id: '5412578377715150143',
- name: 'siem-kibana',
- },
- machine: {
- type: 'projects/189716325846/machineTypes/n1-standard-1',
- },
- project: {
- id: 'elastic-beats',
- },
- provider: 'gce',
- },
- destination: {
- bytes: 584,
- ip: '10.47.8.200',
- packets: 4,
- port: 902,
- },
- event: {
- kind: 'event',
- },
- };
- expect(buildJsonView(mockDetailItemData)).toEqual(expectedData);
- });
- });
});
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx
index 0614f131bcd10..0227d44f32305 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx
@@ -6,15 +6,13 @@
*/
import { EuiCodeBlock } from '@elastic/eui';
-import { set } from '@elastic/safer-lodash-set/fp';
import React, { useMemo } from 'react';
import styled from 'styled-components';
-import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
import { omitTypenameAndEmpty } from '../../../timelines/components/timeline/body/helpers';
interface Props {
- data: TimelineEventsDetailsItem[];
+ rawEventData: object | undefined;
}
const EuiCodeEditorContainer = styled.div`
@@ -23,15 +21,15 @@ const EuiCodeEditorContainer = styled.div`
}
`;
-export const JsonView = React.memo(({ data }) => {
+export const JsonView = React.memo(({ rawEventData }) => {
const value = useMemo(
() =>
JSON.stringify(
- buildJsonView(data),
+ rawEventData,
omitTypenameAndEmpty,
2 // indent level
),
- [data]
+ [rawEventData]
);
return (
@@ -50,16 +48,3 @@ export const JsonView = React.memo(({ data }) => {
});
JsonView.displayName = 'JsonView';
-
-export const buildJsonView = (data: TimelineEventsDetailsItem[]) =>
- data
- .sort((a, b) => a.field.localeCompare(b.field))
- .reduce(
- (accumulator, item) =>
- set(
- item.field,
- Array.isArray(item.originalValue) ? item.originalValue.join() : item.originalValue,
- accumulator
- ),
- {}
- );
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx
new file mode 100644
index 0000000000000..f6c43da2da8ac
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx
@@ -0,0 +1,193 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { render, screen } from '@testing-library/react';
+import React from 'react';
+
+import { BrowserField } from '../../../containers/source';
+import { FieldValueCell } from './field_value_cell';
+import { TestProviders } from '../../../mock';
+import { EventFieldsData } from '../types';
+
+const contextId = 'test';
+
+const eventId = 'TUWyf3wBFCFU0qRJTauW';
+
+const hostIpData: EventFieldsData = {
+ aggregatable: true,
+ ariaRowindex: 35,
+ category: 'host',
+ description: 'Host ip addresses.',
+ example: '127.0.0.1',
+ field: 'host.ip',
+ fields: {},
+ format: '',
+ indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'],
+ isObjectArray: false,
+ name: 'host.ip',
+ originalValue: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'],
+ searchable: true,
+ type: 'ip',
+ values: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'],
+};
+const hostIpValues = ['127.0.0.1', '::1', '10.1.2.3', 'fe80::4001:aff:fec8:32'];
+
+describe('FieldValueCell', () => {
+ describe('common behavior', () => {
+ beforeEach(() => {
+ render(
+
+
+
+ );
+ });
+
+ test('it formats multiple values such that each value is displayed on a single line', () => {
+ expect(screen.getByTestId(`event-field-${hostIpData.field}`)).toHaveClass(
+ 'euiFlexGroup--directionColumn'
+ );
+ });
+ });
+
+ describe('when `BrowserField` metadata is NOT available', () => {
+ beforeEach(() => {
+ render(
+
+
+
+ );
+ });
+
+ test('it renders each of the expected values when `fieldFromBrowserField` is undefined', () => {
+ hostIpValues.forEach((value) => {
+ expect(screen.getByText(value)).toBeInTheDocument();
+ });
+ });
+
+ test('it renders values formatted as plain text (without `eventFieldsTable__fieldValue` formatting)', () => {
+ expect(screen.getByTestId(`event-field-${hostIpData.field}`).firstChild).not.toHaveClass(
+ 'eventFieldsTable__fieldValue'
+ );
+ });
+ });
+
+ describe('`message` field formatting', () => {
+ const messageData: EventFieldsData = {
+ aggregatable: false,
+ ariaRowindex: 50,
+ category: 'base',
+ description:
+ 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.',
+ example: 'Hello World',
+ field: 'message',
+ fields: {},
+ format: '',
+ indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'],
+ isObjectArray: false,
+ name: 'message',
+ originalValue: ['Endpoint network event'],
+ searchable: true,
+ type: 'string',
+ values: ['Endpoint network event'],
+ };
+ const messageValues = ['Endpoint network event'];
+
+ const messageFieldFromBrowserField: BrowserField = {
+ aggregatable: false,
+ category: 'base',
+ description:
+ 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.',
+ example: 'Hello World',
+ fields: {},
+ format: '',
+ indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'],
+ name: 'message',
+ searchable: true,
+ type: 'string',
+ };
+
+ beforeEach(() => {
+ render(
+
+
+
+ );
+ });
+
+ test('it renders special formatting for the `message` field', () => {
+ expect(screen.getByTestId('event-field-message')).toBeInTheDocument();
+ });
+
+ test('it renders the expected message value', () => {
+ messageValues.forEach((value) => {
+ expect(screen.getByText(value)).toBeInTheDocument();
+ });
+ });
+ });
+
+ describe('when `BrowserField` metadata IS available', () => {
+ const hostIpFieldFromBrowserField: BrowserField = {
+ aggregatable: true,
+ category: 'host',
+ description: 'Host ip addresses.',
+ example: '127.0.0.1',
+ fields: {},
+ format: '',
+ indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'],
+ name: 'host.ip',
+ searchable: true,
+ type: 'ip',
+ };
+
+ beforeEach(() => {
+ render(
+
+
+
+ );
+ });
+
+ test('it renders values formatted with the expected class', () => {
+ expect(screen.getByTestId(`event-field-${hostIpData.field}`).firstChild).toHaveClass(
+ 'eventFieldsTable__fieldValue'
+ );
+ });
+
+ test('it renders link buttons for each of the host ip addresses', () => {
+ expect(screen.getAllByRole('button').length).toBe(hostIpValues.length);
+ });
+
+ test('it renders each of the expected values when `fieldFromBrowserField` is provided', () => {
+ hostIpValues.forEach((value) => {
+ expect(screen.getByText(value)).toBeInTheDocument();
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx
index fc20f84d3650d..dc6c84b8138fe 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx
@@ -6,7 +6,7 @@
*/
import React from 'react';
-import { EuiText } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { BrowserField } from '../../../containers/source';
import { OverflowField } from '../../tables/helpers';
import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field';
@@ -36,18 +36,28 @@ export const FieldValueCell = React.memo(
values,
}: FieldValueCellProps) => {
return (
-
+
{values != null &&
values.map((value, i) => {
if (fieldFromBrowserField == null) {
return (
-
- {value}
-
+
+
+ {value}
+
+
);
}
return (
-
+
{data.field === MESSAGE_FIELD_NAME ? (
) : (
@@ -63,10 +73,10 @@ export const FieldValueCell = React.memo(
linkValue={(getLinkValue && getLinkValue(data.field)) ?? linkValue}
/>
)}
-
+
);
})}
-
+
);
}
);
diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts
index 3712d389edeb1..035bdbbceff88 100644
--- a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts
+++ b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts
@@ -139,3 +139,191 @@ export const generateMockDetailItemData = (): TimelineEventsDetailsItem[] => [
];
export const mockDetailItemData: TimelineEventsDetailsItem[] = generateMockDetailItemData();
+
+export const rawEventData = {
+ _index: '.ds-logs-endpoint.events.network-default-2021.09.28-000001',
+ _id: 'TUWyf3wBFCFU0qRJTauW',
+ _score: 1,
+ _source: {
+ agent: {
+ id: '2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624',
+ type: 'endpoint',
+ version: '8.0.0-SNAPSHOT',
+ },
+ process: {
+ Ext: {
+ ancestry: [
+ 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=',
+ 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==',
+ ],
+ },
+ name: 'filebeat',
+ pid: 22535,
+ entity_id: 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=',
+ executable:
+ '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat',
+ },
+ destination: {
+ address: '127.0.0.1',
+ port: 9200,
+ ip: '127.0.0.1',
+ },
+ source: {
+ address: '127.0.0.1',
+ port: 54146,
+ ip: '127.0.0.1',
+ },
+ message: 'Endpoint network event',
+ network: {
+ transport: 'tcp',
+ type: 'ipv4',
+ },
+ '@timestamp': '2021-10-14T16:45:58.0310772Z',
+ ecs: {
+ version: '1.11.0',
+ },
+ data_stream: {
+ namespace: 'default',
+ type: 'logs',
+ dataset: 'endpoint.events.network',
+ },
+ elastic: {
+ agent: {
+ id: '12345',
+ },
+ },
+ host: {
+ hostname: 'test-linux-1',
+ os: {
+ Ext: {
+ variant: 'Debian',
+ },
+ kernel: '4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)',
+ name: 'Linux',
+ family: 'debian',
+ type: 'linux',
+ version: '10',
+ platform: 'debian',
+ full: 'Debian 10',
+ },
+ ip: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'],
+ name: 'test-linux-1',
+ id: '76ea303129f249aa7382338e4263eac1',
+ mac: ['aa:bb:cc:dd:ee:ff'],
+ architecture: 'x86_64',
+ },
+ event: {
+ agent_id_status: 'verified',
+ sequence: 44872,
+ ingested: '2021-10-14T16:46:04Z',
+ created: '2021-10-14T16:45:58.0310772Z',
+ kind: 'event',
+ module: 'endpoint',
+ action: 'connection_attempted',
+ id: 'MKPXftjGeHiQzUNj++++nn6R',
+ category: ['network'],
+ type: ['start'],
+ dataset: 'endpoint.events.network',
+ outcome: 'unknown',
+ },
+ user: {
+ Ext: {
+ real: {
+ name: 'root',
+ id: 0,
+ },
+ },
+ name: 'root',
+ id: 0,
+ },
+ group: {
+ Ext: {
+ real: {
+ name: 'root',
+ id: 0,
+ },
+ },
+ name: 'root',
+ id: 0,
+ },
+ },
+ fields: {
+ 'host.os.full.text': ['Debian 10'],
+ 'event.category': ['network'],
+ 'process.name.text': ['filebeat'],
+ 'host.os.name.text': ['Linux'],
+ 'host.os.full': ['Debian 10'],
+ 'host.hostname': ['test-linux-1'],
+ 'process.pid': [22535],
+ 'host.mac': ['42:01:0a:c8:00:32'],
+ 'elastic.agent.id': ['abcdefg-f6d5-4ce6-915d-8f1f8f413624'],
+ 'host.os.version': ['10'],
+ 'host.os.name': ['Linux'],
+ 'source.ip': ['127.0.0.1'],
+ 'destination.address': ['127.0.0.1'],
+ 'host.name': ['test-linux-1'],
+ 'event.agent_id_status': ['verified'],
+ 'event.kind': ['event'],
+ 'event.outcome': ['unknown'],
+ 'group.name': ['root'],
+ 'user.id': ['0'],
+ 'host.os.type': ['linux'],
+ 'process.Ext.ancestry': [
+ 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=',
+ 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==',
+ ],
+ 'user.Ext.real.id': ['0'],
+ 'data_stream.type': ['logs'],
+ 'host.architecture': ['x86_64'],
+ 'process.name': ['filebeat'],
+ 'agent.id': ['2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624'],
+ 'source.port': [54146],
+ 'ecs.version': ['1.11.0'],
+ 'event.created': ['2021-10-14T16:45:58.031Z'],
+ 'agent.version': ['8.0.0-SNAPSHOT'],
+ 'host.os.family': ['debian'],
+ 'destination.port': [9200],
+ 'group.id': ['0'],
+ 'user.name': ['root'],
+ 'source.address': ['127.0.0.1'],
+ 'process.entity_id': [
+ 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=',
+ ],
+ 'host.ip': ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'],
+ 'process.executable.caseless': [
+ '/opt/elastic/agent/data/elastic-agent-058c40/install/filebeat-8.0.0-snapshot-linux-x86_64/filebeat',
+ ],
+ 'event.sequence': [44872],
+ 'agent.type': ['endpoint'],
+ 'process.executable.text': [
+ '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat',
+ ],
+ 'group.Ext.real.name': ['root'],
+ 'event.module': ['endpoint'],
+ 'host.os.kernel': ['4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)'],
+ 'host.os.full.caseless': ['debian 10'],
+ 'host.id': ['76ea303129f249aa7382338e4263eac1'],
+ 'process.name.caseless': ['filebeat'],
+ 'network.type': ['ipv4'],
+ 'process.executable': [
+ '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat',
+ ],
+ 'user.Ext.real.name': ['root'],
+ 'data_stream.namespace': ['default'],
+ message: ['Endpoint network event'],
+ 'destination.ip': ['127.0.0.1'],
+ 'network.transport': ['tcp'],
+ 'host.os.Ext.variant': ['Debian'],
+ 'group.Ext.real.id': ['0'],
+ 'event.ingested': ['2021-10-14T16:46:04.000Z'],
+ 'event.action': ['connection_attempted'],
+ '@timestamp': ['2021-10-14T16:45:58.031Z'],
+ 'host.os.platform': ['debian'],
+ 'data_stream.dataset': ['endpoint.events.network'],
+ 'event.type': ['start'],
+ 'event.id': ['MKPXftjGeHiQzUNj++++nn6R'],
+ 'host.os.name.caseless': ['linux'],
+ 'event.dataset': ['endpoint.events.network'],
+ 'user.name.text': ['root'],
+ },
+};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx
index 17d43d80a5a9a..6a7f0602c3675 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx
@@ -33,6 +33,7 @@ interface Props {
isDraggable?: boolean;
loading: boolean;
messageHeight?: number;
+ rawEventData: object | undefined;
timelineTabType: TimelineTabs | 'flyout';
timelineId: string;
hostRisk: HostRisk | null;
@@ -93,6 +94,7 @@ export const ExpandableEvent = React.memo(
loading,
detailsData,
hostRisk,
+ rawEventData,
}) => {
if (!event.eventId) {
return {i18n.EVENT_DETAILS_PLACEHOLDER};
@@ -111,6 +113,7 @@ export const ExpandableEvent = React.memo(
id={event.eventId}
isAlert={isAlert}
isDraggable={isDraggable}
+ rawEventData={rawEventData}
timelineId={timelineId}
timelineTabType={timelineTabType}
hostRisk={hostRisk}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx
index f8786e0706834..b9d7e0a8c024f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx
@@ -79,7 +79,7 @@ const EventDetailsPanelComponent: React.FC = ({
tabType,
timelineId,
}) => {
- const [loading, detailsData] = useTimelineEventsDetails({
+ const [loading, detailsData, rawEventData] = useTimelineEventsDetails({
docValueFields,
entityType,
indexName: expandedEvent.indexName ?? '',
@@ -195,6 +195,7 @@ const EventDetailsPanelComponent: React.FC = ({
isAlert={isAlert}
isDraggable={isDraggable}
loading={loading}
+ rawEventData={rawEventData}
timelineId={timelineId}
timelineTabType="flyout"
hostRisk={hostRisk}
@@ -228,6 +229,7 @@ const EventDetailsPanelComponent: React.FC = ({
isAlert={isAlert}
isDraggable={isDraggable}
loading={loading}
+ rawEventData={rawEventData}
timelineId={timelineId}
timelineTabType={tabType}
hostRisk={hostRisk}
diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx
index e59eaeed4f2a6..f05966bd97870 100644
--- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx
@@ -42,7 +42,7 @@ export const useTimelineEventsDetails = ({
indexName,
eventId,
skip,
-}: UseTimelineEventsDetailsProps): [boolean, EventsArgs['detailsData']] => {
+}: UseTimelineEventsDetailsProps): [boolean, EventsArgs['detailsData'], object | undefined] => {
const { data } = useKibana().services;
const refetch = useRef(noop);
const abortCtrl = useRef(new AbortController());
@@ -55,6 +55,8 @@ export const useTimelineEventsDetails = ({
const [timelineDetailsResponse, setTimelineDetailsResponse] =
useState(null);
+ const [rawEventData, setRawEventData] = useState