mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][Endpoint] Updates to Endpoint artifacts Find API in support of spaces (#212696)
## Summary Changes in support of Spaces, which is currently behind feature flag `endpointManagementSpaceAwarenessEnabled`: - Artifacts `find` API (via `lists` plugin extension points) were updated to append additional filtering criteria to the request that ensure only data scoped to the active space is returned. The Find API will return: - global artifacts - per-policy artifacts currently assigned to policies that are visible in the current space - per-policy artifacts currently NOT assigned to any policies that have an owner space id that matches the active space (dangling artifats) - Artifacts `get`-one API was updated to validate that user can read artifact in active space. Read of single artifact will be allowed: - artifact is global - If per-space artifact is assigned to policies and at least one policy is visible in active space - if per-space artifact is not assigned to any policies but its owner space matches the active space - if user has the global artifact management privilege - In addition to API integration tests for the `find`/`get` APIs, tests were also added for endpoint exceptions - Consolidated endpoint exceptions generator (`endpoint_exceptions_generator.ts`) into `EndpointListItemGenerator` for better reuse
This commit is contained in:
parent
4a67b8b3af
commit
df78cbbedd
26 changed files with 986 additions and 281 deletions
|
@ -1,85 +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 type {
|
||||
CreateExceptionListItemSchema,
|
||||
ExceptionListItemSchema,
|
||||
ListOperatorType,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { LIST_ITEM_ENTRY_OPERATOR_TYPES } from './common/artifact_list_item_entry_values';
|
||||
import { exceptionItemToCreateExceptionItem } from './exceptions_list_item_generator';
|
||||
import { BaseDataGenerator } from './base_data_generator';
|
||||
import { GLOBAL_ARTIFACT_TAG } from '../service/artifacts';
|
||||
import { ENDPOINT_EVENTS_LOG_INDEX_FIELDS } from './common/alerts_ecs_fields';
|
||||
|
||||
export class EndpointExceptionsGenerator extends BaseDataGenerator<ExceptionListItemSchema> {
|
||||
generate(overrides: Partial<ExceptionListItemSchema> = {}): ExceptionListItemSchema {
|
||||
return {
|
||||
name: `Generated Exception (${this.randomString(5)})`,
|
||||
comments: [],
|
||||
description: 'created by EndpointExceptionsGenerator',
|
||||
id: this.seededUUIDv4(),
|
||||
item_id: this.seededUUIDv4(),
|
||||
list_id: ENDPOINT_LIST_ID,
|
||||
tags: [GLOBAL_ARTIFACT_TAG],
|
||||
entries: this.randomEndpointExceptionEntries(1),
|
||||
meta: undefined,
|
||||
namespace_type: 'agnostic',
|
||||
os_types: [this.randomOSFamily()] as ExceptionListItemSchema['os_types'],
|
||||
created_at: this.randomPastDate(),
|
||||
created_by: this.randomUser(),
|
||||
updated_at: '2020-04-20T15:25:31.830Z',
|
||||
expire_time: undefined,
|
||||
updated_by: this.randomUser(),
|
||||
_version: this.randomString(5),
|
||||
type: 'simple',
|
||||
tie_breaker_id: this.seededUUIDv4(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
generateEndpointExceptionForCreate(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {}
|
||||
): CreateExceptionListItemSchema {
|
||||
return {
|
||||
...exceptionItemToCreateExceptionItem(this.generate()),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
protected randomEndpointExceptionEntries(
|
||||
count: number = this.randomN(5)
|
||||
): ExceptionListItemSchema['entries'] {
|
||||
const operatorTypes = LIST_ITEM_ENTRY_OPERATOR_TYPES.filter(
|
||||
(item) =>
|
||||
!(
|
||||
[
|
||||
ListOperatorTypeEnum.LIST,
|
||||
ListOperatorTypeEnum.NESTED,
|
||||
ListOperatorTypeEnum.EXISTS,
|
||||
] as ListOperatorType[]
|
||||
).includes(item)
|
||||
);
|
||||
const fieldList = ENDPOINT_EVENTS_LOG_INDEX_FIELDS.filter((field) => field.endsWith('.text'));
|
||||
|
||||
return Array.from({ length: count || 1 }, () => {
|
||||
const operatorType = this.randomChoice(operatorTypes);
|
||||
|
||||
return {
|
||||
field: this.randomChoice(fieldList),
|
||||
operator: 'included',
|
||||
type: operatorType,
|
||||
value:
|
||||
operatorType === ListOperatorTypeEnum.MATCH_ANY
|
||||
? [this.randomString(10), this.randomString(10)]
|
||||
: this.randomString(10),
|
||||
};
|
||||
}) as ExceptionListItemSchema['entries'];
|
||||
}
|
||||
}
|
|
@ -11,10 +11,16 @@ import type {
|
|||
UpdateExceptionListItemSchema,
|
||||
EntriesArray,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants';
|
||||
import {
|
||||
ListOperatorTypeEnum,
|
||||
type ListOperatorType,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ENDPOINT_ARTIFACT_LISTS, ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { ConditionEntryField } from '@kbn/securitysolution-utils';
|
||||
import { LIST_ITEM_ENTRY_OPERATOR_TYPES } from './common/artifact_list_item_entry_values';
|
||||
import { BaseDataGenerator } from './base_data_generator';
|
||||
import { BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG } from '../service/artifacts/constants';
|
||||
import { ENDPOINT_EVENTS_LOG_INDEX_FIELDS } from './common/alerts_ecs_fields';
|
||||
|
||||
/** Utility that removes null and undefined from a Type's property value */
|
||||
type NonNullableTypeProperties<T> = {
|
||||
|
@ -143,6 +149,57 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList
|
|||
return Object.assign(exceptionItemToCreateExceptionItem(this.generate()), overrides);
|
||||
}
|
||||
|
||||
generateEndpointException(
|
||||
overrides: Partial<ExceptionListItemSchema> = {}
|
||||
): ExceptionListItemSchema {
|
||||
return this.generate({
|
||||
name: `Endpoint exception (${this.randomString(5)})`,
|
||||
list_id: ENDPOINT_LIST_ID,
|
||||
entries: this.randomEndpointExceptionEntries(1),
|
||||
tags: [],
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
generateEndpointExceptionForCreate(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {}
|
||||
): CreateExceptionListItemSchema {
|
||||
return {
|
||||
...exceptionItemToCreateExceptionItem(this.generateEndpointException()),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
protected randomEndpointExceptionEntries(
|
||||
count: number = this.randomN(5)
|
||||
): ExceptionListItemSchema['entries'] {
|
||||
const operatorTypes = LIST_ITEM_ENTRY_OPERATOR_TYPES.filter(
|
||||
(item) =>
|
||||
!(
|
||||
[
|
||||
ListOperatorTypeEnum.LIST,
|
||||
ListOperatorTypeEnum.NESTED,
|
||||
ListOperatorTypeEnum.EXISTS,
|
||||
] as ListOperatorType[]
|
||||
).includes(item)
|
||||
);
|
||||
const fieldList = ENDPOINT_EVENTS_LOG_INDEX_FIELDS.filter((field) => field.endsWith('.text'));
|
||||
|
||||
return Array.from({ length: count || 1 }, () => {
|
||||
const operatorType = this.randomChoice(operatorTypes);
|
||||
|
||||
return {
|
||||
field: this.randomChoice(fieldList),
|
||||
operator: 'included',
|
||||
type: operatorType,
|
||||
value:
|
||||
operatorType === ListOperatorTypeEnum.MATCH_ANY
|
||||
? [this.randomString(10), this.randomString(10)]
|
||||
: this.randomString(10),
|
||||
};
|
||||
}) as ExceptionListItemSchema['entries'];
|
||||
}
|
||||
|
||||
generateTrustedApp(overrides: Partial<ExceptionListItemSchema> = {}): ExceptionListItemSchema {
|
||||
return this.generate({
|
||||
name: `Trusted app (${this.randomString(5)})`,
|
||||
|
|
|
@ -10,7 +10,6 @@ import type { ToolingLog } from '@kbn/tooling-log';
|
|||
import { BaseDataGenerator } from '../../../../common/endpoint/data_generators/base_data_generator';
|
||||
import type { NewTrustedApp } from '../../../../common/endpoint/types';
|
||||
import { stringify } from '../../../../server/endpoint/utils/stringify';
|
||||
import { EndpointExceptionsGenerator } from '../../../../common/endpoint/data_generators/endpoint_exceptions_generator';
|
||||
import {
|
||||
BY_POLICY_ARTIFACT_TAG_PREFIX,
|
||||
GLOBAL_ARTIFACT_TAG,
|
||||
|
@ -276,7 +275,7 @@ export const createEndpointExceptions = async ({
|
|||
reportProgress,
|
||||
throttler,
|
||||
}: ArtifactCreationOptions): Promise<void> => {
|
||||
const generate = new EndpointExceptionsGenerator();
|
||||
const generate = new ExceptionsListItemGenerator();
|
||||
let doneCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
|||
CreateExceptionListItemOptions,
|
||||
ExceptionsListPreCreateItemServerExtension,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
import { EndpointArtifactExceptionValidationError } from '../validators/errors';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
import {
|
||||
BlocklistValidator,
|
||||
|
@ -17,63 +18,80 @@ import {
|
|||
HostIsolationExceptionsValidator,
|
||||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
import { setArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreCreateItemServerExtension['callback'];
|
||||
export const getExceptionsPreCreateItemHandler = (
|
||||
endpointAppContext: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreCreateItemServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request } }): Promise<CreateExceptionListItemOptions> {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
}
|
||||
|
||||
let isEndpointArtifact = false;
|
||||
let validatedItem = data;
|
||||
|
||||
// Validate trusted apps
|
||||
if (TrustedAppValidator.isTrustedApp(data)) {
|
||||
isEndpointArtifact = true;
|
||||
const trustedAppValidator = new TrustedAppValidator(endpointAppContext, request);
|
||||
const validatedItem = await trustedAppValidator.validatePreCreateItem(data);
|
||||
validatedItem = await trustedAppValidator.validatePreCreateItem(data);
|
||||
trustedAppValidator.notifyFeatureUsage(data, 'TRUSTED_APP_BY_POLICY');
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate event filter
|
||||
if (EventFilterValidator.isEventFilter(data)) {
|
||||
isEndpointArtifact = true;
|
||||
const eventFilterValidator = new EventFilterValidator(endpointAppContext, request);
|
||||
const validatedItem = await eventFilterValidator.validatePreCreateItem(data);
|
||||
validatedItem = await eventFilterValidator.validatePreCreateItem(data);
|
||||
eventFilterValidator.notifyFeatureUsage(data, 'EVENT_FILTERS_BY_POLICY');
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate host isolation
|
||||
if (HostIsolationExceptionsValidator.isHostIsolationException(data)) {
|
||||
isEndpointArtifact = true;
|
||||
const hostIsolationExceptionsValidator = new HostIsolationExceptionsValidator(
|
||||
endpointAppContext,
|
||||
request
|
||||
);
|
||||
const validatedItem = await hostIsolationExceptionsValidator.validatePreCreateItem(data);
|
||||
validatedItem = await hostIsolationExceptionsValidator.validatePreCreateItem(data);
|
||||
hostIsolationExceptionsValidator.notifyFeatureUsage(
|
||||
data,
|
||||
'HOST_ISOLATION_EXCEPTION_BY_POLICY'
|
||||
);
|
||||
hostIsolationExceptionsValidator.notifyFeatureUsage(data, 'HOST_ISOLATION_EXCEPTION');
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate blocklists
|
||||
if (BlocklistValidator.isBlocklist(data)) {
|
||||
isEndpointArtifact = true;
|
||||
const blocklistValidator = new BlocklistValidator(endpointAppContext, request);
|
||||
const validatedItem = await blocklistValidator.validatePreCreateItem(data);
|
||||
validatedItem = await blocklistValidator.validatePreCreateItem(data);
|
||||
blocklistValidator.notifyFeatureUsage(data, 'BLOCKLIST_BY_POLICY');
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// validate endpoint exceptions
|
||||
if (EndpointExceptionsValidator.isEndpointException(data)) {
|
||||
isEndpointArtifact = true;
|
||||
const endpointExceptionValidator = new EndpointExceptionsValidator(
|
||||
endpointAppContext,
|
||||
request
|
||||
);
|
||||
const validatedItem = await endpointExceptionValidator.validatePreCreateItem(data);
|
||||
validatedItem = await endpointExceptionValidator.validatePreCreateItem(data);
|
||||
endpointExceptionValidator.notifyFeatureUsage(data, 'ENDPOINT_EXCEPTIONS');
|
||||
}
|
||||
|
||||
if (
|
||||
isEndpointArtifact &&
|
||||
endpointAppContext.experimentalFeatures.endpointManagementSpaceAwarenessEnabled
|
||||
) {
|
||||
if (!request) {
|
||||
throw new EndpointArtifactExceptionValidationError(`Missing HTTP Request object`);
|
||||
}
|
||||
|
||||
const spaceId = (await endpointAppContext.getActiveSpace(request)).id;
|
||||
setArtifactOwnerSpaceId(validatedItem, spaceId);
|
||||
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,9 @@ import {
|
|||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreDeleteItemServerExtension['callback'];
|
||||
export const getExceptionsPreDeleteItemHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreDeleteItemServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request, exceptionListClient } }) {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
|
|
|
@ -15,10 +15,9 @@ import {
|
|||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreExportServerExtension['callback'];
|
||||
export const getExceptionsPreExportHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreExportServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request, exceptionListClient } }) {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
|
|
|
@ -16,10 +16,9 @@ import {
|
|||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreGetOneItemServerExtension['callback'];
|
||||
export const getExceptionsPreGetOneHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreGetOneItemServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request, exceptionListClient } }) {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
|
@ -40,7 +39,9 @@ export const getExceptionsPreGetOneHandler = (
|
|||
|
||||
// Validate Trusted Applications
|
||||
if (TrustedAppValidator.isTrustedApp({ listId })) {
|
||||
await new TrustedAppValidator(endpointAppContextService, request).validatePreGetOneItem();
|
||||
await new TrustedAppValidator(endpointAppContextService, request).validatePreGetOneItem(
|
||||
exceptionItem
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -49,19 +50,23 @@ export const getExceptionsPreGetOneHandler = (
|
|||
await new HostIsolationExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreGetOneItem();
|
||||
).validatePreGetOneItem(exceptionItem);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Event Filters Exception
|
||||
if (EventFilterValidator.isEventFilter({ listId })) {
|
||||
await new EventFilterValidator(endpointAppContextService, request).validatePreGetOneItem();
|
||||
await new EventFilterValidator(endpointAppContextService, request).validatePreGetOneItem(
|
||||
exceptionItem
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate Blocklists
|
||||
if (BlocklistValidator.isBlocklist({ listId })) {
|
||||
await new BlocklistValidator(endpointAppContextService, request).validatePreGetOneItem();
|
||||
await new BlocklistValidator(endpointAppContextService, request).validatePreGetOneItem(
|
||||
exceptionItem
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -70,7 +75,7 @@ export const getExceptionsPreGetOneHandler = (
|
|||
await new EndpointExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreGetOneItem();
|
||||
).validatePreGetOneItem(exceptionItem);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,23 +9,23 @@ import type { ExceptionsListPreImportServerExtension } from '@kbn/lists-plugin/s
|
|||
import { EndpointArtifactExceptionValidationError } from '../validators/errors';
|
||||
import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../../../common/endpoint/service/artifacts/constants';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreImportServerExtension['callback'];
|
||||
export const getExceptionsPreImportHandler = (): ValidatorCallback => {
|
||||
return async ({ data }) => {
|
||||
const hasEndpointArtifactListOrListItems = [...data.lists, ...data.items].some((item) => {
|
||||
if ('list_id' in item) {
|
||||
return ALL_ENDPOINT_ARTIFACT_LIST_IDS.includes(item.list_id);
|
||||
export const getExceptionsPreImportHandler =
|
||||
(): ExceptionsListPreImportServerExtension['callback'] => {
|
||||
return async ({ data }) => {
|
||||
const hasEndpointArtifactListOrListItems = [...data.lists, ...data.items].some((item) => {
|
||||
if ('list_id' in item) {
|
||||
return ALL_ENDPOINT_ARTIFACT_LIST_IDS.includes(item.list_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasEndpointArtifactListOrListItems) {
|
||||
throw new EndpointArtifactExceptionValidationError(
|
||||
'Import is not supported for Endpoint artifact exceptions'
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (hasEndpointArtifactListOrListItems) {
|
||||
throw new EndpointArtifactExceptionValidationError(
|
||||
'Import is not supported for Endpoint artifact exceptions'
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
return data;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { ExceptionsListPreMultiListFindServerExtension } from '@kbn/lists-plugin/server';
|
||||
import { EndpointArtifactExceptionValidationError } from '../validators/errors';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
import {
|
||||
BlocklistValidator,
|
||||
|
@ -14,54 +15,58 @@ import {
|
|||
HostIsolationExceptionsValidator,
|
||||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
import { setFindRequestFilterScopeToActiveSpace } from '../utils';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreMultiListFindServerExtension['callback'];
|
||||
export const getExceptionsPreMultiListFindHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreMultiListFindServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request } }) {
|
||||
if (!data.namespaceType.includes('agnostic')) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// validate Trusted application
|
||||
if (data.listId.some((id) => TrustedAppValidator.isTrustedApp({ listId: id }))) {
|
||||
await new TrustedAppValidator(endpointAppContextService, request).validatePreMultiListFind();
|
||||
return data;
|
||||
}
|
||||
let isEndpointArtifact = false;
|
||||
|
||||
// Validate Host Isolation Exceptions
|
||||
if (
|
||||
if (data.listId.some((id) => TrustedAppValidator.isTrustedApp({ listId: id }))) {
|
||||
// validate Trusted application
|
||||
isEndpointArtifact = true;
|
||||
await new TrustedAppValidator(endpointAppContextService, request).validatePreMultiListFind();
|
||||
} else if (
|
||||
data.listId.some((listId) =>
|
||||
HostIsolationExceptionsValidator.isHostIsolationException({ listId })
|
||||
)
|
||||
) {
|
||||
// Validate Host Isolation Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new HostIsolationExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreMultiListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Event Filters Exceptions
|
||||
if (data.listId.some((listId) => EventFilterValidator.isEventFilter({ listId }))) {
|
||||
} else if (data.listId.some((listId) => EventFilterValidator.isEventFilter({ listId }))) {
|
||||
// Event Filters Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new EventFilterValidator(endpointAppContextService, request).validatePreMultiListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// validate Blocklist
|
||||
if (data.listId.some((id) => BlocklistValidator.isBlocklist({ listId: id }))) {
|
||||
} else if (data.listId.some((id) => BlocklistValidator.isBlocklist({ listId: id }))) {
|
||||
// validate Blocklist
|
||||
isEndpointArtifact = true;
|
||||
await new BlocklistValidator(endpointAppContextService, request).validatePreMultiListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate Endpoint Exceptions
|
||||
if (data.listId.some((id) => EndpointExceptionsValidator.isEndpointException({ listId: id }))) {
|
||||
} else if (
|
||||
data.listId.some((id) => EndpointExceptionsValidator.isEndpointException({ listId: id }))
|
||||
) {
|
||||
// Validate Endpoint Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new EndpointExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreMultiListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
if (isEndpointArtifact) {
|
||||
if (!request) {
|
||||
throw new EndpointArtifactExceptionValidationError(`Missing HTTP Request object`);
|
||||
}
|
||||
|
||||
await setFindRequestFilterScopeToActiveSpace(endpointAppContextService, request, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { ExceptionsListPreSingleListFindServerExtension } from '@kbn/lists-plugin/server';
|
||||
import { EndpointArtifactExceptionValidationError } from '../validators/errors';
|
||||
import { setFindRequestFilterScopeToActiveSpace } from '../utils';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
import {
|
||||
BlocklistValidator,
|
||||
|
@ -15,54 +17,63 @@ import {
|
|||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreSingleListFindServerExtension['callback'];
|
||||
export const getExceptionsPreSingleListFindHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreSingleListFindServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request } }) {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
}
|
||||
|
||||
let isEndpointArtifact = false;
|
||||
|
||||
const { listId } = data;
|
||||
|
||||
// Validate Trusted applications
|
||||
if (TrustedAppValidator.isTrustedApp({ listId })) {
|
||||
// Validate Trusted applications
|
||||
isEndpointArtifact = true;
|
||||
await new TrustedAppValidator(endpointAppContextService, request).validatePreSingleListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Host Isolation Exceptions
|
||||
if (HostIsolationExceptionsValidator.isHostIsolationException({ listId })) {
|
||||
} else if (HostIsolationExceptionsValidator.isHostIsolationException({ listId })) {
|
||||
// Host Isolation Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new HostIsolationExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreSingleListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Event Filters Exceptions
|
||||
if (EventFilterValidator.isEventFilter({ listId })) {
|
||||
} else if (EventFilterValidator.isEventFilter({ listId })) {
|
||||
// Event Filters Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new EventFilterValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreSingleListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate Blocklists
|
||||
if (BlocklistValidator.isBlocklist({ listId })) {
|
||||
await new BlocklistValidator(endpointAppContextService, request).validatePreSingleListFind();
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate Endpoint Exceptions
|
||||
if (EndpointExceptionsValidator.isEndpointException({ listId })) {
|
||||
} else if (EndpointExceptionsValidator.isEndpointException({ listId })) {
|
||||
// Validate Endpoint Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new EndpointExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreSingleListFind();
|
||||
return data;
|
||||
} else if (EndpointExceptionsValidator.isEndpointException({ listId })) {
|
||||
// Validate Endpoint Exceptions
|
||||
isEndpointArtifact = true;
|
||||
await new EndpointExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
).validatePreSingleListFind();
|
||||
}
|
||||
|
||||
if (isEndpointArtifact) {
|
||||
if (!request) {
|
||||
throw new EndpointArtifactExceptionValidationError(`Missing HTTP Request object`);
|
||||
}
|
||||
|
||||
await setFindRequestFilterScopeToActiveSpace(endpointAppContextService, request, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
|
@ -15,10 +15,9 @@ import {
|
|||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreSummaryServerExtension['callback'];
|
||||
export const getExceptionsPreSummaryHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreSummaryServerExtension['callback'] => {
|
||||
return async function ({ data, context: { request, exceptionListClient } }) {
|
||||
if (data.namespaceType !== 'agnostic') {
|
||||
return data;
|
||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
|||
ExceptionsListPreUpdateItemServerExtension,
|
||||
UpdateExceptionListItemOptions,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
import { EndpointArtifactExceptionValidationError } from '../validators/errors';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
import {
|
||||
|
@ -18,11 +19,14 @@ import {
|
|||
HostIsolationExceptionsValidator,
|
||||
TrustedAppValidator,
|
||||
} from '../validators';
|
||||
import {
|
||||
hasArtifactOwnerSpaceId,
|
||||
setArtifactOwnerSpaceId,
|
||||
} from '../../../../common/endpoint/service/artifacts/utils';
|
||||
|
||||
type ValidatorCallback = ExceptionsListPreUpdateItemServerExtension['callback'];
|
||||
export const getExceptionsPreUpdateItemHandler = (
|
||||
endpointAppContextService: EndpointAppContextService
|
||||
): ValidatorCallback => {
|
||||
): ExceptionsListPreUpdateItemServerExtension['callback'] => {
|
||||
return async function ({
|
||||
data,
|
||||
context: { request, exceptionListClient },
|
||||
|
@ -31,6 +35,8 @@ export const getExceptionsPreUpdateItemHandler = (
|
|||
return data;
|
||||
}
|
||||
|
||||
let isEndpointArtifact = false;
|
||||
let validatedItem = data;
|
||||
const currentSavedItem = await exceptionListClient.getExceptionListItem({
|
||||
id: data.id,
|
||||
itemId: data.itemId,
|
||||
|
@ -47,36 +53,34 @@ export const getExceptionsPreUpdateItemHandler = (
|
|||
|
||||
// Validate Trusted Applications
|
||||
if (TrustedAppValidator.isTrustedApp({ listId })) {
|
||||
isEndpointArtifact = true;
|
||||
const trustedAppValidator = new TrustedAppValidator(endpointAppContextService, request);
|
||||
const validatedItem = await trustedAppValidator.validatePreUpdateItem(data, currentSavedItem);
|
||||
validatedItem = await trustedAppValidator.validatePreUpdateItem(data, currentSavedItem);
|
||||
trustedAppValidator.notifyFeatureUsage(
|
||||
data as ExceptionItemLikeOptions,
|
||||
'TRUSTED_APP_BY_POLICY'
|
||||
);
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate Event Filters
|
||||
if (EventFilterValidator.isEventFilter({ listId })) {
|
||||
isEndpointArtifact = true;
|
||||
const eventFilterValidator = new EventFilterValidator(endpointAppContextService, request);
|
||||
const validatedItem = await eventFilterValidator.validatePreUpdateItem(
|
||||
data,
|
||||
currentSavedItem
|
||||
);
|
||||
validatedItem = await eventFilterValidator.validatePreUpdateItem(data, currentSavedItem);
|
||||
eventFilterValidator.notifyFeatureUsage(
|
||||
data as ExceptionItemLikeOptions,
|
||||
'EVENT_FILTERS_BY_POLICY'
|
||||
);
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate host isolation
|
||||
if (HostIsolationExceptionsValidator.isHostIsolationException({ listId })) {
|
||||
isEndpointArtifact = true;
|
||||
const hostIsolationExceptionValidator = new HostIsolationExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
);
|
||||
const validatedItem = await hostIsolationExceptionValidator.validatePreUpdateItem(
|
||||
validatedItem = await hostIsolationExceptionValidator.validatePreUpdateItem(
|
||||
data,
|
||||
currentSavedItem
|
||||
);
|
||||
|
@ -88,27 +92,27 @@ export const getExceptionsPreUpdateItemHandler = (
|
|||
data as ExceptionItemLikeOptions,
|
||||
'HOST_ISOLATION_EXCEPTION'
|
||||
);
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate Blocklists
|
||||
if (BlocklistValidator.isBlocklist({ listId })) {
|
||||
isEndpointArtifact = true;
|
||||
const blocklistValidator = new BlocklistValidator(endpointAppContextService, request);
|
||||
const validatedItem = await blocklistValidator.validatePreUpdateItem(data, currentSavedItem);
|
||||
validatedItem = await blocklistValidator.validatePreUpdateItem(data, currentSavedItem);
|
||||
blocklistValidator.notifyFeatureUsage(
|
||||
data as ExceptionItemLikeOptions,
|
||||
'BLOCKLIST_BY_POLICY'
|
||||
);
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
// Validate Endpoint Exceptions
|
||||
if (EndpointExceptionsValidator.isEndpointException({ listId })) {
|
||||
isEndpointArtifact = true;
|
||||
const endpointExceptionValidator = new EndpointExceptionsValidator(
|
||||
endpointAppContextService,
|
||||
request
|
||||
);
|
||||
const validatedItem = await endpointExceptionValidator.validatePreUpdateItem(
|
||||
validatedItem = await endpointExceptionValidator.validatePreUpdateItem(
|
||||
data,
|
||||
currentSavedItem
|
||||
);
|
||||
|
@ -116,6 +120,20 @@ export const getExceptionsPreUpdateItemHandler = (
|
|||
data as ExceptionItemLikeOptions,
|
||||
'ENDPOINT_EXCEPTIONS'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isEndpointArtifact &&
|
||||
endpointAppContextService.experimentalFeatures.endpointManagementSpaceAwarenessEnabled
|
||||
) {
|
||||
if (!hasArtifactOwnerSpaceId(validatedItem)) {
|
||||
if (!request) {
|
||||
throw new EndpointArtifactExceptionValidationError(`Missing HTTP Request object`);
|
||||
}
|
||||
const spaceId = (await endpointAppContextService.getActiveSpace(request)).id;
|
||||
setArtifactOwnerSpaceId(validatedItem, spaceId);
|
||||
}
|
||||
|
||||
return validatedItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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 './set_find_request_filter_scope_to_active_space';
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 {
|
||||
FindExceptionListItemOptions,
|
||||
FindExceptionListsItemOptions,
|
||||
} from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client_types';
|
||||
import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants';
|
||||
import { setFindRequestFilterScopeToActiveSpace } from './set_find_request_filter_scope_to_active_space';
|
||||
import { createMockEndpointAppContextService } from '../../../endpoint/mocks';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
import { httpServerMock } from '@kbn/core-http-server-mocks';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
|
||||
describe('Artifacts: setFindRequestFilterScopeToActiveSpace()', () => {
|
||||
let endpointAppContextServices: EndpointAppContextService;
|
||||
let kibanaRequest: ReturnType<typeof httpServerMock.createKibanaRequest>;
|
||||
let findOptionsMock: FindExceptionListItemOptions | FindExceptionListsItemOptions;
|
||||
|
||||
beforeEach(() => {
|
||||
endpointAppContextServices = createMockEndpointAppContextService();
|
||||
kibanaRequest = httpServerMock.createKibanaRequest();
|
||||
|
||||
(
|
||||
endpointAppContextServices.getInternalFleetServices()
|
||||
.packagePolicy as jest.Mocked<PackagePolicyClient>
|
||||
).listIds.mockResolvedValue({
|
||||
items: ['policy-1', 'policy-2'],
|
||||
total: 2,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
});
|
||||
|
||||
findOptionsMock = {
|
||||
listId: ENDPOINT_ARTIFACT_LISTS.trustedApps.id,
|
||||
namespaceType: 'agnostic',
|
||||
filter: undefined,
|
||||
perPage: 20,
|
||||
page: 1,
|
||||
sortField: undefined,
|
||||
sortOrder: undefined,
|
||||
};
|
||||
|
||||
// @ts-expect-error updating a readonly field
|
||||
endpointAppContextServices.experimentalFeatures.endpointManagementSpaceAwarenessEnabled = true;
|
||||
});
|
||||
|
||||
it('should nothing if feature flag is disabled', async () => {
|
||||
// @ts-expect-error updating a readonly field
|
||||
endpointAppContextServices.experimentalFeatures.endpointManagementSpaceAwarenessEnabled = false;
|
||||
await setFindRequestFilterScopeToActiveSpace(
|
||||
endpointAppContextServices,
|
||||
kibanaRequest,
|
||||
findOptionsMock
|
||||
);
|
||||
|
||||
expect(findOptionsMock.filter).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should inject additional filtering into find request', async () => {
|
||||
await setFindRequestFilterScopeToActiveSpace(
|
||||
endpointAppContextServices,
|
||||
kibanaRequest,
|
||||
findOptionsMock
|
||||
);
|
||||
|
||||
expect(findOptionsMock.filter).toEqual(`
|
||||
(
|
||||
(
|
||||
exception-list-agnostic.attributes.tags:("policy:all" OR "policy:policy-1" OR "policy:policy-2"
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
NOT exception-list-agnostic.attributes.tags:"policy:*"
|
||||
AND
|
||||
exception-list-agnostic.attributes.tags:"ownerSpaceId:default"
|
||||
)
|
||||
)`);
|
||||
});
|
||||
|
||||
it('should inject additional filtering into find request when it already has a filter value', async () => {
|
||||
findOptionsMock.filter = 'somevalue:match-this';
|
||||
await setFindRequestFilterScopeToActiveSpace(
|
||||
endpointAppContextServices,
|
||||
kibanaRequest,
|
||||
findOptionsMock
|
||||
);
|
||||
|
||||
expect(findOptionsMock.filter).toEqual(`
|
||||
(
|
||||
(
|
||||
exception-list-agnostic.attributes.tags:("policy:all" OR "policy:policy-1" OR "policy:policy-2"
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
NOT exception-list-agnostic.attributes.tags:"policy:*"
|
||||
AND
|
||||
exception-list-agnostic.attributes.tags:"ownerSpaceId:default"
|
||||
)
|
||||
) AND (somevalue:match-this)`);
|
||||
});
|
||||
|
||||
it('should inject additional filtering when using multi-list search format', async () => {
|
||||
findOptionsMock.listId = [
|
||||
ENDPOINT_ARTIFACT_LISTS.trustedApps.id,
|
||||
ENDPOINT_ARTIFACT_LISTS.eventFilters.id,
|
||||
];
|
||||
findOptionsMock.filter = ['', 'somevalue:match-this'];
|
||||
await setFindRequestFilterScopeToActiveSpace(
|
||||
endpointAppContextServices,
|
||||
kibanaRequest,
|
||||
findOptionsMock
|
||||
);
|
||||
|
||||
expect(findOptionsMock.filter).toEqual([
|
||||
'\n (\n (\n exception-list-agnostic.attributes.tags:("policy:all" OR "policy:policy-1" OR "policy:policy-2"\n )\n )\n OR\n (\n NOT exception-list-agnostic.attributes.tags:"policy:*"\n AND\n exception-list-agnostic.attributes.tags:"ownerSpaceId:default"\n )\n )',
|
||||
'\n (\n (\n exception-list-agnostic.attributes.tags:("policy:all" OR "policy:policy-1" OR "policy:policy-2"\n )\n )\n OR\n (\n NOT exception-list-agnostic.attributes.tags:"policy:*"\n AND\n exception-list-agnostic.attributes.tags:"ownerSpaceId:default"\n )\n ) AND (somevalue:match-this)',
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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 {
|
||||
FindExceptionListItemOptions,
|
||||
FindExceptionListsItemOptions,
|
||||
} from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client_types';
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { stringify } from '../../../endpoint/utils/stringify';
|
||||
import { GLOBAL_ARTIFACT_TAG } from '../../../../common/endpoint/service/artifacts';
|
||||
import {
|
||||
buildPerPolicyTag,
|
||||
buildSpaceOwnerIdTag,
|
||||
} from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services';
|
||||
|
||||
/**
|
||||
* Mutates the Find options provided on input to include a filter that will scope the search
|
||||
* down to only data that should be visible in active space
|
||||
* @param endpointServices
|
||||
* @param httpRequest
|
||||
* @param findOptions
|
||||
*/
|
||||
export const setFindRequestFilterScopeToActiveSpace = async (
|
||||
endpointServices: EndpointAppContextService,
|
||||
httpRequest: KibanaRequest,
|
||||
findOptions: FindExceptionListItemOptions | FindExceptionListsItemOptions
|
||||
): Promise<void> => {
|
||||
if (endpointServices.experimentalFeatures.endpointManagementSpaceAwarenessEnabled) {
|
||||
const logger = endpointServices.createLogger('setFindRequestFilterScopeToActiveSpace');
|
||||
|
||||
logger.debug(() => `Find options prior to adjusting filter:\n${stringify(findOptions)}`);
|
||||
|
||||
const spaceId = (await endpointServices.getActiveSpace(httpRequest)).id;
|
||||
const fleetServices = endpointServices.getInternalFleetServices(spaceId);
|
||||
const soScopedClient = fleetServices.savedObjects.createInternalScopedSoClient({ spaceId });
|
||||
const { items: allEndpointPolicyIds } = await fleetServices.packagePolicy.listIds(
|
||||
soScopedClient,
|
||||
{ kuery: fleetServices.endpointPolicyKuery, perPage: 10_000 }
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
() =>
|
||||
`policies currently visible in space ID [${spaceId}]:\n${stringify(allEndpointPolicyIds)}`
|
||||
);
|
||||
|
||||
// Filter to scope down the data visible in active space id by appending to the Find options the following filter:
|
||||
// (
|
||||
// All global artifacts
|
||||
// -OR-
|
||||
// All per-policy artifacts assigned to a policy visible in active space
|
||||
// )
|
||||
// -OR-
|
||||
// (
|
||||
// Artifacts NOT containing a `policy:` tag ("dangling" per-policy artifacts)
|
||||
// -AND-
|
||||
// having an owner space ID value that matches active space
|
||||
// )
|
||||
//
|
||||
const spaceVisibleDataFilter = `
|
||||
(
|
||||
(
|
||||
exception-list-agnostic.attributes.tags:("${GLOBAL_ARTIFACT_TAG}"${
|
||||
allEndpointPolicyIds.length === 0
|
||||
? ')'
|
||||
: ` OR ${allEndpointPolicyIds
|
||||
.map((policyId) => `"${buildPerPolicyTag(policyId)}"`)
|
||||
.join(' OR ')}
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
NOT exception-list-agnostic.attributes.tags:"${buildPerPolicyTag('*')}"
|
||||
AND
|
||||
exception-list-agnostic.attributes.tags:"${buildSpaceOwnerIdTag(spaceId)}"
|
||||
)
|
||||
)`
|
||||
}`;
|
||||
|
||||
if (isSingleListFindOptions(findOptions)) {
|
||||
findOptions.filter = `${spaceVisibleDataFilter}${
|
||||
findOptions.filter ? ` AND (${findOptions.filter})` : ''
|
||||
}`;
|
||||
} else {
|
||||
if (!findOptions.filter) {
|
||||
findOptions.filter = [];
|
||||
}
|
||||
|
||||
// Add the filter for every list that was defined in the options
|
||||
findOptions.listId.forEach((listId, index) => {
|
||||
const userFilter = findOptions.filter[index];
|
||||
findOptions.filter[index] = `${spaceVisibleDataFilter}${
|
||||
userFilter ? ` AND (${userFilter})` : ''
|
||||
}`;
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug(() => `Find options updated with active space filter:\n${stringify(findOptions)}`);
|
||||
}
|
||||
};
|
||||
|
||||
const isSingleListFindOptions = (
|
||||
findOptions: FindExceptionListItemOptions | FindExceptionListsItemOptions
|
||||
): findOptions is FindExceptionListItemOptions => {
|
||||
return !Array.isArray(findOptions.listId);
|
||||
};
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { EndpointArtifactExceptionValidationError } from './errors';
|
||||
import { httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
import { createFleetAuthzMock, createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
import {
|
||||
|
@ -227,6 +227,18 @@ describe('When using Artifacts Exceptions BaseValidator', () => {
|
|||
(endpointAppContextServices.getEndpointAuthz as jest.Mock).mockResolvedValue(authzMock);
|
||||
setArtifactOwnerSpaceId(exceptionLikeItem, DEFAULT_SPACE_ID);
|
||||
validator = new BaseValidatorMock(endpointAppContextServices, kibanaRequest);
|
||||
packagePolicyService = endpointAppContextServices.getInternalFleetServices()
|
||||
.packagePolicy as jest.Mocked<PackagePolicyClient>;
|
||||
packagePolicyService.listIds.mockResolvedValue({
|
||||
items: ['policy-1', 'policy-2'],
|
||||
total: 2,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
});
|
||||
packagePolicyService.getByIDs.mockResolvedValue([
|
||||
Object.assign(createPackagePolicyMock(), { id: 'policy-1' }),
|
||||
Object.assign(createPackagePolicyMock(), { id: 'policy-2' }),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('#validateCreateOnwerSpaceIds()', () => {
|
||||
|
@ -452,5 +464,85 @@ describe('When using Artifacts Exceptions BaseValidator', () => {
|
|||
).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#validateCanReadItemInActiveSpace()', () => {
|
||||
const itemNotFoundInSpaceErrorMessage =
|
||||
'EndpointExceptionsError: Item not found in space [default]';
|
||||
let savedExceptionItem: ExceptionListItemSchema;
|
||||
|
||||
beforeEach(async () => {
|
||||
authzMock.canManageGlobalArtifacts = false;
|
||||
savedExceptionItem = createExceptionListItemMock({
|
||||
// Saved item is owned by different space id
|
||||
tags: [
|
||||
buildPerPolicyTag('some-other-policy'),
|
||||
buildPerPolicyTag('policy-1'),
|
||||
buildSpaceOwnerIdTag('foo'),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing if feature flag is disabled', async () => {
|
||||
setSpaceAwarenessFeatureFlag('disabled');
|
||||
savedExceptionItem.tags = [buildSpaceOwnerIdTag('foo')];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should allow read if item is global', async () => {
|
||||
savedExceptionItem.tags = [GLOBAL_ARTIFACT_TAG, buildSpaceOwnerIdTag('foo')];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should allow read if user has global artifact privilege', async () => {
|
||||
authzMock.canManageGlobalArtifacts = true;
|
||||
savedExceptionItem.tags = [
|
||||
buildPerPolicyTag('policy-999-not-visible-in-space'),
|
||||
buildSpaceOwnerIdTag('foo'),
|
||||
];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should allow read if item is per-policy with no policies assigned and space owner matches active space', async () => {
|
||||
savedExceptionItem.tags = [buildSpaceOwnerIdTag('default')];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should error if item is per-policy with no policies assigned but space owner is NOT the active space', async () => {
|
||||
savedExceptionItem.tags = [buildSpaceOwnerIdTag('foo')];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).rejects.toThrowError(itemNotFoundInSpaceErrorMessage);
|
||||
});
|
||||
|
||||
it('should allow read if per-policy item has at least one policy that is visible in active space', async () => {
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should error if per-policy item does not have at least 1 policy id that is visible in active space', async () => {
|
||||
savedExceptionItem.tags = [
|
||||
buildPerPolicyTag('some-other-policy'),
|
||||
buildSpaceOwnerIdTag('default'),
|
||||
];
|
||||
|
||||
await expect(
|
||||
validator._validateCanReadItemInActiveSpace(savedExceptionItem)
|
||||
).rejects.toThrowError(itemNotFoundInSpaceErrorMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,10 +12,13 @@ import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-t
|
|||
import { OperatingSystem } from '@kbn/securitysolution-utils';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {} from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client_types';
|
||||
import { groupBy } from 'lodash';
|
||||
import { stringify } from '../../../endpoint/utils/stringify';
|
||||
import { ENDPOINT_AUTHZ_ERROR_MESSAGE } from '../../../endpoint/errors';
|
||||
import {
|
||||
getArtifactOwnerSpaceIds,
|
||||
setArtifactOwnerSpaceId,
|
||||
isArtifactGlobal,
|
||||
} from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import type { FeatureKeys } from '../../../endpoint/services';
|
||||
import type { EndpointAuthz } from '../../../../common/endpoint/types/authz';
|
||||
|
@ -174,9 +177,14 @@ export class BaseValidator {
|
|||
*/
|
||||
protected async validateByPolicyItem(item: ExceptionItemLikeOptions): Promise<void> {
|
||||
if (this.isItemByPolicy(item)) {
|
||||
const { packagePolicy, savedObjects } = this.endpointAppContext.getInternalFleetServices();
|
||||
const spaceId = this.endpointAppContext.experimentalFeatures
|
||||
.endpointManagementSpaceAwarenessEnabled
|
||||
? await this.getActiveSpaceId()
|
||||
: undefined;
|
||||
const { packagePolicy, savedObjects } =
|
||||
this.endpointAppContext.getInternalFleetServices(spaceId);
|
||||
const policyIds = getPolicyIdsFromArtifact(item);
|
||||
const soClient = savedObjects.createInternalScopedSoClient();
|
||||
const soClient = savedObjects.createInternalScopedSoClient({ spaceId });
|
||||
|
||||
if (policyIds.length === 0) {
|
||||
return;
|
||||
|
@ -186,6 +194,19 @@ export class BaseValidator {
|
|||
ignoreMissing: true,
|
||||
});
|
||||
|
||||
this.logger.debug(
|
||||
() =>
|
||||
`Lookup of policy ids:\n[${policyIds.join(
|
||||
' | '
|
||||
)}] for space [${spaceId}] returned:\n${stringify(
|
||||
(policiesFromFleet ?? []).map((policy) => ({
|
||||
id: policy.id,
|
||||
name: policy.name,
|
||||
spaceIds: policy.spaceIds,
|
||||
}))
|
||||
)}`
|
||||
);
|
||||
|
||||
if (!policiesFromFleet) {
|
||||
throw new EndpointArtifactExceptionValidationError(
|
||||
`invalid policy ids: ${policyIds.join(', ')}`
|
||||
|
@ -290,19 +311,6 @@ export class BaseValidator {
|
|||
return (await this.endpointAppContext.getActiveSpace(this.request)).id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the artifact item (if necessary) with a `ownerSpaceId` tag using the HTTP request's active space
|
||||
* @param item
|
||||
* @protected
|
||||
*/
|
||||
protected async setOwnerSpaceId(
|
||||
item: Partial<Pick<ExceptionListItemSchema, 'tags'>>
|
||||
): Promise<void> {
|
||||
if (this.endpointAppContext.experimentalFeatures.endpointManagementSpaceAwarenessEnabled) {
|
||||
setArtifactOwnerSpaceId(item, await this.getActiveSpaceId());
|
||||
}
|
||||
}
|
||||
|
||||
protected async validateCanCreateGlobalArtifacts(item: ExceptionItemLikeOptions): Promise<void> {
|
||||
if (this.endpointAppContext.experimentalFeatures.endpointManagementSpaceAwarenessEnabled) {
|
||||
if (
|
||||
|
@ -376,4 +384,72 @@ export class BaseValidator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async validateCanReadItemInActiveSpace(
|
||||
currentSavedItem: ExceptionListItemSchema
|
||||
): Promise<void> {
|
||||
if (this.endpointAppContext.experimentalFeatures.endpointManagementSpaceAwarenessEnabled) {
|
||||
this.logger.debug(
|
||||
() => `Validating if can read single item:\n${stringify(currentSavedItem)}`
|
||||
);
|
||||
|
||||
// Everyone can read global artifacts and those with global artifact management privilege can do it all
|
||||
if (
|
||||
isArtifactGlobal(currentSavedItem) ||
|
||||
(await this.endpointAuthzPromise).canManageGlobalArtifacts
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeSpaceId = await this.getActiveSpaceId();
|
||||
const ownerSpaceIds = getArtifactOwnerSpaceIds(currentSavedItem);
|
||||
const policyIds = getPolicyIdsFromArtifact(currentSavedItem);
|
||||
|
||||
// If per-policy item is not assigned to any policy (dangling artifact) and this artifact
|
||||
// is owned by the active space, then allow read.
|
||||
if (policyIds.length === 0 && ownerSpaceIds.includes(activeSpaceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if at least one policy is visible in active space, then allow read
|
||||
if (policyIds.length > 0) {
|
||||
const { packagePolicy, savedObjects } =
|
||||
this.endpointAppContext.getInternalFleetServices(activeSpaceId);
|
||||
const soClient = savedObjects.createInternalScopedSoClient({ spaceId: activeSpaceId });
|
||||
const policiesFromFleet = await packagePolicy
|
||||
.getByIDs(soClient, policyIds, {
|
||||
ignoreMissing: true,
|
||||
})
|
||||
.then((packagePolicies) => {
|
||||
this.logger.debug(
|
||||
() =>
|
||||
`Lookup of policy ids:[${policyIds.join(
|
||||
' | '
|
||||
)}]\nvia fleet for space ID [${activeSpaceId}] returned:\n${stringify(
|
||||
(packagePolicies ?? []).map((policy) => ({
|
||||
id: policy.id,
|
||||
name: policy.name,
|
||||
spaceIds: policy.spaceIds,
|
||||
}))
|
||||
)}`
|
||||
);
|
||||
|
||||
return groupBy(packagePolicies ?? [], 'id');
|
||||
});
|
||||
|
||||
if (policyIds.some((policyId) => Boolean(policiesFromFleet[policyId]))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
() => `item can not be read from space [${activeSpaceId}]:\n${stringify(currentSavedItem)}`
|
||||
);
|
||||
|
||||
throw new EndpointExceptionsValidationError(
|
||||
`Item not found in space [${activeSpaceId}]`,
|
||||
404
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import type {
|
|||
UpdateExceptionListItemOptions,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants';
|
||||
import { hasArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import { BaseValidator } from './base_validator';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
import { isValidHash } from '../../../../common/endpoint/service/artifacts/validations';
|
||||
|
@ -245,8 +244,6 @@ export class BlocklistValidator extends BaseValidator {
|
|||
await this.validateByPolicyItem(item);
|
||||
await this.validateCreateOwnerSpaceIds(item);
|
||||
|
||||
await this.setOwnerSpaceId(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -255,8 +252,9 @@ export class BlocklistValidator extends BaseValidator {
|
|||
await this.validateCanDeleteItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreGetOneItem(): Promise<void> {
|
||||
async validatePreGetOneItem(currentItem: ExceptionListItemSchema): Promise<void> {
|
||||
await this.validateHasReadPrivilege();
|
||||
await this.validateCanReadItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreMultiListFind(): Promise<void> {
|
||||
|
@ -304,10 +302,6 @@ export class BlocklistValidator extends BaseValidator {
|
|||
await this.validateUpdateOwnerSpaceIds(updatedItem, currentItem);
|
||||
await this.validateCanUpdateItemInActiveSpace(_updatedItem, currentItem);
|
||||
|
||||
if (!hasArtifactOwnerSpaceId(_updatedItem)) {
|
||||
await this.setOwnerSpaceId(_updatedItem);
|
||||
}
|
||||
|
||||
return _updatedItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import type {
|
|||
import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { EndpointExceptionsValidationError } from './endpoint_exception_errors';
|
||||
import { hasArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import { BaseValidator, GLOBAL_ARTIFACT_MANAGEMENT_NOT_ALLOWED_MESSAGE } from './base_validator';
|
||||
|
||||
export class EndpointExceptionsValidator extends BaseValidator {
|
||||
|
@ -46,8 +45,6 @@ export class EndpointExceptionsValidator extends BaseValidator {
|
|||
await this.validateHasWritePrivilege();
|
||||
await this.validateCreateOwnerSpaceIds(item);
|
||||
|
||||
await this.setOwnerSpaceId(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -58,10 +55,6 @@ export class EndpointExceptionsValidator extends BaseValidator {
|
|||
await this.validateHasWritePrivilege();
|
||||
await this.validateUpdateOwnerSpaceIds(item, currentItem);
|
||||
|
||||
if (!hasArtifactOwnerSpaceId(item)) {
|
||||
await this.setOwnerSpaceId(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -70,8 +63,9 @@ export class EndpointExceptionsValidator extends BaseValidator {
|
|||
await this.validateCanDeleteItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreGetOneItem(): Promise<void> {
|
||||
async validatePreGetOneItem(currentItem: ExceptionListItemSchema): Promise<void> {
|
||||
await this.validateHasReadPrivilege();
|
||||
await this.validateCanReadItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreMultiListFind(): Promise<void> {
|
||||
|
|
|
@ -14,7 +14,6 @@ import type {
|
|||
UpdateExceptionListItemOptions,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
|
||||
import { hasArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
|
||||
import { BaseValidator } from './base_validator';
|
||||
|
@ -62,8 +61,6 @@ export class EventFilterValidator extends BaseValidator {
|
|||
|
||||
await this.validateCreateOwnerSpaceIds(item);
|
||||
|
||||
await this.setOwnerSpaceId(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -91,10 +88,6 @@ export class EventFilterValidator extends BaseValidator {
|
|||
await this.validateUpdateOwnerSpaceIds(_updatedItem, currentItem);
|
||||
await this.validateCanUpdateItemInActiveSpace(_updatedItem, currentItem);
|
||||
|
||||
if (!hasArtifactOwnerSpaceId(_updatedItem)) {
|
||||
await this.setOwnerSpaceId(_updatedItem);
|
||||
}
|
||||
|
||||
return _updatedItem;
|
||||
}
|
||||
|
||||
|
@ -108,8 +101,9 @@ export class EventFilterValidator extends BaseValidator {
|
|||
}
|
||||
}
|
||||
|
||||
async validatePreGetOneItem(): Promise<void> {
|
||||
async validatePreGetOneItem(currentItem: ExceptionListItemSchema): Promise<void> {
|
||||
await this.validateHasReadPrivilege();
|
||||
await this.validateCanReadItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreSummary(): Promise<void> {
|
||||
|
|
|
@ -13,7 +13,6 @@ import type {
|
|||
UpdateExceptionListItemOptions,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { hasArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import { BaseValidator, BasicEndpointExceptionDataSchema } from './base_validator';
|
||||
import { EndpointArtifactExceptionValidationError } from './errors';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
|
@ -82,8 +81,6 @@ export class HostIsolationExceptionsValidator extends BaseValidator {
|
|||
await this.validateByPolicyItem(item);
|
||||
await this.validateCreateOwnerSpaceIds(item);
|
||||
|
||||
await this.setOwnerSpaceId(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -99,15 +96,12 @@ export class HostIsolationExceptionsValidator extends BaseValidator {
|
|||
await this.validateUpdateOwnerSpaceIds(_updatedItem, currentItem);
|
||||
await this.validateCanUpdateItemInActiveSpace(_updatedItem, currentItem);
|
||||
|
||||
if (!hasArtifactOwnerSpaceId(_updatedItem)) {
|
||||
await this.setOwnerSpaceId(_updatedItem);
|
||||
}
|
||||
|
||||
return _updatedItem;
|
||||
}
|
||||
|
||||
async validatePreGetOneItem(): Promise<void> {
|
||||
async validatePreGetOneItem(currentItem: ExceptionListItemSchema): Promise<void> {
|
||||
await this.validateHasReadPrivilege();
|
||||
await this.validateCanReadItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreSummary(): Promise<void> {
|
||||
|
|
|
@ -76,6 +76,10 @@ export class BaseValidatorMock extends BaseValidator {
|
|||
_validateCanDeleteItemInActiveSpace(currentSavedItem: ExceptionListItemSchema): Promise<void> {
|
||||
return this.validateCanDeleteItemInActiveSpace(currentSavedItem);
|
||||
}
|
||||
|
||||
_validateCanReadItemInActiveSpace(currentSavedItem: ExceptionListItemSchema): Promise<void> {
|
||||
return this.validateCanReadItemInActiveSpace(currentSavedItem);
|
||||
}
|
||||
}
|
||||
|
||||
export const createExceptionItemLikeOptionsMock = (
|
||||
|
|
|
@ -15,7 +15,7 @@ import type {
|
|||
CreateExceptionListItemOptions,
|
||||
UpdateExceptionListItemOptions,
|
||||
} from '@kbn/lists-plugin/server';
|
||||
import { hasArtifactOwnerSpaceId } from '../../../../common/endpoint/service/artifacts/utils';
|
||||
import {} from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client_types';
|
||||
import { BaseValidator } from './base_validator';
|
||||
import type { ExceptionItemLikeOptions } from '../types';
|
||||
import type { TrustedAppConditionEntry as ConditionEntry } from '../../../../common/endpoint/types';
|
||||
|
@ -210,8 +210,6 @@ export class TrustedAppValidator extends BaseValidator {
|
|||
await this.validateCreateOwnerSpaceIds(item);
|
||||
await this.validateCanCreateGlobalArtifacts(item);
|
||||
|
||||
await this.setOwnerSpaceId(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -220,8 +218,9 @@ export class TrustedAppValidator extends BaseValidator {
|
|||
await this.validateCanDeleteItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreGetOneItem(): Promise<void> {
|
||||
async validatePreGetOneItem(currentItem: ExceptionListItemSchema): Promise<void> {
|
||||
await this.validateHasReadPrivilege();
|
||||
await this.validateCanReadItemInActiveSpace(currentItem);
|
||||
}
|
||||
|
||||
async validatePreMultiListFind(): Promise<void> {
|
||||
|
@ -264,10 +263,6 @@ export class TrustedAppValidator extends BaseValidator {
|
|||
await this.validateUpdateOwnerSpaceIds(_updatedItem, currentItem);
|
||||
await this.validateCanUpdateItemInActiveSpace(_updatedItem, currentItem);
|
||||
|
||||
if (!hasArtifactOwnerSpaceId(_updatedItem)) {
|
||||
await this.setOwnerSpaceId(_updatedItem);
|
||||
}
|
||||
|
||||
return _updatedItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,10 @@ import {
|
|||
} from '@kbn/security-solution-plugin/common/endpoint/service/artifacts/utils';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
||||
import { exceptionItemToCreateExceptionItem } from '@kbn/security-solution-plugin/common/endpoint/data_generators/exceptions_list_item_generator';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type {
|
||||
ExceptionListItemSchema,
|
||||
FoundExceptionListItemSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { Role } from '@kbn/security-plugin-types-common';
|
||||
import { GLOBAL_ARTIFACT_TAG } from '@kbn/security-solution-plugin/common/endpoint/service/artifacts';
|
||||
import { PolicyTestResourceInfo } from '../../../../../security_solution_endpoint/services/endpoint_policy';
|
||||
|
@ -37,6 +40,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
// @skipInServerless: due to the fact that the serverless builtin roles are not yet updated with new privilege
|
||||
// and tests below are currently creating a new role/user
|
||||
describe('@ess @skipInServerless, @skipInServerlessMKI Endpoint Artifacts space awareness support', function () {
|
||||
const afterEachDataCleanup: Array<Pick<ArtifactTestData, 'cleanup'>> = [];
|
||||
const spaceOneId = 'space_one';
|
||||
const spaceTwoId = 'space_two';
|
||||
|
||||
|
@ -45,6 +49,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let supertestArtifactManager: TestAgent;
|
||||
let supertestGlobalArtifactManager: TestAgent;
|
||||
let spaceOnePolicy: PolicyTestResourceInfo;
|
||||
let spaceTwoPolicy: PolicyTestResourceInfo;
|
||||
|
||||
before(async () => {
|
||||
// For testing, we're using the `t3_analyst` role which already has All privileges
|
||||
|
@ -93,12 +98,21 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
ensureSpaceIdExists(kbnServer, spaceTwoId, { log }),
|
||||
]);
|
||||
|
||||
spaceOnePolicy = await policyTestResources.createPolicy();
|
||||
spaceOnePolicy = await policyTestResources.createPolicy({ options: { spaceId: spaceOneId } });
|
||||
spaceTwoPolicy = await policyTestResources.createPolicy({ options: { spaceId: spaceTwoId } });
|
||||
});
|
||||
|
||||
// the endpoint uses data streams and es archiver does not support deleting them at the moment so we need
|
||||
// to do it manually
|
||||
after(async () => {
|
||||
await spaceOnePolicy.cleanup();
|
||||
// @ts-expect-error
|
||||
spaceOnePolicy = undefined;
|
||||
|
||||
await spaceTwoPolicy.cleanup();
|
||||
// @ts-expect-error
|
||||
spaceTwoPolicy = undefined;
|
||||
|
||||
if (artifactManagerRole) {
|
||||
await rolesUsersProvider.loader.delete(artifactManagerRole.name);
|
||||
// @ts-expect-error
|
||||
|
@ -110,12 +124,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
// @ts-expect-error
|
||||
globalArtifactManagerRole = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
if (spaceOnePolicy) {
|
||||
await spaceOnePolicy.cleanup();
|
||||
// @ts-expect-error
|
||||
spaceOnePolicy = undefined;
|
||||
}
|
||||
afterEach(async () => {
|
||||
await Promise.allSettled(afterEachDataCleanup.splice(0).map((data) => data.cleanup()));
|
||||
});
|
||||
|
||||
const artifactLists = Object.keys(ENDPOINT_ARTIFACT_LISTS);
|
||||
|
@ -127,27 +139,50 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
describe(`for ${listInfo.name}`, () => {
|
||||
let spaceOnePerPolicyArtifact: ArtifactTestData;
|
||||
let spaceOneGlobalArtifact: ArtifactTestData;
|
||||
let spaceTwoPerPolicyArtifact: ArtifactTestData;
|
||||
let spaceTwoGlobalArtifact: ArtifactTestData;
|
||||
|
||||
beforeEach(async () => {
|
||||
// SPACE 1 ARTIFACTS
|
||||
spaceOnePerPolicyArtifact = await endpointArtifactTestResources.createArtifact(
|
||||
listInfo.id,
|
||||
{ tags: [buildPerPolicyTag(spaceOnePolicy.packagePolicy.id)] },
|
||||
{ supertest: supertestArtifactManager, spaceId: spaceOneId }
|
||||
);
|
||||
afterEachDataCleanup.push(spaceOnePerPolicyArtifact);
|
||||
|
||||
spaceOneGlobalArtifact = await endpointArtifactTestResources.createArtifact(
|
||||
listInfo.id,
|
||||
{ tags: [GLOBAL_ARTIFACT_TAG] },
|
||||
{ supertest: supertestGlobalArtifactManager, spaceId: spaceOneId }
|
||||
);
|
||||
afterEachDataCleanup.push(spaceOneGlobalArtifact);
|
||||
|
||||
// SPACE 2 ARTIFACTS
|
||||
spaceTwoPerPolicyArtifact = await endpointArtifactTestResources.createArtifact(
|
||||
listInfo.id,
|
||||
{ tags: [buildPerPolicyTag(spaceTwoPolicy.packagePolicy.id)] },
|
||||
{ supertest: supertestGlobalArtifactManager, spaceId: spaceTwoId }
|
||||
);
|
||||
afterEachDataCleanup.push(spaceTwoPerPolicyArtifact);
|
||||
|
||||
spaceTwoGlobalArtifact = await endpointArtifactTestResources.createArtifact(
|
||||
listInfo.id,
|
||||
{ tags: [GLOBAL_ARTIFACT_TAG] },
|
||||
{ supertest: supertestGlobalArtifactManager, spaceId: spaceTwoId }
|
||||
);
|
||||
afterEachDataCleanup.push(spaceTwoGlobalArtifact);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (spaceOnePerPolicyArtifact) {
|
||||
await spaceOnePerPolicyArtifact.cleanup();
|
||||
// @ts-expect-error assigning `undefined`
|
||||
spaceOnePerPolicyArtifact = undefined;
|
||||
}
|
||||
// @ts-expect-error assigning `undefined`
|
||||
spaceOnePerPolicyArtifact = undefined;
|
||||
// @ts-expect-error assigning `undefined`
|
||||
spaceOneGlobalArtifact = undefined;
|
||||
// @ts-expect-error assigning `undefined`
|
||||
spaceTwoPerPolicyArtifact = undefined;
|
||||
// @ts-expect-error assigning `undefined`
|
||||
spaceTwoGlobalArtifact = undefined;
|
||||
});
|
||||
|
||||
it('should add owner space id when item is created', async () => {
|
||||
|
@ -176,6 +211,55 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('should return only artifacts in active space when sending a find request', async () => {
|
||||
const response = await supertestArtifactManager
|
||||
.get(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL + '/_find'))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log))
|
||||
.query({
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
list_id: listInfo.id,
|
||||
namespace_type: 'agnostic',
|
||||
})
|
||||
.send()
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as FoundExceptionListItemSchema;
|
||||
|
||||
log.info(`find results:\n${JSON.stringify(body)}, null, 2`);
|
||||
|
||||
expect(body.total).to.eql(3);
|
||||
expect(body.data.map((item) => item.item_id).sort()).to.eql(
|
||||
[
|
||||
spaceOnePerPolicyArtifact.artifact.item_id,
|
||||
spaceOneGlobalArtifact.artifact.item_id,
|
||||
spaceTwoGlobalArtifact.artifact.item_id,
|
||||
].sort()
|
||||
);
|
||||
});
|
||||
|
||||
it('should get single global artifact regardless of space', async () => {
|
||||
const response = await supertestArtifactManager
|
||||
.get(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log))
|
||||
.query({
|
||||
item_id: spaceTwoGlobalArtifact.artifact.item_id,
|
||||
namespace_type: 'agnostic',
|
||||
})
|
||||
.send()
|
||||
.expect(200);
|
||||
|
||||
expect((response.body as ExceptionListItemSchema).item_id).to.equal(
|
||||
spaceTwoGlobalArtifact.artifact.item_id
|
||||
);
|
||||
});
|
||||
|
||||
describe('and user does NOT have global artifact management privilege', () => {
|
||||
it('should error when attempting to create artifact with additional owner space id tags', async () => {
|
||||
await supertestArtifactManager
|
||||
|
@ -256,7 +340,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.send(
|
||||
exceptionItemToCreateExceptionItem({
|
||||
...spaceOnePerPolicyArtifact.artifact,
|
||||
description: 'updating item',
|
||||
tags: [buildSpaceOwnerIdTag(spaceOneId)], // removed policy assignment
|
||||
})
|
||||
)
|
||||
.expect(403);
|
||||
|
@ -284,6 +368,22 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
`EndpointArtifactError: Updates to this shared item can only be done from the following space ID: ${spaceOneId} (or by someone having global artifact management privilege)`
|
||||
);
|
||||
});
|
||||
|
||||
it('should error when attempting to GET single artifact not associated with active space', async () => {
|
||||
await supertestArtifactManager
|
||||
.get(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log).ignoreCodes([404]))
|
||||
.query({
|
||||
// Artifact from Space 2
|
||||
item_id: spaceTwoPerPolicyArtifact.artifact.item_id,
|
||||
namespace_type: spaceTwoPerPolicyArtifact.artifact.namespace_type,
|
||||
})
|
||||
.send()
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and user has privilege to manage global artifacts', () => {
|
||||
|
@ -305,7 +405,15 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
expect((body as ExceptionListItemSchema).tags).to.eql([
|
||||
const itemCreated = body as ExceptionListItemSchema;
|
||||
|
||||
afterEachDataCleanup.push({
|
||||
cleanup: () => {
|
||||
return endpointArtifactTestResources.deleteExceptionItem(itemCreated);
|
||||
},
|
||||
});
|
||||
|
||||
expect(itemCreated.tags).to.eql([
|
||||
buildSpaceOwnerIdTag('foo'),
|
||||
buildSpaceOwnerIdTag(spaceOneId),
|
||||
]);
|
||||
|
@ -403,8 +511,148 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
// @ts-expect-error
|
||||
spaceOnePerPolicyArtifact = undefined;
|
||||
});
|
||||
|
||||
it('should allow GET of single artifact not associated with active space', async () => {
|
||||
await supertestGlobalArtifactManager
|
||||
.get(addSpaceIdToPath('/', spaceTwoId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log))
|
||||
.query({
|
||||
item_id: spaceTwoPerPolicyArtifact.artifact.item_id,
|
||||
namespace_type: spaceTwoPerPolicyArtifact.artifact.namespace_type,
|
||||
})
|
||||
.send()
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Endpoint exceptions are currently ONLY global, but an effort is underway to possibly fold them
|
||||
// into a "regular" artifact, at which point we should just remove this entire `describe()` block
|
||||
// and add endpoint exceptions to the execution of the tests above.
|
||||
describe('and for Endpoint Exceptions', () => {
|
||||
let endpointException: ArtifactTestData;
|
||||
|
||||
beforeEach(async () => {
|
||||
endpointException = await endpointArtifactTestResources.createEndpointException(undefined, {
|
||||
supertest: supertestGlobalArtifactManager,
|
||||
spaceId: spaceOneId,
|
||||
});
|
||||
afterEachDataCleanup.push(endpointException);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// @ts-expect-error
|
||||
endpointException = undefined;
|
||||
});
|
||||
|
||||
it('should error on create when user does not have global artifact privilege', async () => {
|
||||
await supertestArtifactManager
|
||||
.post(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log).ignoreCodes([403]))
|
||||
.send(
|
||||
Object.assign(
|
||||
exceptionItemToCreateExceptionItem({
|
||||
...endpointException.artifact,
|
||||
}),
|
||||
{ item_id: undefined }
|
||||
)
|
||||
)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('should allow create when user has global artifact privilege', async () => {
|
||||
const response = await supertestGlobalArtifactManager
|
||||
.post(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log).ignoreCodes([403]))
|
||||
.send(
|
||||
Object.assign(
|
||||
exceptionItemToCreateExceptionItem({
|
||||
...endpointException.artifact,
|
||||
}),
|
||||
{ item_id: undefined }
|
||||
)
|
||||
)
|
||||
.expect(200);
|
||||
|
||||
afterEachDataCleanup.push({
|
||||
cleanup: () =>
|
||||
endpointArtifactTestResources.deleteExceptionItem(
|
||||
response.body as ExceptionListItemSchema
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
it('should error on update when user does not have global artifact privilege', async () => {
|
||||
await supertestArtifactManager
|
||||
.put(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log).ignoreCodes([403]))
|
||||
.send(
|
||||
exceptionItemToCreateExceptionItem({
|
||||
...endpointException.artifact,
|
||||
description: 'item was updated',
|
||||
})
|
||||
)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('should allow update when user has global artifact privilege', async () => {
|
||||
await supertestGlobalArtifactManager
|
||||
.put(addSpaceIdToPath('/', spaceOneId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log))
|
||||
.send(
|
||||
exceptionItemToCreateExceptionItem({
|
||||
...endpointException.artifact,
|
||||
description: 'item was updated',
|
||||
})
|
||||
)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should error on delete when user does not have global artifact privilege', async () => {
|
||||
await supertestArtifactManager
|
||||
.delete(addSpaceIdToPath('/', spaceTwoId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log).ignoreCodes([403]))
|
||||
.query({
|
||||
item_id: endpointException.artifact.item_id,
|
||||
namespace_type: endpointException.artifact.namespace_type,
|
||||
})
|
||||
.send()
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('should allow delete when user has global artifact privilege', async () => {
|
||||
await supertestGlobalArtifactManager
|
||||
.delete(addSpaceIdToPath('/', spaceTwoId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set('x-elastic-internal-origin', 'kibana')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.on('error', createSupertestErrorLogger(log))
|
||||
.query({
|
||||
item_id: endpointException.artifact.item_id,
|
||||
namespace_type: endpointException.artifact.namespace_type,
|
||||
})
|
||||
.send()
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,16 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
import {
|
||||
CreateExceptionListItemSchema,
|
||||
CreateExceptionListSchema,
|
||||
ExceptionListItemSchema,
|
||||
ExceptionListTypeEnum,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import {
|
||||
ENDPOINT_ARTIFACT_LISTS,
|
||||
ENDPOINT_ARTIFACT_LIST_IDS,
|
||||
EXCEPTION_LIST_ITEM_URL,
|
||||
EXCEPTION_LIST_URL,
|
||||
ENDPOINT_LIST_NAME,
|
||||
ENDPOINT_LIST_DESCRIPTION,
|
||||
ENDPOINT_LIST_ID,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import { Response } from 'superagent';
|
||||
import { ExceptionsListItemGenerator } from '@kbn/security-solution-plugin/common/endpoint/data_generators/exceptions_list_item_generator';
|
||||
|
@ -26,6 +30,7 @@ import { BLOCKLISTS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public
|
|||
import { ManifestConstants } from '@kbn/security-solution-plugin/server/endpoint/lib/artifacts';
|
||||
import TestAgent from 'supertest/lib/agent';
|
||||
import { addSpaceIdToPath, DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
|
||||
import { isArtifactGlobal } from '@kbn/security-solution-plugin/common/endpoint/service/artifacts';
|
||||
import { FtrService } from '../../functional/ftr_provider_context';
|
||||
import { InternalUnifiedManifestSchemaResponseType } from '../apps/integrations/mocks';
|
||||
|
||||
|
@ -73,6 +78,8 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
createPayload: CreateExceptionListItemSchema,
|
||||
{ supertest = this.supertest, spaceId = DEFAULT_SPACE_ID }: ArtifactCreateOptions = {}
|
||||
): Promise<ArtifactTestData> {
|
||||
this.log.verbose(`Creating exception item:\n${JSON.stringify(createPayload)}`);
|
||||
|
||||
const artifact = await supertest
|
||||
.post(addSpaceIdToPath('/', spaceId, EXCEPTION_LIST_ITEM_URL))
|
||||
.set('kbn-xsrf', 'true')
|
||||
|
@ -80,28 +87,15 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
.then(this.getHttpResponseFailureHandler())
|
||||
.then((response) => response.body as ExceptionListItemSchema);
|
||||
|
||||
const { item_id: itemId, namespace_type: namespaceType, list_id: listId } = artifact;
|
||||
const { item_id: itemId, list_id: listId } = artifact;
|
||||
const artifactAssignment = isArtifactGlobal(artifact) ? 'Global' : 'Per-Policy';
|
||||
|
||||
this.log.info(
|
||||
`Created exception list item in space [${spaceId}], List ID [${listId}], Item ID ${itemId}`
|
||||
`Created [${artifactAssignment}] exception list item in space [${spaceId}], List ID [${listId}], Item ID [${itemId}]`
|
||||
);
|
||||
|
||||
const cleanup = async () => {
|
||||
const deleteResponse = await supertest
|
||||
.delete(
|
||||
`${addSpaceIdToPath(
|
||||
'/',
|
||||
spaceId,
|
||||
EXCEPTION_LIST_ITEM_URL
|
||||
)}?item_id=${itemId}&namespace_type=${namespaceType}`
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.then(this.getHttpResponseFailureHandler([404]));
|
||||
|
||||
this.log.info(
|
||||
`Deleted exception list item [${listId}]: ${itemId} (${deleteResponse.status})`
|
||||
);
|
||||
await this.deleteExceptionItem(artifact, { supertest, spaceId });
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -110,6 +104,49 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
};
|
||||
}
|
||||
|
||||
async deleteExceptionItem(
|
||||
{
|
||||
list_id: listId,
|
||||
item_id: itemId,
|
||||
namespace_type: nameSpaceType,
|
||||
}: Pick<ExceptionListItemSchema, 'list_id' | 'item_id' | 'namespace_type'>,
|
||||
{ supertest = this.supertest, spaceId = DEFAULT_SPACE_ID }: ArtifactCreateOptions = {}
|
||||
): Promise<void> {
|
||||
const deleteResponse = await supertest
|
||||
.delete(
|
||||
`${addSpaceIdToPath(
|
||||
'/',
|
||||
spaceId,
|
||||
EXCEPTION_LIST_ITEM_URL
|
||||
)}?item_id=${itemId}&namespace_type=${nameSpaceType}`
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.then(this.getHttpResponseFailureHandler([404]));
|
||||
|
||||
this.log.info(`Deleted exception list item [${listId}]: ${itemId} (${deleteResponse.status})`);
|
||||
}
|
||||
|
||||
async createEndpointException(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {},
|
||||
options?: ArtifactCreateOptions
|
||||
): Promise<ArtifactTestData> {
|
||||
await this.ensureListExists(
|
||||
{
|
||||
name: ENDPOINT_LIST_NAME,
|
||||
description: ENDPOINT_LIST_DESCRIPTION,
|
||||
list_id: ENDPOINT_LIST_ID,
|
||||
type: ExceptionListTypeEnum.ENDPOINT,
|
||||
namespace_type: 'agnostic',
|
||||
},
|
||||
options
|
||||
);
|
||||
const endpointException =
|
||||
this.exceptionsGenerator.generateEndpointExceptionForCreate(overrides);
|
||||
|
||||
return this.createExceptionItem(endpointException, options);
|
||||
}
|
||||
|
||||
async createTrustedApp(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {},
|
||||
options?: ArtifactCreateOptions
|
||||
|
@ -168,6 +205,9 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
case ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id: {
|
||||
return this.createHostIsolationException(overrides, options);
|
||||
}
|
||||
case ENDPOINT_LIST_ID: {
|
||||
return this.createEndpointException(overrides, options);
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unexpected list id ${listId}`);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { Immutable } from '@kbn/security-solution-plugin/common/endpoint/types';
|
|||
// NOTE: import path below should be the deep path to the actual module - else we get CI errors
|
||||
import { pkgKeyFromPackageInfo } from '@kbn/fleet-plugin/public/services/pkg_key_from_package_info';
|
||||
import { EndpointError } from '@kbn/security-solution-plugin/common/endpoint/errors';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
||||
import { FtrProviderContext } from '../configs/ftr_provider_context';
|
||||
|
||||
const FLEET_API_ROOT = '/api/fleet';
|
||||
|
@ -157,9 +158,11 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC
|
|||
async createPolicy({
|
||||
agentPolicyOverrides = {},
|
||||
integrationPolicyOverrides = {},
|
||||
options = {},
|
||||
}: Partial<{
|
||||
agentPolicyOverrides: Partial<CreateAgentPolicyRequest['body']>;
|
||||
integrationPolicyOverrides: Partial<CreatePackagePolicyRequest['body']>;
|
||||
options: Partial<{ spaceId: string }>;
|
||||
}> = {}): Promise<PolicyTestResourceInfo> {
|
||||
// create Agent Policy
|
||||
let agentPolicy: CreateAgentPolicyResponse['item'];
|
||||
|
@ -171,7 +174,7 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC
|
|||
...agentPolicyOverrides,
|
||||
};
|
||||
const { body: createResponse }: { body: CreateAgentPolicyResponse } = await supertest
|
||||
.post(FLEET_API_AGENT_POLICIES)
|
||||
.post(addSpaceIdToPath('/', options?.spaceId ?? '', FLEET_API_AGENT_POLICIES))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(newAgentPolicyData)
|
||||
.expect(200);
|
||||
|
@ -220,7 +223,7 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC
|
|||
...integrationPolicyOverrides,
|
||||
};
|
||||
const { body: createResponse }: { body: CreatePackagePolicyResponse } = await supertest
|
||||
.post(FLEET_API_PACKAGE_POLICIES)
|
||||
.post(addSpaceIdToPath('/', options?.spaceId ?? '', FLEET_API_PACKAGE_POLICIES))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(newPackagePolicyData)
|
||||
.expect(200);
|
||||
|
@ -245,7 +248,9 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC
|
|||
packagePolicyIds: [packagePolicy.id],
|
||||
};
|
||||
await supertest
|
||||
.post(FLEET_API_PACKAGE_POLICIES_DELETE)
|
||||
.post(
|
||||
addSpaceIdToPath('/', options?.spaceId ?? '', FLEET_API_PACKAGE_POLICIES_DELETE)
|
||||
)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(deletePackagePolicyData)
|
||||
.expect(200);
|
||||
|
@ -263,7 +268,7 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC
|
|||
agentPolicyId: agentPolicy.id,
|
||||
};
|
||||
await supertest
|
||||
.post(FLEET_API_AGENT_POLICIES_DELETE)
|
||||
.post(addSpaceIdToPath('/', options?.spaceId ?? '', FLEET_API_AGENT_POLICIES_DELETE))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(deleteAgentPolicyData)
|
||||
.expect(200);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue