Fix adding prepackaged rules via fleet plugin (#114467)

Co-authored-by: Garrett Spong <garrett.spong@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Georgii Gorbachev 2021-10-20 03:16:31 +02:00 committed by GitHub
parent 67d6355b2e
commit 5d1b5104fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 794 additions and 561 deletions

View file

@ -772,17 +772,17 @@
"interfaces": [
{
"parentPluginId": "securitySolution",
"id": "def-server.AppRequestContext",
"id": "def-server.SecuritySolutionApiRequestHandlerContext",
"type": "Interface",
"tags": [],
"label": "AppRequestContext",
"label": "SecuritySolutionApiRequestHandlerContext",
"description": [],
"path": "x-pack/plugins/security_solution/server/types.ts",
"deprecated": false,
"children": [
{
"parentPluginId": "securitySolution",
"id": "def-server.AppRequestContext.getAppClient",
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getAppClient",
"type": "Function",
"tags": [],
"label": "getAppClient",
@ -804,7 +804,7 @@
},
{
"parentPluginId": "securitySolution",
"id": "def-server.AppRequestContext.getSpaceId",
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getSpaceId",
"type": "Function",
"tags": [],
"label": "getSpaceId",
@ -819,7 +819,7 @@
},
{
"parentPluginId": "securitySolution",
"id": "def-server.AppRequestContext.getExecutionLogClient",
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getExecutionLogClient",
"type": "Function",
"tags": [],
"label": "getExecutionLogClient",
@ -31438,4 +31438,4 @@
}
]
}
}
}

View file

@ -41,7 +41,7 @@ import {
SPACE_IDS,
} from '../../common/technical_rule_data_field_names';
import { ParsedTechnicalFields } from '../../common/parse_technical_fields';
import { Dataset, RuleDataPluginService } from '../rule_data_plugin_service';
import { Dataset, IRuleDataService } from '../rule_data_plugin_service';
const getEsQueryConfig: typeof getEsQueryConfigTyped = getEsQueryConfigNonTyped;
const getSafeSortIds: typeof getSafeSortIdsTyped = getSafeSortIdsNonTyped;
@ -71,7 +71,7 @@ export interface ConstructorOptions {
authorization: PublicMethodsOf<AlertingAuthorization>;
auditLogger?: AuditLogger;
esClient: ElasticsearchClient;
ruleDataService: RuleDataPluginService;
ruleDataService: IRuleDataService;
}
export interface UpdateOptions<Params extends AlertTypeParams> {
@ -116,7 +116,7 @@ export class AlertsClient {
private readonly authorization: PublicMethodsOf<AlertingAuthorization>;
private readonly esClient: ElasticsearchClient;
private readonly spaceId: string | undefined;
private readonly ruleDataService: RuleDataPluginService;
private readonly ruleDataService: IRuleDataService;
constructor(options: ConstructorOptions) {
this.logger = options.logger;

View file

@ -13,8 +13,7 @@ import { loggingSystemMock } from 'src/core/server/mocks';
import { securityMock } from '../../../security/server/mocks';
import { AuditLogger } from '../../../security/server';
import { alertingAuthorizationMock } from '../../../alerting/server/authorization/alerting_authorization.mock';
import { ruleDataPluginServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../rule_data_plugin_service';
import { ruleDataServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock';
jest.mock('./alerts_client');
@ -26,7 +25,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = {
getAlertingAuthorization: (_: KibanaRequest) => alertingAuthMock,
securityPluginSetup,
esClient: {} as ElasticsearchClient,
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
ruleDataService: ruleDataServiceMock.create(),
};
const fakeRequest = {

View file

@ -9,7 +9,7 @@ import { PublicMethodsOf } from '@kbn/utility-types';
import { ElasticsearchClient, KibanaRequest, Logger } from 'src/core/server';
import { AlertingAuthorization } from '../../../alerting/server';
import { SecurityPluginSetup } from '../../../security/server';
import { RuleDataPluginService } from '../rule_data_plugin_service';
import { IRuleDataService } from '../rule_data_plugin_service';
import { AlertsClient } from './alerts_client';
export interface AlertsClientFactoryProps {
@ -17,7 +17,7 @@ export interface AlertsClientFactoryProps {
esClient: ElasticsearchClient;
getAlertingAuthorization: (request: KibanaRequest) => PublicMethodsOf<AlertingAuthorization>;
securityPluginSetup: SecurityPluginSetup | undefined;
ruleDataService: RuleDataPluginService | null;
ruleDataService: IRuleDataService | null;
}
export class AlertsClientFactory {
@ -28,7 +28,7 @@ export class AlertsClientFactory {
request: KibanaRequest
) => PublicMethodsOf<AlertingAuthorization>;
private securityPluginSetup!: SecurityPluginSetup | undefined;
private ruleDataService!: RuleDataPluginService | null;
private ruleDataService!: IRuleDataService | null;
public initialize(options: AlertsClientFactoryProps) {
/**

View file

@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
import { AuditLogger } from '../../../../security/server';
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../../rule_data_plugin_service';
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
const alertingAuthMock = alertingAuthorizationMock.create();
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
authorization: alertingAuthMock,
esClient: esClientMock,
auditLogger,
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
ruleDataService: ruleDataServiceMock.create(),
};
const DEFAULT_SPACE = 'test_default_space_id';

View file

@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
import { AuditLogger } from '../../../../security/server';
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../../rule_data_plugin_service';
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
const alertingAuthMock = alertingAuthorizationMock.create();
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
authorization: alertingAuthMock,
esClient: esClientMock,
auditLogger,
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
ruleDataService: ruleDataServiceMock.create(),
};
const DEFAULT_SPACE = 'test_default_space_id';

View file

@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
import { AuditLogger } from '../../../../security/server';
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../../rule_data_plugin_service';
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
const alertingAuthMock = alertingAuthorizationMock.create();
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
authorization: alertingAuthMock,
esClient: esClientMock,
auditLogger,
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
ruleDataService: ruleDataServiceMock.create(),
};
const DEFAULT_SPACE = 'test_default_space_id';

View file

@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
import { AuditLogger } from '../../../../security/server';
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../../rule_data_plugin_service';
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
const alertingAuthMock = alertingAuthorizationMock.create();
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
authorization: alertingAuthMock,
esClient: esClientMock,
auditLogger,
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
ruleDataService: ruleDataServiceMock.create(),
};
const DEFAULT_SPACE = 'test_default_space_id';

View file

@ -12,7 +12,7 @@ import { PluginInitializerContext } from 'src/core/server';
import { RuleRegistryPlugin } from './plugin';
export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin';
export { RuleDataPluginService } from './rule_data_plugin_service';
export { IRuleDataService, RuleDataPluginService } from './rule_data_plugin_service';
export { RuleDataClient } from './rule_data_client';
export { IRuleDataClient } from './rule_data_client/types';
export type {

View file

@ -7,12 +7,17 @@
import { alertsClientMock } from './alert_data_client/alerts_client.mock';
import { createRuleDataClientMock } from './rule_data_client/rule_data_client.mock';
import { ruleDataPluginServiceMock } from './rule_data_plugin_service/rule_data_plugin_service.mock';
import {
ruleDataServiceMock,
RuleDataServiceMock,
} from './rule_data_plugin_service/rule_data_plugin_service.mock';
import { createLifecycleAlertServicesMock } from './utils/lifecycle_alert_services_mock';
export const ruleRegistryMocks = {
createLifecycleAlertServices: createLifecycleAlertServicesMock,
createRuleDataPluginService: ruleDataPluginServiceMock.create,
createRuleDataService: ruleDataServiceMock.create,
createRuleDataClient: createRuleDataClientMock,
createAlertsClientMock: alertsClientMock,
};
export { RuleDataServiceMock };

View file

@ -20,7 +20,7 @@ import { PluginStartContract as AlertingStart } from '../../alerting/server';
import { SecurityPluginSetup } from '../../security/server';
import { RuleRegistryPluginConfig } from './config';
import { RuleDataPluginService } from './rule_data_plugin_service';
import { IRuleDataService, RuleDataService } from './rule_data_plugin_service';
import { AlertsClientFactory } from './alert_data_client/alerts_client_factory';
import { AlertsClient } from './alert_data_client/alerts_client';
import { RacApiRequestHandlerContext, RacRequestHandlerContext } from './types';
@ -35,7 +35,7 @@ export interface RuleRegistryPluginStartDependencies {
}
export interface RuleRegistryPluginSetupContract {
ruleDataService: RuleDataPluginService;
ruleDataService: IRuleDataService;
}
export interface RuleRegistryPluginStartContract {
@ -57,7 +57,7 @@ export class RuleRegistryPlugin
private readonly logger: Logger;
private readonly kibanaVersion: string;
private readonly alertsClientFactory: AlertsClientFactory;
private ruleDataService: RuleDataPluginService | null;
private ruleDataService: IRuleDataService | null;
private security: SecurityPluginSetup | undefined;
constructor(initContext: PluginInitializerContext) {
@ -100,7 +100,7 @@ export class RuleRegistryPlugin
}
};
this.ruleDataService = new RuleDataPluginService({
this.ruleDataService = new RuleDataService({
logger,
kibanaVersion,
isWriteEnabled: isWriteEnabled(this.config, this.legacyConfig),

View file

@ -5,13 +5,10 @@
* 2.0.
*/
import type { PublicMethodsOf } from '@kbn/utility-types';
import { RuleDataPluginService } from './rule_data_plugin_service';
import { IRuleDataService } from './rule_data_plugin_service';
type Schema = PublicMethodsOf<RuleDataPluginService>;
const createRuleDataPluginService = () => {
const mocked: jest.Mocked<Schema> = {
export const ruleDataServiceMock = {
create: (): jest.Mocked<IRuleDataService> => ({
getResourcePrefix: jest.fn(),
getResourceName: jest.fn(),
isWriteEnabled: jest.fn(),
@ -19,10 +16,9 @@ const createRuleDataPluginService = () => {
initializeIndex: jest.fn(),
findIndexByName: jest.fn(),
findIndicesByFeature: jest.fn(),
};
return mocked;
}),
};
export const ruleDataPluginServiceMock = {
create: createRuleDataPluginService,
};
export const RuleDataServiceMock = jest
.fn<jest.Mocked<IRuleDataService>, []>()
.mockImplementation(ruleDataServiceMock.create);

View file

@ -17,6 +17,59 @@ import { Dataset, IndexOptions } from './index_options';
import { ResourceInstaller } from './resource_installer';
import { joinWithDash } from './utils';
/**
* A service for creating and using Elasticsearch indices for alerts-as-data.
*/
export interface IRuleDataService {
/**
* Returns a prefix used in the naming scheme of index aliases, templates
* and other Elasticsearch resources that this service creates
* for alerts-as-data indices.
*/
getResourcePrefix(): string;
/**
* Prepends a relative resource name with the resource prefix.
* @returns Full name of the resource.
* @example 'security.alerts' => '.alerts-security.alerts'
*/
getResourceName(relativeName: string): string;
/**
* If write is enabled, everything works as usual.
* If it's disabled, writing to all alerts-as-data indices will be disabled,
* and also Elasticsearch resources associated with the indices will not be
* installed.
*/
isWriteEnabled(): boolean;
/**
* Installs common Elasticsearch resources used by all alerts-as-data indices.
*/
initializeService(): void;
/**
* Initializes alerts-as-data index and starts index bootstrapping right away.
* @param indexOptions Index parameters: names and resources.
* @returns Client for reading and writing data to this index.
*/
initializeIndex(indexOptions: IndexOptions): IRuleDataClient;
/**
* Looks up the index information associated with the given registration context and dataset.
*/
findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null;
/**
* Looks up the index information associated with the given Kibana "feature".
* Note: features are used in RBAC.
*/
findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[];
}
// TODO: This is a leftover. Remove its usage from the "observability" plugin and delete it.
export type RuleDataPluginService = IRuleDataService;
interface ConstructorOptions {
getClusterClient: () => Promise<ElasticsearchClient>;
logger: Logger;
@ -24,10 +77,7 @@ interface ConstructorOptions {
isWriteEnabled: boolean;
}
/**
* A service for creating and using Elasticsearch indices for alerts-as-data.
*/
export class RuleDataPluginService {
export class RuleDataService implements IRuleDataService {
private readonly indicesByBaseName: Map<string, IndexInfo>;
private readonly indicesByFeatureId: Map<string, IndexInfo[]>;
private readonly resourceInstaller: ResourceInstaller;
@ -49,37 +99,18 @@ export class RuleDataPluginService {
this.isInitialized = false;
}
/**
* Returns a prefix used in the naming scheme of index aliases, templates
* and other Elasticsearch resources that this service creates
* for alerts-as-data indices.
*/
public getResourcePrefix(): string {
return INDEX_PREFIX;
}
/**
* Prepends a relative resource name with the resource prefix.
* @returns Full name of the resource.
* @example 'security.alerts' => '.alerts-security.alerts'
*/
public getResourceName(relativeName: string): string {
return joinWithDash(this.getResourcePrefix(), relativeName);
}
/**
* If write is enabled, everything works as usual.
* If it's disabled, writing to all alerts-as-data indices will be disabled,
* and also Elasticsearch resources associated with the indices will not be
* installed.
*/
public isWriteEnabled(): boolean {
return this.options.isWriteEnabled;
}
/**
* Installs common Elasticsearch resources used by all alerts-as-data indices.
*/
public initializeService(): void {
// Run the installation of common resources and handle exceptions.
this.installCommonResources = this.resourceInstaller
@ -93,11 +124,6 @@ export class RuleDataPluginService {
this.isInitialized = true;
}
/**
* Initializes alerts-as-data index and starts index bootstrapping right away.
* @param indexOptions Index parameters: names and resources.
* @returns Client for reading and writing data to this index.
*/
public initializeIndex(indexOptions: IndexOptions): IRuleDataClient {
if (!this.isInitialized) {
throw new Error(
@ -156,18 +182,11 @@ export class RuleDataPluginService {
});
}
/**
* Looks up the index information associated with the given registration context and dataset.
*/
public findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null {
const baseName = this.getResourceName(`${registrationContext}.${dataset}`);
return this.indicesByBaseName.get(baseName) ?? null;
}
/**
* Looks up the index information associated with the given Kibana "feature".
* Note: features are used in RBAC.
*/
public findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[] {
const foundIndices = this.indicesByFeatureId.get(featureId) ?? [];
return dataset ? foundIndices.filter((i) => i.indexOptions.dataset === dataset) : foundIndices;

View file

@ -0,0 +1,62 @@
/*
* 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 { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants';
import {
ExperimentalFeatures,
parseExperimentalConfigValue,
} from '../common/experimental_features';
import { ConfigType } from './config';
import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types';
export const createMockConfig = (): ConfigType => {
const enableExperimental: string[] = [];
return {
[SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX,
maxRuleImportExportSize: 10000,
maxRuleImportPayloadBytes: 10485760,
maxTimelineImportExportSize: 10000,
maxTimelineImportPayloadBytes: 10485760,
enableExperimental,
endpointResultListDefaultFirstPageIndex: 0,
endpointResultListDefaultPageSize: 10,
packagerTaskInterval: '60s',
alertMergeStrategy: 'missingFields',
alertIgnoreFields: [],
prebuiltRulesFromFileSystem: true,
prebuiltRulesFromSavedObjects: false,
ruleExecutionLog: {
underlyingClient: UnderlyingLogClient.savedObjects,
},
kibanaIndex: '.kibana',
experimentalFeatures: parseExperimentalConfigValue(enableExperimental),
};
};
const withExperimentalFeature = (
config: ConfigType,
feature: keyof ExperimentalFeatures
): ConfigType => {
const enableExperimental = config.enableExperimental.concat(feature);
return {
...config,
enableExperimental,
experimentalFeatures: parseExperimentalConfigValue(enableExperimental),
};
};
const withRuleRegistryEnabled = (config: ConfigType, isEnabled: boolean): ConfigType => {
return isEnabled ? withExperimentalFeature(config, 'ruleRegistryEnabled') : config;
};
export const configMock = {
createDefault: createMockConfig,
withExperimentalFeature,
withRuleRegistryEnabled,
};

View file

@ -9,8 +9,10 @@ import { schema, TypeOf } from '@kbn/config-schema';
import { PluginInitializerContext } from '../../../../src/core/server';
import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX } from '../common/constants';
import {
ExperimentalFeatures,
getExperimentalAllowedValues,
isValidExperimentalValue,
parseExperimentalConfigValue,
} from '../common/experimental_features';
import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types';
@ -134,7 +136,23 @@ export const configSchema = schema.object({
prebuiltRulesFromSavedObjects: schema.boolean({ defaultValue: true }),
});
export const createConfig = (context: PluginInitializerContext) =>
context.config.get<TypeOf<typeof configSchema>>();
export type ConfigSchema = TypeOf<typeof configSchema>;
export type ConfigType = TypeOf<typeof configSchema>;
export type ConfigType = ConfigSchema & {
kibanaIndex: string;
experimentalFeatures: ExperimentalFeatures;
};
export const createConfig = (context: PluginInitializerContext): ConfigType => {
const globalConfig = context.config.legacy.get();
const pluginConfig = context.config.get<TypeOf<typeof configSchema>>();
const kibanaIndex = globalConfig.kibana.index;
const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental);
return {
...pluginConfig,
kibanaIndex,
experimentalFeatures,
};
};

View file

@ -27,13 +27,18 @@ import {
import { ManifestManager } from './services/artifacts';
import { AppClientFactory } from '../client';
import { ConfigType } from '../config';
import { IRequestContextFactory } from '../request_context_factory';
import { LicenseService } from '../../common/license';
import {
ExperimentalFeatures,
parseExperimentalConfigValue,
} from '../../common/experimental_features';
import { ExperimentalFeatures } from '../../common/experimental_features';
import { EndpointMetadataService } from './services/metadata';
import { EndpointAppContentServicesNotStartedError } from './errors';
import {
EndpointAppContentServicesNotSetUpError,
EndpointAppContentServicesNotStartedError,
} from './errors';
export interface EndpointAppContextServiceSetupContract {
securitySolutionRequestContextFactory: IRequestContextFactory;
}
export type EndpointAppContextServiceStartContract = Partial<
Pick<
@ -59,40 +64,29 @@ export type EndpointAppContextServiceStartContract = Partial<
* of the plugin lifecycle. And stop during the stop phase, if needed.
*/
export class EndpointAppContextService {
private agentService: AgentService | undefined;
private manifestManager: ManifestManager | undefined;
private packagePolicyService: PackagePolicyServiceInterface | undefined;
private agentPolicyService: AgentPolicyServiceInterface | undefined;
private config: ConfigType | undefined;
private license: LicenseService | undefined;
private setupDependencies: EndpointAppContextServiceSetupContract | null = null;
private startDependencies: EndpointAppContextServiceStartContract | null = null;
public security: SecurityPluginStart | undefined;
private cases: CasesPluginStartContract | undefined;
private endpointMetadataService: EndpointMetadataService | undefined;
private experimentalFeatures: ExperimentalFeatures | undefined;
public setup(dependencies: EndpointAppContextServiceSetupContract) {
this.setupDependencies = dependencies;
}
public start(dependencies: EndpointAppContextServiceStartContract) {
this.agentService = dependencies.agentService;
this.packagePolicyService = dependencies.packagePolicyService;
this.agentPolicyService = dependencies.agentPolicyService;
this.manifestManager = dependencies.manifestManager;
this.config = dependencies.config;
this.license = dependencies.licenseService;
this.security = dependencies.security;
this.cases = dependencies.cases;
this.endpointMetadataService = dependencies.endpointMetadataService;
this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental);
if (!this.setupDependencies) {
throw new EndpointAppContentServicesNotSetUpError();
}
if (this.manifestManager && dependencies.registerIngestCallback) {
this.startDependencies = dependencies;
this.security = dependencies.security;
if (dependencies.registerIngestCallback && dependencies.manifestManager) {
dependencies.registerIngestCallback(
'packagePolicyCreate',
getPackagePolicyCreateCallback(
dependencies.logger,
this.manifestManager,
dependencies.appClientFactory,
dependencies.config.maxTimelineImportExportSize,
dependencies.config.prebuiltRulesFromFileSystem,
dependencies.config.prebuiltRulesFromSavedObjects,
dependencies.security,
dependencies.manifestManager,
this.setupDependencies.securitySolutionRequestContextFactory,
dependencies.alerting,
dependencies.licenseService,
dependencies.exceptionListsClient
@ -106,7 +100,10 @@ export class EndpointAppContextService {
dependencies.registerIngestCallback(
'postPackagePolicyDelete',
getPackagePolicyDeleteCallback(dependencies.exceptionListsClient, this.experimentalFeatures)
getPackagePolicyDeleteCallback(
dependencies.exceptionListsClient,
dependencies.config.experimentalFeatures
)
);
}
}
@ -114,43 +111,43 @@ export class EndpointAppContextService {
public stop() {}
public getExperimentalFeatures(): Readonly<ExperimentalFeatures> | undefined {
return this.experimentalFeatures;
return this.startDependencies?.config.experimentalFeatures;
}
public getEndpointMetadataService(): EndpointMetadataService {
if (!this.endpointMetadataService) {
if (this.startDependencies == null) {
throw new EndpointAppContentServicesNotStartedError();
}
return this.endpointMetadataService;
return this.startDependencies.endpointMetadataService;
}
public getAgentService(): AgentService | undefined {
return this.agentService;
return this.startDependencies?.agentService;
}
public getPackagePolicyService(): PackagePolicyServiceInterface | undefined {
return this.packagePolicyService;
return this.startDependencies?.packagePolicyService;
}
public getAgentPolicyService(): AgentPolicyServiceInterface | undefined {
return this.agentPolicyService;
return this.startDependencies?.agentPolicyService;
}
public getManifestManager(): ManifestManager | undefined {
return this.manifestManager;
return this.startDependencies?.manifestManager;
}
public getLicenseService(): LicenseService {
if (!this.license) {
if (this.startDependencies == null) {
throw new EndpointAppContentServicesNotStartedError();
}
return this.license;
return this.startDependencies.licenseService;
}
public async getCasesClient(req: KibanaRequest): Promise<CasesClient> {
if (!this.cases) {
if (this.startDependencies?.cases == null) {
throw new EndpointAppContentServicesNotStartedError();
}
return this.cases.getCasesClientWithRequest(req);
return this.startDependencies.cases.getCasesClientWithRequest(req);
}
}

View file

@ -17,6 +17,12 @@ export class EndpointError extends Error {
export class NotFoundError extends EndpointError {}
export class EndpointAppContentServicesNotSetUpError extends EndpointError {
constructor() {
super('EndpointAppContextService has not been set up (EndpointAppContextService.setup())');
}
}
export class EndpointAppContentServicesNotStartedError extends EndpointError {
constructor() {
super('EndpointAppContextService has not been started (EndpointAppContextService.start())');

View file

@ -22,6 +22,7 @@ import { AppClientFactory } from '../client';
import { createMockConfig } from '../lib/detection_engine/routes/__mocks__';
import {
EndpointAppContextService,
EndpointAppContextServiceSetupContract,
EndpointAppContextServiceStartContract,
} from './endpoint_app_context_services';
import { ManifestManager } from './services/artifacts/manifest_manager/manifest_manager';
@ -37,6 +38,7 @@ import { parseExperimentalConfigValue } from '../../common/experimental_features
// a restricted path.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { createCasesClientMock } from '../../../cases/server/client/mocks';
import { requestContextFactoryMock } from '../request_context_factory.mock';
import { EndpointMetadataService } from './services/metadata';
/**
@ -69,13 +71,25 @@ export const createMockEndpointAppContextService = (
} as unknown as jest.Mocked<EndpointAppContextService>;
};
/**
* Creates a mocked input contract for the `EndpointAppContextService#setup()` method
*/
export const createMockEndpointAppContextServiceSetupContract =
(): jest.Mocked<EndpointAppContextServiceSetupContract> => {
return {
securitySolutionRequestContextFactory: requestContextFactoryMock.create(),
};
};
/**
* Creates a mocked input contract for the `EndpointAppContextService#start()` method
*/
export const createMockEndpointAppContextServiceStartContract =
(): jest.Mocked<EndpointAppContextServiceStartContract> => {
const factory = new AppClientFactory();
const config = createMockConfig();
const factory = new AppClientFactory();
factory.setup({ getSpaceId: () => 'mockSpace', config });
const casesClientMock = createCasesClientMock();
const savedObjectsStart = savedObjectsServiceMock.createStartContract();
const agentService = createMockAgentService();
@ -86,8 +100,6 @@ export const createMockEndpointAppContextServiceStartContract =
agentPolicyService
);
factory.setup({ getSpaceId: () => 'mockSpace', config });
return {
agentService,
agentPolicyService,

View file

@ -25,6 +25,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import {
createMockEndpointAppContextServiceSetupContract,
createMockEndpointAppContextServiceStartContract,
createRouteHandlerContext,
} from '../../mocks';
@ -130,6 +131,7 @@ describe('Action Log API', () => {
const esClientMock = elasticsearchServiceMock.createScopedClusterClient();
const routerMock = httpServiceMock.createRouter();
endpointAppContextService = new EndpointAppContextService();
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
registerActionAuditLogRoutes(routerMock, {

View file

@ -18,6 +18,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
import { SecuritySolutionRequestHandlerContext } from '../../../types';
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import {
createMockEndpointAppContextServiceSetupContract,
createMockEndpointAppContextServiceStartContract,
createMockPackageService,
createRouteHandlerContext,
@ -157,9 +158,12 @@ describe('Host Isolation', () => {
keep_policies_up_to_date: false,
})
);
licenseEmitter = new Subject();
licenseService = new LicenseService();
licenseService.start(licenseEmitter);
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start({
...startContract,
licenseService,

View file

@ -21,6 +21,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import {
createMockEndpointAppContextServiceSetupContract,
createMockEndpointAppContextServiceStartContract,
createRouteHandlerContext,
} from '../../mocks';
@ -67,6 +68,7 @@ describe('Endpoint Action Status', () => {
const esClientMock = elasticsearchServiceMock.createScopedClusterClient();
const routerMock = httpServiceMock.createRouter();
endpointAppContextService = new EndpointAppContextService();
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
registerActionStatusRoutes(routerMock, {

View file

@ -22,6 +22,7 @@ import { HostInfo, HostResultList, HostStatus } from '../../../../common/endpoin
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
import { registerEndpointRoutes } from './index';
import {
createMockEndpointAppContextServiceSetupContract,
createMockEndpointAppContextServiceStartContract,
createMockPackageService,
createRouteHandlerContext,
@ -134,6 +135,7 @@ describe('test endpoint route', () => {
keep_policies_up_to_date: false,
})
);
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
mockAgentService = startContract.agentService!;
@ -394,6 +396,7 @@ describe('test endpoint route', () => {
keep_policies_up_to_date: false,
})
);
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
mockAgentService = startContract.agentService!;

View file

@ -7,6 +7,7 @@
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import {
createMockEndpointAppContextServiceSetupContract,
createMockEndpointAppContextServiceStartContract,
createRouteHandlerContext,
} from '../../mocks';
@ -45,6 +46,7 @@ describe('test policy response handler', () => {
mockSavedObjectClient = savedObjectsClientMock.create();
mockResponse = httpServerMock.createResponseFactory();
endpointAppContextService = new EndpointAppContextService();
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
});
@ -161,6 +163,7 @@ describe('test policy response handler', () => {
page: 1,
perPage: 1,
};
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
endpointAppContextService.start({
...createMockEndpointAppContextServiceStartContract(),
...{ agentService: mockAgentService },

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { httpServerMock, loggingSystemMock } from 'src/core/server/mocks';
import { createNewPackagePolicyMock, deletePackagePolicyMock } from '../../../fleet/common/mocks';
import {
@ -18,7 +20,8 @@ import {
getPackagePolicyUpdateCallback,
} from './fleet_integration';
import { KibanaRequest } from 'kibana/server';
import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__';
import { requestContextMock } from '../lib/detection_engine/routes/__mocks__';
import { requestContextFactoryMock } from '../request_context_factory.mock';
import { EndpointAppContextServiceStartContract } from '../endpoint/endpoint_app_context_services';
import { createMockEndpointAppContextServiceStartContract } from '../endpoint/mocks';
import { licenseMock } from '../../../licensing/common/licensing.mock';
@ -42,16 +45,12 @@ import {
ExperimentalFeatures,
} from '../../common/experimental_features';
import { DeletePackagePoliciesResponse } from '../../../fleet/common';
import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
describe('ingest_integration tests ', () => {
let endpointAppContextMock: EndpointAppContextServiceStartContract;
let req: KibanaRequest;
let ctx: SecuritySolutionRequestHandlerContext;
const exceptionListClient: ExceptionListClient = getExceptionListClientMock();
const maxTimelineImportExportSize = createMockConfig().maxTimelineImportExportSize;
const prebuiltRulesFromFileSystem = createMockConfig().prebuiltRulesFromFileSystem;
const prebuiltRulesFromSavedObjects = createMockConfig().prebuiltRulesFromSavedObjects;
let licenseEmitter: Subject<ILicense>;
let licenseService: LicenseService;
const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } });
@ -88,11 +87,7 @@ describe('ingest_integration tests ', () => {
const callback = getPackagePolicyCreateCallback(
logger,
manifestManager,
endpointAppContextMock.appClientFactory,
maxTimelineImportExportSize,
prebuiltRulesFromFileSystem,
prebuiltRulesFromSavedObjects,
endpointAppContextMock.security,
requestContextFactoryMock.create(),
endpointAppContextMock.alerting,
licenseService,
exceptionListClient

View file

@ -8,7 +8,6 @@
import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server';
import { ExceptionListClient } from '../../../lists/server';
import { PluginStartContract as AlertsStartContract } from '../../../alerting/server';
import { SecurityPluginStart } from '../../../security/server';
import {
PostPackagePolicyCreateCallback,
PostPackagePolicyDeleteCallback,
@ -18,15 +17,15 @@ import {
import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common';
import { NewPolicyData, PolicyConfig } from '../../common/endpoint/types';
import { ManifestManager } from '../endpoint/services';
import { AppClientFactory } from '../client';
import { ExperimentalFeatures } from '../../common/experimental_features';
import { LicenseService } from '../../common/license';
import { ManifestManager } from '../endpoint/services';
import { IRequestContextFactory } from '../request_context_factory';
import { installPrepackagedRules } from './handlers/install_prepackaged_rules';
import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest';
import { createDefaultPolicy } from './handlers/create_default_policy';
import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license';
import { removePolicyFromTrustedApps } from './handlers/remove_policy_from_trusted_apps';
import { ExperimentalFeatures } from '../../common/experimental_features';
const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
packagePolicy: T
@ -40,11 +39,7 @@ const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
export const getPackagePolicyCreateCallback = (
logger: Logger,
manifestManager: ManifestManager,
appClientFactory: AppClientFactory,
maxTimelineImportExportSize: number,
prebuiltRulesFromFileSystem: boolean,
prebuiltRulesFromSavedObjects: boolean,
securityStart: SecurityPluginStart,
securitySolutionRequestContextFactory: IRequestContextFactory,
alerts: AlertsStartContract,
licenseService: LicenseService,
exceptionsClient: ExceptionListClient | undefined
@ -59,20 +54,23 @@ export const getPackagePolicyCreateCallback = (
return newPackagePolicy;
}
// In this callback we are handling an HTTP request to the fleet plugin. Since we use
// code from the security_solution plugin to handle it (installPrepackagedRules),
// we need to build the context that is native to security_solution and pass it there.
const securitySolutionContext = await securitySolutionRequestContextFactory.create(
context,
request
);
// perform these operations in parallel in order to help in not delaying the API response too much
const [, manifestValue] = await Promise.all([
// Install Detection Engine prepackaged rules
exceptionsClient &&
installPrepackagedRules({
logger,
appClientFactory,
context,
context: securitySolutionContext,
request,
securityStart,
alerts,
maxTimelineImportExportSize,
prebuiltRulesFromFileSystem,
prebuiltRulesFromSavedObjects,
exceptionsClient,
}),

View file

@ -5,25 +5,18 @@
* 2.0.
*/
import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server';
import { KibanaRequest, Logger } from 'kibana/server';
import { ExceptionListClient } from '../../../../lists/server';
import { PluginStartContract as AlertsStartContract } from '../../../../alerting/server';
import { SecurityPluginStart } from '../../../../security/server';
import { AppClientFactory } from '../../client';
import { createDetectionIndex } from '../../lib/detection_engine/routes/index/create_index_route';
import { createPrepackagedRules } from '../../lib/detection_engine/routes/rules/add_prepackaged_rules_route';
import { buildFrameworkRequest } from '../../lib/timeline/utils/common';
import { SecuritySolutionApiRequestHandlerContext } from '../../types';
export interface InstallPrepackagedRulesProps {
logger: Logger;
appClientFactory: AppClientFactory;
context: RequestHandlerContext;
context: SecuritySolutionApiRequestHandlerContext;
request: KibanaRequest;
securityStart: SecurityPluginStart;
alerts: AlertsStartContract;
maxTimelineImportExportSize: number;
prebuiltRulesFromFileSystem: boolean;
prebuiltRulesFromSavedObjects: boolean;
exceptionsClient: ExceptionListClient;
}
@ -33,29 +26,14 @@ export interface InstallPrepackagedRulesProps {
*/
export const installPrepackagedRules = async ({
logger,
appClientFactory,
context,
request,
securityStart,
alerts,
maxTimelineImportExportSize,
prebuiltRulesFromFileSystem,
prebuiltRulesFromSavedObjects,
exceptionsClient,
}: InstallPrepackagedRulesProps): Promise<void> => {
// prep for detection rules creation
const appClient = appClientFactory.create(request);
// This callback is called by fleet plugin.
// It doesn't have access to SecuritySolutionRequestHandlerContext in runtime.
// Muting the error to have green CI.
// @ts-expect-error
const frameworkRequest = await buildFrameworkRequest(context, securityStart, request);
// Create detection index & rules (if necessary). move past any failure, this is just a convenience
try {
// @ts-expect-error
await createDetectionIndex(context, appClient);
await createDetectionIndex(context);
} catch (err) {
if (err.statusCode !== 409) {
// 409 -> detection index already exists, which is fine
@ -68,14 +46,8 @@ export const installPrepackagedRules = async ({
// this checks to make sure index exists first, safe to try in case of failure above
// may be able to recover from minor errors
await createPrepackagedRules(
// @ts-expect-error
context,
appClient,
alerts.getRulesClientWithRequest(request),
frameworkRequest,
maxTimelineImportExportSize,
prebuiltRulesFromFileSystem,
prebuiltRulesFromSavedObjects,
exceptionsClient
);
} catch (err) {

View file

@ -7,7 +7,7 @@
import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server';
import { Plugin, PluginSetup, PluginStart } from './plugin';
import { configSchema, ConfigType } from './config';
import { configSchema, ConfigSchema, ConfigType } from './config';
import { SIGNALS_INDEX_KEY } from '../common/constants';
import { AppClient } from './types';
@ -15,7 +15,7 @@ export const plugin = (context: PluginInitializerContext) => {
return new Plugin(context);
};
export const config: PluginConfigDescriptor<ConfigType> = {
export const config: PluginConfigDescriptor<ConfigSchema> = {
exposeToBrowser: {
enableExperimental: true,
},
@ -47,5 +47,5 @@ export const config: PluginConfigDescriptor<ConfigType> = {
export { ConfigType, Plugin, PluginSetup, PluginStart };
export { AppClient };
export type { AppRequestContext } from './types';
export type { SecuritySolutionApiRequestHandlerContext } from './types';
export { EndpointError } from './endpoint/errors';

View file

@ -5,34 +5,11 @@
* 2.0.
*/
import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../../../../../common/constants';
import { requestContextMock } from './request_context';
import { serverMock } from './server';
import { requestMock } from './request';
import { responseMock } from './response_factory';
import { ConfigType } from '../../../../config';
import { UnderlyingLogClient } from '../../rule_execution_log/types';
export { requestMock, requestContextMock, responseMock, serverMock };
export const createMockConfig = (): ConfigType => ({
[SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX,
maxRuleImportExportSize: 10000,
maxRuleImportPayloadBytes: 10485760,
maxTimelineImportExportSize: 10000,
maxTimelineImportPayloadBytes: 10485760,
enableExperimental: [],
endpointResultListDefaultFirstPageIndex: 0,
endpointResultListDefaultPageSize: 10,
packagerTaskInterval: '60s',
alertMergeStrategy: 'missingFields',
alertIgnoreFields: [],
prebuiltRulesFromFileSystem: true,
prebuiltRulesFromSavedObjects: false,
ruleExecutionLog: {
underlyingClient: UnderlyingLogClient.savedObjects,
},
});
export { requestContextMock } from './request_context';
export { requestMock } from './request';
export { responseMock } from './response_factory';
export { serverMock } from './server';
export { configMock, createMockConfig } from '../../../../config.mock';
export const mockGetCurrentUser = {
user: {

View file

@ -5,66 +5,103 @@
* 2.0.
*/
import type { SecuritySolutionRequestHandlerContext } from '../../../../types';
import {
coreMock,
elasticsearchServiceMock,
savedObjectsClientMock,
} from '../../../../../../../../src/core/server/mocks';
import type { MockedKeys } from '@kbn/utility-types/jest';
import { coreMock } from 'src/core/server/mocks';
import { ActionsApiRequestHandlerContext } from '../../../../../../actions/server';
import { AlertingApiRequestHandlerContext } from '../../../../../../alerting/server';
import { rulesClientMock } from '../../../../../../alerting/server/mocks';
import { licensingMock } from '../../../../../../licensing/server/mocks';
import { listMock } from '../../../../../../lists/server/mocks';
import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks';
import { siemMock } from '../../../../mocks';
import { createMockConfig } from '../../../../config.mock';
import { ruleExecutionLogClientMock } from '../../rule_execution_log/__mocks__/rule_execution_log_client';
import { requestMock } from './request';
import { internalFrameworkRequest } from '../../../framework';
const createMockClients = () => ({
rulesClient: rulesClientMock.create(),
licensing: { license: licensingMock.createLicenseMock() },
clusterClient: elasticsearchServiceMock.createScopedClusterClient(),
savedObjectsClient: savedObjectsClientMock.create(),
ruleExecutionLogClient: ruleExecutionLogClientMock.create(),
appClient: siemMock.createClient(),
});
import type {
SecuritySolutionApiRequestHandlerContext,
SecuritySolutionRequestHandlerContext,
} from '../../../../types';
/**
* Adds mocking to the interface so we don't have to cast everywhere
*/
type SecuritySolutionRequestHandlerContextMock = SecuritySolutionRequestHandlerContext & {
core: {
elasticsearch: {
client: {
asCurrentUser: {
updateByQuery: jest.Mock;
search: jest.Mock;
security: {
hasPrivileges: jest.Mock;
};
};
};
};
const createMockClients = () => {
const core = coreMock.createRequestHandlerContext();
const license = licensingMock.createLicenseMock();
return {
core,
clusterClient: core.elasticsearch.client,
savedObjectsClient: core.savedObjects.client,
licensing: {
...licensingMock.createRequestHandlerContext({ license }),
license,
},
lists: {
listClient: listMock.getListClient(),
exceptionListClient: listMock.getExceptionListClient(core.savedObjects.client),
},
rulesClient: rulesClientMock.create(),
ruleDataService: ruleRegistryMocks.createRuleDataService(),
config: createMockConfig(),
appClient: siemMock.createClient(),
ruleExecutionLogClient: ruleExecutionLogClientMock.create(),
};
};
type MockClients = ReturnType<typeof createMockClients>;
type SecuritySolutionRequestHandlerContextMock =
MockedKeys<SecuritySolutionRequestHandlerContext> & {
core: MockClients['core'];
};
const createRequestContextMock = (
clients: ReturnType<typeof createMockClients> = createMockClients()
clients: MockClients = createMockClients()
): SecuritySolutionRequestHandlerContextMock => {
const coreContext = coreMock.createRequestHandlerContext();
return {
alerting: { getRulesClient: jest.fn(() => clients.rulesClient) },
core: {
...coreContext,
elasticsearch: {
...coreContext.elasticsearch,
client: clients.clusterClient,
},
savedObjects: { client: clients.savedObjectsClient },
},
core: clients.core,
securitySolution: createSecuritySolutionRequestContextMock(clients),
actions: {} as unknown as jest.Mocked<ActionsApiRequestHandlerContext>,
alerting: {
getRulesClient: jest.fn(() => clients.rulesClient),
} as unknown as jest.Mocked<AlertingApiRequestHandlerContext>,
licensing: clients.licensing,
securitySolution: {
getAppClient: jest.fn(() => clients.appClient),
getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient),
getSpaceId: jest.fn(() => 'default'),
lists: {
getListClient: jest.fn(() => clients.lists.listClient),
getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient),
},
} as unknown as SecuritySolutionRequestHandlerContextMock;
};
};
const createSecuritySolutionRequestContextMock = (
clients: MockClients
): jest.Mocked<SecuritySolutionApiRequestHandlerContext> => {
const core = clients.core;
const kibanaRequest = requestMock.create();
return {
core,
getConfig: jest.fn(() => clients.config),
getFrameworkRequest: jest.fn(() => {
return {
...kibanaRequest.body,
[internalFrameworkRequest]: kibanaRequest,
context: { core },
user: {
username: 'mockUser',
},
};
}),
getAppClient: jest.fn(() => clients.appClient),
getSpaceId: jest.fn(() => 'default'),
getRuleDataService: jest.fn(() => clients.ruleDataService),
getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient),
getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient),
};
};
const createTools = () => {

View file

@ -5,7 +5,9 @@
* 2.0.
*/
import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'kibana/server';
import type { estypes } from '@elastic/elasticsearch';
import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'src/core/server';
import { ActionResult } from '../../../../../../actions/server';
import { SignalSearchResponse } from '../../signals/types';
import {
@ -562,6 +564,28 @@ export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({
],
});
export const getBasicEmptySearchResponse = (): estypes.SearchResponse<unknown> => ({
took: 1,
timed_out: false,
_shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
hits: {
hits: [],
total: { relation: 'eq', value: 0 },
max_score: 0,
},
});
export const getBasicNoShardsSearchResponse = (): estypes.SearchResponse<unknown> => ({
took: 1,
timed_out: false,
_shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
hits: {
hits: [],
total: { relation: 'eq', value: 0 },
max_score: 0,
},
});
export const getEmptySignalsResponse = (): SignalSearchResponse => ({
took: 1,
timed_out: false,
@ -588,7 +612,7 @@ export const getEmptyEqlSequencesResponse = (): EqlSearchResponse<unknown> => ({
timed_out: false,
});
export const getSuccessfulSignalUpdateResponse = () => ({
export const getSuccessfulSignalUpdateResponse = (): estypes.UpdateByQueryResponse => ({
took: 18,
timed_out: false,
total: 1,

View file

@ -16,9 +16,8 @@ import {
createBootstrapIndex,
} from '@kbn/securitysolution-es-utils';
import type {
AppClient,
SecuritySolutionApiRequestHandlerContext,
SecuritySolutionPluginRouter,
SecuritySolutionRequestHandlerContext,
} from '../../../../types';
import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants';
import { buildSiemResponse } from '../utils';
@ -32,15 +31,8 @@ import signalsPolicy from './signals_policy.json';
import { templateNeedsUpdate } from './check_template_version';
import { getIndexVersion } from './get_index_version';
import { isOutdated } from '../../migrations/helpers';
import { RuleDataPluginService } from '../../../../../../rule_registry/server';
import { ConfigType } from '../../../../config';
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
export const createIndexRoute = (
router: SecuritySolutionPluginRouter,
ruleDataService: RuleDataPluginService,
config: ConfigType
) => {
export const createIndexRoute = (router: SecuritySolutionPluginRouter) => {
router.post(
{
path: DETECTION_ENGINE_INDEX_URL,
@ -51,14 +43,13 @@ export const createIndexRoute = (
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental);
try {
const siemClient = context.securitySolution?.getAppClient();
if (!siemClient) {
return siemResponse.error({ statusCode: 404 });
}
await createDetectionIndex(context, siemClient, ruleDataService, ruleRegistryEnabled);
await createDetectionIndex(context.securitySolution);
return response.ok({ body: { acknowledged: true } });
} catch (err) {
const error = transformError(err);
@ -71,30 +62,18 @@ export const createIndexRoute = (
);
};
class CreateIndexError extends Error {
public readonly statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.statusCode = statusCode;
}
}
export const createDetectionIndex = async (
context: SecuritySolutionRequestHandlerContext,
siemClient: AppClient,
ruleDataService: RuleDataPluginService,
ruleRegistryEnabled: boolean
context: SecuritySolutionApiRequestHandlerContext
): Promise<void> => {
const config = context.getConfig();
const esClient = context.core.elasticsearch.client.asCurrentUser;
const spaceId = siemClient.getSpaceId();
if (!siemClient) {
throw new CreateIndexError('', 404);
}
const siemClient = context.getAppClient();
const spaceId = context.getSpaceId();
const index = siemClient.getSignalsIndex();
const indexExists = await getIndexExists(esClient, index);
const { ruleRegistryEnabled } = config.experimentalFeatures;
// If using the rule registry implementation, we don't want to create new .siem-signals indices -
// only create/update resources if there are existing indices
if (ruleRegistryEnabled && !indexExists) {
@ -106,7 +85,10 @@ export const createDetectionIndex = async (
if (!policyExists) {
await setPolicy(esClient, index, signalsPolicy);
}
const ruleDataService = context.getRuleDataService();
const aadIndexAliasName = ruleDataService.getResourceName(`security.alerts-${spaceId}`);
if (await templateNeedsUpdate({ alias: index, esClient })) {
await esClient.indices.putIndexTemplate({
name: index,

View file

@ -19,9 +19,9 @@ describe('read_privileges route', () => {
server = serverMock.create();
({ context } = requestContextMock.createTools());
context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({
body: getMockPrivilegesResult(),
});
context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(getMockPrivilegesResult())
);
readPrivilegesRoute(server.router, true);
});

View file

@ -10,14 +10,13 @@ import {
addPrepackagedRulesRequest,
getFindResultWithSingleHit,
getAlertMock,
getBasicEmptySearchResponse,
getBasicNoShardsSearchResponse,
} from '../__mocks__/request_responses';
import { requestContextMock, serverMock, createMockConfig, mockGetCurrentUser } from '../__mocks__';
import { configMock, requestContextMock, serverMock } from '../__mocks__';
import { AddPrepackagedRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema';
import { SecurityPluginSetup } from '../../../../../../security/server';
import { addPrepackedRulesRoute, createPrepackagedRules } from './add_prepackaged_rules_route';
import { listMock } from '../../../../../../lists/server/mocks';
import { siemMock } from '../../../../mocks';
import { FrameworkRequest } from '../../../framework';
import { ExceptionListClient } from '../../../../../../lists/server';
import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
@ -76,25 +75,21 @@ describe.each([
['Legacy', false],
['RAC', true],
])('add_prepackaged_rules_route - %s', (_, isRuleRegistryEnabled) => {
const siemMockClient = siemMock.createClient();
let server: ReturnType<typeof serverMock.create>;
let { clients, context } = requestContextMock.createTools();
let securitySetup: SecurityPluginSetup;
let mockExceptionsClient: ExceptionListClient;
const testif = isRuleRegistryEnabled ? test.skip : test;
const defaultConfig = context.securitySolution.getConfig();
beforeEach(() => {
server = serverMock.create();
({ clients, context } = requestContextMock.createTools());
securitySetup = {
authc: {
getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser),
},
authz: {},
} as unknown as SecurityPluginSetup;
mockExceptionsClient = listMock.getExceptionListClient();
context.securitySolution.getConfig.mockImplementation(() =>
configMock.withRuleRegistryEnabled(defaultConfig, isRuleRegistryEnabled)
);
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
clients.rulesClient.update.mockResolvedValue(
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
@ -110,9 +105,9 @@ describe.each([
});
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
);
addPrepackedRulesRoute(server.router, createMockConfig(), securitySetup, isRuleRegistryEnabled);
addPrepackedRulesRoute(server.router);
});
describe('status codes', () => {
@ -138,7 +133,9 @@ describe.each([
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
const request = addPrepackagedRulesRequest();
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(
getBasicNoShardsSearchResponse()
)
);
const response = await server.inject(request, context);
@ -302,39 +299,22 @@ describe.each([
describe('createPrepackagedRules', () => {
test('uses exception lists client from context when available', async () => {
context.lists = {
getExceptionListClient: jest.fn(),
getListClient: jest.fn(),
};
const config = createMockConfig();
await createPrepackagedRules(
context,
siemMockClient,
context.securitySolution,
clients.rulesClient,
{} as FrameworkRequest,
1200,
config.prebuiltRulesFromFileSystem,
config.prebuiltRulesFromSavedObjects,
mockExceptionsClient
);
expect(mockExceptionsClient.createEndpointList).not.toHaveBeenCalled();
expect(context.lists?.getExceptionListClient).toHaveBeenCalled();
expect(context.securitySolution.getExceptionListClient).toHaveBeenCalled();
});
test('uses passed in exceptions list client when lists client not available in context', async () => {
const { lists, ...myContext } = context;
const config = createMockConfig();
context.securitySolution.getExceptionListClient.mockImplementation(() => null);
await createPrepackagedRules(
myContext,
siemMockClient,
context.securitySolution,
clients.rulesClient,
{} as FrameworkRequest,
1200,
config.prebuiltRulesFromFileSystem,
config.prebuiltRulesFromSavedObjects,
mockExceptionsClient
);

View file

@ -9,9 +9,8 @@ import moment from 'moment';
import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils';
import { validate } from '@kbn/securitysolution-io-ts-utils';
import type {
AppClient,
SecuritySolutionApiRequestHandlerContext,
SecuritySolutionPluginRouter,
SecuritySolutionRequestHandlerContext,
} from '../../../../types';
import {
@ -21,10 +20,6 @@ import {
import { importTimelineResultSchema } from '../../../../../common/types/timeline';
import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants';
import { ConfigType } from '../../../../config';
import { SetupPlugins } from '../../../../plugin';
import { buildFrameworkRequest } from '../../../timeline/utils/common';
import { getLatestPrepackagedRules } from '../../rules/get_prepackaged_rules';
import { installPrepackagedRules } from '../../rules/install_prepacked_rules';
import { updatePrepackagedRules } from '../../rules/update_prepacked_rules';
@ -35,17 +30,11 @@ import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_
import { buildSiemResponse } from '../utils';
import { RulesClient } from '../../../../../../alerting/server';
import { FrameworkRequest } from '../../../framework';
import { ExceptionListClient } from '../../../../../../lists/server';
import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines';
export const addPrepackedRulesRoute = (
router: SecuritySolutionPluginRouter,
config: ConfigType,
security: SetupPlugins['security'],
isRuleRegistryEnabled: boolean
) => {
export const addPrepackedRulesRoute = (router: SecuritySolutionPluginRouter) => {
router.put(
{
path: DETECTION_ENGINE_PREPACKAGED_URL,
@ -63,7 +52,6 @@ export const addPrepackedRulesRoute = (
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
const frameworkRequest = await buildFrameworkRequest(context, security, _);
try {
const rulesClient = context.alerting?.getRulesClient();
@ -74,15 +62,9 @@ export const addPrepackedRulesRoute = (
}
const validated = await createPrepackagedRules(
context,
siemClient,
context.securitySolution,
rulesClient,
frameworkRequest,
config.maxTimelineImportExportSize,
config.prebuiltRulesFromFileSystem,
config.prebuiltRulesFromSavedObjects,
undefined,
isRuleRegistryEnabled
undefined
);
return response.ok({ body: validated ?? {} });
} catch (err) {
@ -105,22 +87,26 @@ class PrepackagedRulesError extends Error {
}
export const createPrepackagedRules = async (
context: SecuritySolutionRequestHandlerContext,
siemClient: AppClient,
context: SecuritySolutionApiRequestHandlerContext,
rulesClient: RulesClient,
frameworkRequest: FrameworkRequest,
maxTimelineImportExportSize: ConfigType['maxTimelineImportExportSize'],
prebuiltRulesFromFileSystem: ConfigType['prebuiltRulesFromFileSystem'],
prebuiltRulesFromSavedObjects: ConfigType['prebuiltRulesFromSavedObjects'],
exceptionsClient?: ExceptionListClient,
isRuleRegistryEnabled?: boolean | undefined
exceptionsClient?: ExceptionListClient
): Promise<PrePackagedRulesAndTimelinesSchema | null> => {
const config = context.getConfig();
const frameworkRequest = context.getFrameworkRequest();
const esClient = context.core.elasticsearch.client;
const savedObjectsClient = context.core.savedObjects.client;
const exceptionsListClient =
context.lists != null ? context.lists.getExceptionListClient() : exceptionsClient;
const siemClient = context.getAppClient();
const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient;
const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient);
const ruleStatusClient = context.securitySolution.getExecutionLogClient();
const ruleStatusClient = context.getExecutionLogClient();
const {
maxTimelineImportExportSize,
prebuiltRulesFromFileSystem,
prebuiltRulesFromSavedObjects,
experimentalFeatures: { ruleRegistryEnabled },
} = config;
if (!siemClient || !rulesClient) {
throw new PrepackagedRulesError('', 404);
}
@ -137,12 +123,12 @@ export const createPrepackagedRules = async (
);
const prepackagedRules = await getExistingPrepackagedRules({
rulesClient,
isRuleRegistryEnabled: isRuleRegistryEnabled ?? false,
isRuleRegistryEnabled: ruleRegistryEnabled,
});
const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules);
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules);
const signalsIndex = siemClient.getSignalsIndex();
if (!isRuleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) {
if (!ruleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) {
const signalsIndexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex);
if (!signalsIndexExists) {
throw new PrepackagedRulesError(
@ -153,12 +139,7 @@ export const createPrepackagedRules = async (
}
await Promise.all(
installPrepackagedRules(
rulesClient,
rulesToInstall,
signalsIndex,
isRuleRegistryEnabled ?? false
)
installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex, ruleRegistryEnabled)
);
const timeline = await installPrepackagedTimelines(
maxTimelineImportExportSize,
@ -172,11 +153,11 @@ export const createPrepackagedRules = async (
await updatePrepackagedRules(
rulesClient,
savedObjectsClient,
context.securitySolution.getSpaceId(),
context.getSpaceId(),
ruleStatusClient,
rulesToUpdate,
signalsIndex,
isRuleRegistryEnabled ?? false
ruleRegistryEnabled
);
const prepackagedRulesOutput: PrePackagedRulesAndTimelinesSchema = {

View file

@ -14,6 +14,8 @@ import {
getEmptyFindResult,
getAlertMock,
createBulkMlRuleRequest,
getBasicEmptySearchResponse,
getBasicNoShardsSearchResponse,
} from '../__mocks__/request_responses';
import { requestContextMock, serverMock, requestMock } from '../__mocks__';
import { createRulesBulkRoute } from './create_rules_bulk_route';
@ -43,7 +45,7 @@ describe.each([
); // successful creation
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
);
createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled);
});
@ -93,7 +95,9 @@ describe.each([
test('returns an error object if the index does not exist when rule registry not enabled', async () => {
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(
getBasicNoShardsSearchResponse()
)
);
const response = await server.inject(getReadBulkRequest(), context);

View file

@ -13,6 +13,8 @@ import {
getRuleExecutionStatuses,
getFindResultWithSingleHit,
createMlRuleRequest,
getBasicEmptySearchResponse,
getBasicNoShardsSearchResponse,
} from '../__mocks__/request_responses';
import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks';
import { buildMlAuthz } from '../../../machine_learning/authz';
@ -44,7 +46,7 @@ describe.each([
clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); // needed to transform: ;
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
);
createRulesRoute(server.router, ml, isRuleRegistryEnabled);
});
@ -103,7 +105,9 @@ describe.each([
describe('unhappy paths', () => {
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(
getBasicNoShardsSearchResponse()
)
);
const response = await server.inject(getCreateRequest(), context);

View file

@ -12,6 +12,8 @@ import {
getEmptyFindResult,
getAlertMock,
getFindResultWithSingleHit,
getBasicEmptySearchResponse,
getBasicNoShardsSearchResponse,
} from '../__mocks__/request_responses';
import { createMockConfig, requestContextMock, serverMock, requestMock } from '../__mocks__';
import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks';
@ -52,7 +54,7 @@ describe.each([
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
);
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
);
importRulesRoute(server.router, config, ml, isRuleRegistryEnabled);
});
@ -133,7 +135,9 @@ describe.each([
test('returns an error if the index does not exist when rule registry not enabled', async () => {
clients.appClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
elasticsearchClientMock.createSuccessTransportRequestPromise(
getBasicNoShardsSearchResponse()
)
);
const response = await server.inject(request, context);
expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400);

View file

@ -26,11 +26,13 @@ describe('set signal status', () => {
beforeEach(() => {
server = serverMock.create();
({ context } = requestContextMock.createTools());
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(
getSuccessfulSignalUpdateResponse()
)
);
setSignalsStatusRoute(server.router);
});

View file

@ -5,14 +5,13 @@
* 2.0.
*/
import { KibanaRequest } from '../../../../../../src/core/server';
import { KibanaRequest, RequestHandlerContext } from '../../../../../../src/core/server';
import { AuthenticatedUser } from '../../../../security/common/model';
import type { SecuritySolutionRequestHandlerContext } from '../../types';
export const internalFrameworkRequest = Symbol('internalFrameworkRequest');
export interface FrameworkRequest extends Pick<KibanaRequest, 'body'> {
[internalFrameworkRequest]: KibanaRequest;
context: SecuritySolutionRequestHandlerContext;
context: RequestHandlerContext;
user: AuthenticatedUser | null;
}

View file

@ -32,17 +32,12 @@ const notes = [
const existingNoteIds = undefined;
const isImmutable = true;
jest.mock('moment', () => {
const mockMoment = {
toISOString: jest
.fn()
.mockReturnValueOnce('2020-11-03T11:37:31.655Z')
.mockReturnValue('2020-11-04T11:37:31.655Z'),
subtract: jest.fn(),
};
mockMoment.subtract.mockReturnValue(mockMoment);
return jest.fn().mockReturnValue(mockMoment);
});
// System under test uses moment.js under the hood, so we need to mock time.
// Mocking moment via jest.mock('moment') breaks imports of moment in other files.
// Instead, we simply mock Date.now() via jest API and moment starts using it.
// This affects all the tests in this file and doesn't affect tests in other files.
// https://jestjs.io/docs/timer-mocks
jest.useFakeTimers('modern').setSystemTime(new Date('2020-11-04T11:37:31.655Z'));
jest.mock('../../../saved_object/timelines', () => ({
persistTimeline: jest.fn().mockResolvedValue({

View file

@ -12,15 +12,14 @@ import { Readable } from 'stream';
import { createListStream } from '@kbn/utils';
import { schema } from '@kbn/config-schema';
import { KibanaRequest } from 'src/core/server';
import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
import { formatErrors } from '@kbn/securitysolution-io-ts-utils';
import { SetupPlugins, StartPlugins } from '../../../plugin';
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
import { FrameworkRequest } from '../../framework';
export const buildFrameworkRequest = async (
context: SecuritySolutionRequestHandlerContext,
context: RequestHandlerContext,
security: StartPlugins['security'] | SetupPlugins['security'] | undefined,
request: KibanaRequest
): Promise<FrameworkRequest> => {
@ -30,7 +29,7 @@ export const buildFrameworkRequest = async (
return set<FrameworkRequest>(
'user',
user,
set<KibanaRequest & { context: SecuritySolutionRequestHandlerContext }>(
set<KibanaRequest & { context: RequestHandlerContext }>(
'context.core.savedObjects.client',
savedObjectsClient,
request

View file

@ -9,45 +9,15 @@ import { Observable } from 'rxjs';
import LRU from 'lru-cache';
import { estypes } from '@elastic/elasticsearch';
import {
CoreSetup,
CoreStart,
Logger,
Plugin as IPlugin,
PluginInitializerContext,
SavedObjectsClient,
} from '../../../../src/core/server';
import {
PluginSetup as DataPluginSetup,
PluginStart as DataPluginStart,
} from '../../../../src/plugins/data/server';
import {
UsageCollectionSetup,
UsageCounter,
} from '../../../../src/plugins/usage_collection/server';
import {
PluginSetupContract as AlertingSetup,
PluginStartContract as AlertPluginStartContract,
} from '../../alerting/server';
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
import { Logger, SavedObjectsClient } from '../../../../src/core/server';
import { UsageCounter } from '../../../../src/plugins/usage_collection/server';
import { PluginStartContract as CasesPluginStartContract } from '../../cases/server';
import { ECS_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets';
import { SecurityPluginSetup as SecuritySetup, SecurityPluginStart } from '../../security/server';
import {
IRuleDataClient,
RuleRegistryPluginSetupContract,
RuleRegistryPluginStartContract,
Dataset,
} from '../../rule_registry/server';
import { PluginSetupContract as FeaturesSetup } from '../../features/server';
import { MlPluginSetup as MlSetup } from '../../ml/server';
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
import { IRuleDataClient, Dataset } from '../../rule_registry/server';
import { ListPluginSetup } from '../../lists/server';
import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server';
import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server';
import { ILicense, LicensingPluginStart } from '../../licensing/server';
import { FleetStartContract } from '../../fleet/server';
import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
import { ILicense } from '../../licensing/server';
import {
createEqlAlertType,
createIndicatorMatchAlertType,
@ -70,7 +40,6 @@ import {
SIGNALS_ID,
LEGACY_NOTIFICATIONS_ID,
QUERY_RULE_TYPE_ID,
DEFAULT_SPACE_ID,
INDICATOR_RULE_TYPE_ID,
ML_RULE_TYPE_ID,
EQL_RULE_TYPE_ID,
@ -89,18 +58,13 @@ import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps';
import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution';
import { TelemetryEventsSender } from './lib/telemetry/sender';
import { TelemetryReceiver } from './lib/telemetry/receiver';
import {
TelemetryPluginStart,
TelemetryPluginSetup,
} from '../../../../src/plugins/telemetry/server';
import { licenseService } from './lib/license';
import { PolicyWatcher } from './endpoint/lib/policy/license_watch';
import { parseExperimentalConfigValue } from '../common/experimental_features';
import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet';
import aadFieldConversion from './lib/detection_engine/routes/index/signal_aad_mapping.json';
import { alertsFieldMap } from './lib/detection_engine/rule_types/field_maps/alerts';
import { rulesFieldMap } from './lib/detection_engine/rule_types/field_maps/rules';
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider';
import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features';
import { EndpointMetadataService } from './endpoint/services/metadata';
import { CreateRuleOptions } from './lib/detection_engine/rule_types/types';
@ -109,50 +73,28 @@ import { legacyRulesNotificationAlertType } from './lib/detection_engine/notific
// eslint-disable-next-line no-restricted-imports
import { legacyIsNotificationAlertExecutor } from './lib/detection_engine/notifications/legacy_types';
import { createSecurityRuleTypeWrapper } from './lib/detection_engine/rule_types/create_security_rule_type_wrapper';
import { IEventLogClientService, IEventLogService } from '../../event_log/server';
import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider';
export interface SetupPlugins {
alerting: AlertingSetup;
data: DataPluginSetup;
encryptedSavedObjects?: EncryptedSavedObjectsSetup;
eventLog: IEventLogService;
features: FeaturesSetup;
lists?: ListPluginSetup;
ml?: MlSetup;
ruleRegistry: RuleRegistryPluginSetupContract;
security?: SecuritySetup;
spaces?: SpacesSetup;
taskManager?: TaskManagerSetupContract;
telemetry?: TelemetryPluginSetup;
usageCollection?: UsageCollectionSetup;
}
import { RequestContextFactory } from './request_context_factory';
export interface StartPlugins {
alerting: AlertPluginStartContract;
cases?: CasesPluginStartContract;
data: DataPluginStart;
eventLog: IEventLogClientService;
fleet?: FleetStartContract;
licensing: LicensingPluginStart;
ruleRegistry: RuleRegistryPluginStartContract;
security: SecurityPluginStart;
taskManager?: TaskManagerStartContract;
telemetry?: TelemetryPluginStart;
}
import type {
ISecuritySolutionPlugin,
SecuritySolutionPluginSetupDependencies,
SecuritySolutionPluginStartDependencies,
SecuritySolutionPluginCoreSetupDependencies,
SecuritySolutionPluginCoreStartDependencies,
SecuritySolutionPluginSetup,
SecuritySolutionPluginStart,
PluginInitializerContext,
} from './plugin_contract';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PluginSetup {}
export { SetupPlugins, StartPlugins, PluginSetup, PluginStart } from './plugin_contract';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PluginStart {}
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
private readonly logger: Logger;
export class Plugin implements ISecuritySolutionPlugin {
private readonly pluginContext: PluginInitializerContext;
private readonly config: ConfigType;
private context: PluginInitializerContext;
private appClientFactory: AppClientFactory;
private setupPlugins?: SetupPlugins;
private readonly logger: Logger;
private readonly appClientFactory: AppClientFactory;
private readonly endpointAppContextService = new EndpointAppContextService();
private readonly telemetryReceiver: TelemetryReceiver;
private readonly telemetryEventsSender: TelemetryEventsSender;
@ -167,10 +109,11 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
private telemetryUsageCounter?: UsageCounter;
constructor(context: PluginInitializerContext) {
this.context = context;
this.logger = context.logger.get();
this.pluginContext = context;
this.config = createConfig(context);
this.logger = context.logger.get();
this.appClientFactory = new AppClientFactory();
// Cache up to three artifacts with a max retention of 5 mins each
this.artifactsCache = new LRU<string, Buffer>({ max: 3, maxAge: 1000 * 60 * 5 });
this.telemetryEventsSender = new TelemetryEventsSender(this.logger);
@ -179,26 +122,47 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
this.logger.debug('plugin initialized');
}
public setup(core: CoreSetup<StartPlugins, PluginStart>, plugins: SetupPlugins) {
public setup(
core: SecuritySolutionPluginCoreSetupDependencies,
plugins: SecuritySolutionPluginSetupDependencies
): SecuritySolutionPluginSetup {
this.logger.debug('plugin setup');
this.setupPlugins = plugins;
const config = this.config;
const globalConfig = this.context.config.legacy.get();
const { pluginContext, config, logger, appClientFactory } = this;
const experimentalFeatures = config.experimentalFeatures;
appClientFactory.setup({
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
config,
});
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
initSavedObjects(core.savedObjects);
initUiSettings(core.uiSettings, experimentalFeatures);
const eventLogService = plugins.eventLog;
registerEventLogProvider(eventLogService);
const requestContextFactory = new RequestContextFactory({ config, core, plugins });
const router = core.http.createRouter<SecuritySolutionRequestHandlerContext>();
core.http.registerRouteHandlerContext<SecuritySolutionRequestHandlerContext, typeof APP_ID>(
APP_ID,
(context, request) => requestContextFactory.create(context, request)
);
const endpointContext: EndpointAppContext = {
logFactory: this.context.logger,
logFactory: pluginContext.logger,
service: this.endpointAppContextService,
config: (): Promise<ConfigType> => Promise.resolve(config),
experimentalFeatures,
};
this.endpointAppContextService.setup({
securitySolutionRequestContextFactory: requestContextFactory,
});
initUsageCollectors({
core,
kibanaIndex: globalConfig.kibana.index,
kibanaIndex: config.kibanaIndex,
signalsIndex: config.signalsIndex,
ml: plugins.ml,
usageCollection: plugins.usageCollection,
@ -206,29 +170,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
this.telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID);
const eventLogService = plugins.eventLog;
registerEventLogProvider(eventLogService);
const router = core.http.createRouter<SecuritySolutionRequestHandlerContext>();
core.http.registerRouteHandlerContext<SecuritySolutionRequestHandlerContext, typeof APP_ID>(
APP_ID,
(context, request, response) => ({
getAppClient: () => this.appClientFactory.create(request),
getSpaceId: () => plugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID,
getExecutionLogClient: () =>
new RuleExecutionLogClient({
savedObjectsClient: context.core.savedObjects.client,
eventLogService,
underlyingClient: config.ruleExecutionLog.underlyingClient,
}),
})
);
this.appClientFactory.setup({
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
config,
});
// TODO: Once we are past experimental phase this check can be removed along with legacy registration of rules
const isRuleRegistryEnabled = experimentalFeatures.ruleRegistryEnabled;
@ -265,9 +206,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
// Register rule types via rule-registry
const createRuleOptions: CreateRuleOptions = {
experimentalFeatures,
logger: this.logger,
logger,
ml: plugins.ml,
version: this.context.env.packageInfo.version,
version: pluginContext.env.packageInfo.version,
};
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
@ -278,19 +219,15 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
eventLogService,
});
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createEqlAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(createRuleOptions)));
plugins.alerting.registerType(
securityRuleTypeWrapper(createIndicatorMatchAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createMlAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
plugins.alerting.registerType(securityRuleTypeWrapper(createMlAlertType(createRuleOptions)));
plugins.alerting.registerType(
securityRuleTypeWrapper(createQueryAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
plugins.alerting.registerType(
securityRuleTypeWrapper(createThresholdAlertType(createRuleOptions))
);
}
@ -302,8 +239,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
plugins.encryptedSavedObjects?.canEncrypt === true,
plugins.security,
plugins.ml,
ruleDataService,
this.logger,
logger,
isRuleRegistryEnabled
);
registerEndpointRoutes(router, endpointContext);
@ -329,27 +265,25 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
plugins.features.registerKibanaFeature(getCasesKibanaFeature());
// Continue to register legacy rules against alerting client exposed through rule-registry
if (this.setupPlugins.alerting != null) {
if (plugins.alerting != null) {
const signalRuleType = signalRulesAlertType({
logger: this.logger,
logger,
eventsTelemetry: this.telemetryEventsSender,
version: this.context.env.packageInfo.version,
version: pluginContext.env.packageInfo.version,
ml: plugins.ml,
lists: plugins.lists,
config: this.config,
config,
experimentalFeatures,
eventLogService,
});
const ruleNotificationType = legacyRulesNotificationAlertType({
logger: this.logger,
});
const ruleNotificationType = legacyRulesNotificationAlertType({ logger });
if (isAlertExecutor(signalRuleType)) {
this.setupPlugins.alerting.registerType(signalRuleType);
plugins.alerting.registerType(signalRuleType);
}
if (legacyIsNotificationAlertExecutor(ruleNotificationType)) {
this.setupPlugins.alerting.registerType(ruleNotificationType);
plugins.alerting.registerType(ruleNotificationType);
}
}
@ -394,10 +328,14 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
return {};
}
public start(core: CoreStart, plugins: StartPlugins) {
public start(
core: SecuritySolutionPluginCoreStartDependencies,
plugins: SecuritySolutionPluginStartDependencies
): SecuritySolutionPluginStart {
const { config, logger, appClientFactory } = this;
const savedObjectsClient = new SavedObjectsClient(core.savedObjects.createInternalRepository());
const registerIngestCallback = plugins.fleet?.registerExternalCallback;
const logger = this.logger;
let manifestManager: ManifestManager | undefined;
this.licensing$ = plugins.licensing.license$;
@ -405,7 +343,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
if (this.lists && plugins.taskManager && plugins.fleet) {
// Exceptions, Artifacts and Manifests start
const taskManager = plugins.taskManager;
const experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental);
const exceptionListClient = this.lists.getExceptionListClient(savedObjectsClient, 'kibana');
const artifactClient = new EndpointArtifactClient(
plugins.fleet.createArtifactsClient('endpoint')
@ -418,7 +355,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
packagePolicyService: plugins.fleet.packagePolicyService,
logger,
cache: this.artifactsCache,
experimentalFeatures,
experimentalFeatures: config.experimentalFeatures,
});
// Migrate artifacts to fleet and then start the minifest task after that is done
@ -463,7 +400,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
plugins.fleet?.agentPolicyService!,
logger
),
appClientFactory: this.appClientFactory,
appClientFactory,
security: plugins.security,
alerting: plugins.alerting,
config: this.config,
@ -475,10 +412,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
exceptionListsClient: exceptionListClient,
});
const globalConfig = this.context.config.legacy.get();
this.telemetryReceiver.start(
core,
globalConfig.kibana.index,
config.kibanaIndex,
this.endpointAppContextService,
exceptionListClient
);

View file

@ -0,0 +1,102 @@
/*
* 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 {
CoreSetup,
CoreStart,
Plugin,
PluginInitializerContext,
} from '../../../../src/core/server';
import {
PluginSetup as DataPluginSetup,
PluginStart as DataPluginStart,
} from '../../../../src/plugins/data/server';
import { UsageCollectionSetup as UsageCollectionPluginSetup } from '../../../../src/plugins/usage_collection/server';
import {
PluginSetupContract as AlertingPluginSetup,
PluginStartContract as AlertingPluginStart,
} from '../../alerting/server';
import { PluginStartContract as CasesPluginStart } from '../../cases/server';
import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server';
import { IEventLogClientService, IEventLogService } from '../../event_log/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { FleetStartContract as FleetPluginStart } from '../../fleet/server';
import { LicensingPluginStart } from '../../licensing/server';
import { ListPluginSetup } from '../../lists/server';
import { MlPluginSetup } from '../../ml/server';
import {
RuleRegistryPluginSetupContract as RuleRegistryPluginSetup,
RuleRegistryPluginStartContract as RuleRegistryPluginStart,
} from '../../rule_registry/server';
import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server';
import { SpacesPluginSetup } from '../../spaces/server';
import {
TaskManagerSetupContract as TaskManagerPluginSetup,
TaskManagerStartContract as TaskManagerPluginStart,
} from '../../task_manager/server';
import {
TelemetryPluginStart,
TelemetryPluginSetup,
} from '../../../../src/plugins/telemetry/server';
export interface SecuritySolutionPluginSetupDependencies {
alerting: AlertingPluginSetup;
data: DataPluginSetup;
encryptedSavedObjects?: EncryptedSavedObjectsPluginSetup;
eventLog: IEventLogService;
features: FeaturesPluginSetup;
lists?: ListPluginSetup;
ml?: MlPluginSetup;
ruleRegistry: RuleRegistryPluginSetup;
security?: SecurityPluginSetup;
spaces?: SpacesPluginSetup;
taskManager?: TaskManagerPluginSetup;
telemetry?: TelemetryPluginSetup;
usageCollection?: UsageCollectionPluginSetup;
}
export interface SecuritySolutionPluginStartDependencies {
alerting: AlertingPluginStart;
cases?: CasesPluginStart;
data: DataPluginStart;
eventLog: IEventLogClientService;
fleet?: FleetPluginStart;
licensing: LicensingPluginStart;
ruleRegistry: RuleRegistryPluginStart;
security: SecurityPluginStart;
taskManager?: TaskManagerPluginStart;
telemetry?: TelemetryPluginStart;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SecuritySolutionPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SecuritySolutionPluginStart {}
export type SecuritySolutionPluginCoreSetupDependencies = CoreSetup<
SecuritySolutionPluginStartDependencies,
SecuritySolutionPluginStart
>;
export type SecuritySolutionPluginCoreStartDependencies = CoreStart;
export type ISecuritySolutionPlugin = Plugin<
SecuritySolutionPluginSetup,
SecuritySolutionPluginStart,
SecuritySolutionPluginSetupDependencies,
SecuritySolutionPluginStartDependencies
>;
export type {
PluginInitializerContext,
// Legacy type identifiers left for compatibility with the rest of the code:
SecuritySolutionPluginSetupDependencies as SetupPlugins,
SecuritySolutionPluginStartDependencies as StartPlugins,
SecuritySolutionPluginSetup as PluginSetup,
SecuritySolutionPluginStart as PluginStart,
};

View file

@ -0,0 +1,23 @@
/*
* 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 { requestContextMock } from './lib/detection_engine/routes/__mocks__';
import { IRequestContextFactory } from './request_context_factory';
export const requestContextFactoryMock = {
create: (): jest.Mocked<IRequestContextFactory> => ({
create: jest.fn((context, request) => {
const fullContext = requestContextMock.create();
const securitySolutionContext = fullContext.securitySolution;
return Promise.resolve(securitySolutionContext);
}),
}),
};
export const RequestContextFactoryMock = jest
.fn<jest.Mocked<IRequestContextFactory>, []>()
.mockImplementation(requestContextFactoryMock.create);

View file

@ -0,0 +1,90 @@
/*
* 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 { KibanaRequest, RequestHandlerContext } from 'kibana/server';
import { ExceptionListClient } from '../../lists/server';
import { DEFAULT_SPACE_ID } from '../common/constants';
import { AppClientFactory } from './client';
import { ConfigType } from './config';
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
import { buildFrameworkRequest } from './lib/timeline/utils/common';
import {
SecuritySolutionPluginCoreSetupDependencies,
SecuritySolutionPluginSetupDependencies,
} from './plugin_contract';
import { SecuritySolutionApiRequestHandlerContext } from './types';
export interface IRequestContextFactory {
create(
context: RequestHandlerContext,
request: KibanaRequest
): Promise<SecuritySolutionApiRequestHandlerContext>;
}
interface ConstructorOptions {
config: ConfigType;
core: SecuritySolutionPluginCoreSetupDependencies;
plugins: SecuritySolutionPluginSetupDependencies;
}
export class RequestContextFactory implements IRequestContextFactory {
private readonly appClientFactory: AppClientFactory;
constructor(private readonly options: ConstructorOptions) {
this.appClientFactory = new AppClientFactory();
}
public async create(
context: RequestHandlerContext,
request: KibanaRequest
): Promise<SecuritySolutionApiRequestHandlerContext> {
const { options, appClientFactory } = this;
const { config, plugins } = options;
const { lists, ruleRegistry, security, spaces } = plugins;
appClientFactory.setup({
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
config,
});
const frameworkRequest = await buildFrameworkRequest(context, security, request);
return {
core: context.core,
getConfig: () => config,
getFrameworkRequest: () => frameworkRequest,
getAppClient: () => appClientFactory.create(request),
getSpaceId: () => spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID,
getRuleDataService: () => ruleRegistry.ruleDataService,
getExecutionLogClient: () =>
new RuleExecutionLogClient({
savedObjectsClient: context.core.savedObjects.client,
eventLogService: plugins.eventLog,
underlyingClient: config.ruleExecutionLog.underlyingClient,
}),
getExceptionListClient: () => {
if (!lists) {
return null;
}
const username = security?.authc.getCurrentUser(request)?.username || 'elastic';
return new ExceptionListClient({
savedObjectsClient: context.core.savedObjects.client,
user: username,
});
},
};
}
}

View file

@ -6,7 +6,6 @@
*/
import { Logger } from 'src/core/server';
import { RuleDataPluginService } from '../../../rule_registry/server';
import { SecuritySolutionPluginRouter } from '../types';
@ -67,7 +66,6 @@ export const initRoutes = (
hasEncryptionKey: boolean,
security: SetupPlugins['security'],
ml: SetupPlugins['ml'],
ruleDataService: RuleDataPluginService,
logger: Logger,
isRuleRegistryEnabled: boolean
) => {
@ -85,7 +83,7 @@ export const initRoutes = (
// TODO: pass isRuleRegistryEnabled to all relevant routes
addPrepackedRulesRoute(router, config, security, isRuleRegistryEnabled);
addPrepackedRulesRoute(router);
getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled);
createRulesBulkRoute(router, ml, isRuleRegistryEnabled);
updateRulesBulkRoute(router, ml, isRuleRegistryEnabled);
@ -127,7 +125,7 @@ export const initRoutes = (
// Detection Engine index routes that have the REST endpoints of /api/detection_engine/index
// All REST index creation, policy management for spaces
createIndexRoute(router, ruleDataService, config);
createIndexRoute(router);
readIndexRoute(router, config);
deleteIndexRoute(router);

View file

@ -6,28 +6,35 @@
*/
import type { IRouter, RequestHandlerContext } from 'src/core/server';
import type { ListsApiRequestHandlerContext } from '../../lists/server';
import type { LicensingApiRequestHandlerContext } from '../../licensing/server';
import type { ActionsApiRequestHandlerContext } from '../../actions/server';
import type { AlertingApiRequestHandlerContext } from '../../alerting/server';
import type { LicensingApiRequestHandlerContext } from '../../licensing/server';
import type { ListsApiRequestHandlerContext, ExceptionListClient } from '../../lists/server';
import type { IRuleDataService } from '../../rule_registry/server';
import { AppClient } from './client';
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
import type { ActionsApiRequestHandlerContext } from '../../actions/server';
import { ConfigType } from './config';
import { IRuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/types';
import { FrameworkRequest } from './lib/framework';
export { AppClient };
export interface AppRequestContext {
export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext {
getConfig: () => ConfigType;
getFrameworkRequest: () => FrameworkRequest;
getAppClient: () => AppClient;
getSpaceId: () => string;
getExecutionLogClient: () => RuleExecutionLogClient;
getRuleDataService: () => IRuleDataService;
getExecutionLogClient: () => IRuleExecutionLogClient;
getExceptionListClient: () => ExceptionListClient | null;
}
export type SecuritySolutionRequestHandlerContext = RequestHandlerContext & {
securitySolution: AppRequestContext;
licensing: LicensingApiRequestHandlerContext;
alerting: AlertingApiRequestHandlerContext;
export interface SecuritySolutionRequestHandlerContext extends RequestHandlerContext {
securitySolution: SecuritySolutionApiRequestHandlerContext;
actions: ActionsApiRequestHandlerContext;
alerting: AlertingApiRequestHandlerContext;
licensing: LicensingApiRequestHandlerContext;
lists?: ListsApiRequestHandlerContext;
};
}
export type SecuritySolutionPluginRouter = IRouter<SecuritySolutionRequestHandlerContext>;