diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ae3db559b61b..86be6e7e97155 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -338,7 +338,7 @@ The `config/kibana.yml` file stores user configuration directives. Since this fi #### Setting Up SSL -Kibana includes a self-signed certificate that can be used for development purposes: `yarn start --ssl`. +Kibana includes self-signed certificates that can be used for development purposes in the browser and for communicating with Elasticsearch: `yarn start --ssl` & `yarn es snapshot --ssl`. ### Linting diff --git a/docs/development/core/public/kibana-plugin-public.httperrorrequest.error.md b/docs/development/core/public/kibana-plugin-public.httperrorrequest.error.md deleted file mode 100644 index a8b511f889cdf..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.httperrorrequest.error.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpErrorRequest](./kibana-plugin-public.httperrorrequest.md) > [error](./kibana-plugin-public.httperrorrequest.error.md) - -## HttpErrorRequest.error property - -Signature: - -```typescript -error: Error; -``` diff --git a/docs/development/core/public/kibana-plugin-public.httperrorrequest.md b/docs/development/core/public/kibana-plugin-public.httperrorrequest.md deleted file mode 100644 index e28d092eda71d..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.httperrorrequest.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpErrorRequest](./kibana-plugin-public.httperrorrequest.md) - -## HttpErrorRequest interface - - -Signature: - -```typescript -export interface HttpErrorRequest -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [error](./kibana-plugin-public.httperrorrequest.error.md) | Error | | -| [request](./kibana-plugin-public.httperrorrequest.request.md) | Request | | - diff --git a/docs/development/core/public/kibana-plugin-public.httperrorrequest.request.md b/docs/development/core/public/kibana-plugin-public.httperrorrequest.request.md deleted file mode 100644 index 7a94eeb91bbc8..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.httperrorrequest.request.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpErrorRequest](./kibana-plugin-public.httperrorrequest.md) > [request](./kibana-plugin-public.httperrorrequest.request.md) - -## HttpErrorRequest.request property - -Signature: - -```typescript -request: Request; -``` diff --git a/docs/development/core/public/kibana-plugin-public.httperrorresponse.error.md b/docs/development/core/public/kibana-plugin-public.httperrorresponse.error.md deleted file mode 100644 index 59eee87cb70ba..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.httperrorresponse.error.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpErrorResponse](./kibana-plugin-public.httperrorresponse.md) > [error](./kibana-plugin-public.httperrorresponse.error.md) - -## HttpErrorResponse.error property - -Signature: - -```typescript -error: Error | IHttpFetchError; -``` diff --git a/docs/development/core/public/kibana-plugin-public.httperrorresponse.md b/docs/development/core/public/kibana-plugin-public.httperrorresponse.md deleted file mode 100644 index 5b1ee898a444d..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.httperrorresponse.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpErrorResponse](./kibana-plugin-public.httperrorresponse.md) - -## HttpErrorResponse interface - - -Signature: - -```typescript -export interface HttpErrorResponse extends IHttpResponse -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [error](./kibana-plugin-public.httperrorresponse.error.md) | Error | IHttpFetchError | | - diff --git a/docs/development/core/public/kibana-plugin-public.httpfetchoptions.asresponse.md b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.asresponse.md index 250cf83309b3c..207ddf205c88a 100644 --- a/docs/development/core/public/kibana-plugin-public.httpfetchoptions.asresponse.md +++ b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.asresponse.md @@ -4,7 +4,7 @@ ## HttpFetchOptions.asResponse property -When `true` the return type of [HttpHandler](./kibana-plugin-public.httphandler.md) will be an [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) with detailed request and response information. When `false`, the return type will just be the parsed response body. Defaults to `false`. +When `true` the return type of [HttpHandler](./kibana-plugin-public.httphandler.md) will be an [HttpResponse](./kibana-plugin-public.httpresponse.md) with detailed request and response information. When `false`, the return type will just be the parsed response body. Defaults to `false`. Signature: diff --git a/docs/development/core/public/kibana-plugin-public.httpfetchoptions.assystemrequest.md b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.assystemrequest.md new file mode 100644 index 0000000000000..7243d318df6fc --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.assystemrequest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) > [asSystemRequest](./kibana-plugin-public.httpfetchoptions.assystemrequest.md) + +## HttpFetchOptions.asSystemRequest property + +Whether or not the request should include the "system request" header to differentiate an end user request from Kibana internal request. Can be read on the server-side using KibanaRequest\#isSystemRequest. Defaults to `false`. + +Signature: + +```typescript +asSystemRequest?: boolean; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httpfetchoptions.md b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.md index 6a0c4a8a7f137..403a1ea7ee4e8 100644 --- a/docs/development/core/public/kibana-plugin-public.httpfetchoptions.md +++ b/docs/development/core/public/kibana-plugin-public.httpfetchoptions.md @@ -16,7 +16,8 @@ export interface HttpFetchOptions extends HttpRequestInit | Property | Type | Description | | --- | --- | --- | -| [asResponse](./kibana-plugin-public.httpfetchoptions.asresponse.md) | boolean | When true the return type of [HttpHandler](./kibana-plugin-public.httphandler.md) will be an [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) with detailed request and response information. When false, the return type will just be the parsed response body. Defaults to false. | +| [asResponse](./kibana-plugin-public.httpfetchoptions.asresponse.md) | boolean | When true the return type of [HttpHandler](./kibana-plugin-public.httphandler.md) will be an [HttpResponse](./kibana-plugin-public.httpresponse.md) with detailed request and response information. When false, the return type will just be the parsed response body. Defaults to false. | +| [asSystemRequest](./kibana-plugin-public.httpfetchoptions.assystemrequest.md) | boolean | Whether or not the request should include the "system request" header to differentiate an end user request from Kibana internal request. Can be read on the server-side using KibanaRequest\#isSystemRequest. Defaults to false. | | [headers](./kibana-plugin-public.httpfetchoptions.headers.md) | HttpHeadersInit | Headers to send with the request. See [HttpHeadersInit](./kibana-plugin-public.httpheadersinit.md). | | [prependBasePath](./kibana-plugin-public.httpfetchoptions.prependbasepath.md) | boolean | Whether or not the request should automatically prepend the basePath. Defaults to true. | | [query](./kibana-plugin-public.httpfetchoptions.query.md) | HttpFetchQuery | The query string for an HTTP request. See [HttpFetchQuery](./kibana-plugin-public.httpfetchquery.md). | diff --git a/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.md b/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.md new file mode 100644 index 0000000000000..adccca83f5bb4 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpFetchOptionsWithPath](./kibana-plugin-public.httpfetchoptionswithpath.md) + +## HttpFetchOptionsWithPath interface + +Similar to [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) but with the URL path included. + +Signature: + +```typescript +export interface HttpFetchOptionsWithPath extends HttpFetchOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [path](./kibana-plugin-public.httpfetchoptionswithpath.path.md) | string | | + diff --git a/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.path.md b/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.path.md new file mode 100644 index 0000000000000..9341fd2f7693a --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpfetchoptionswithpath.path.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpFetchOptionsWithPath](./kibana-plugin-public.httpfetchoptionswithpath.md) > [path](./kibana-plugin-public.httpfetchoptionswithpath.path.md) + +## HttpFetchOptionsWithPath.path property + +Signature: + +```typescript +path: string; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httphandler.md b/docs/development/core/public/kibana-plugin-public.httphandler.md index 89458c4743cd6..42a6942eedef0 100644 --- a/docs/development/core/public/kibana-plugin-public.httphandler.md +++ b/docs/development/core/public/kibana-plugin-public.httphandler.md @@ -4,7 +4,7 @@ ## HttpHandler interface -A function for making an HTTP requests to Kibana's backend. See [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) for options and [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) for the response. +A function for making an HTTP requests to Kibana's backend. See [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) for options and [HttpResponse](./kibana-plugin-public.httpresponse.md) for the response. Signature: diff --git a/docs/development/core/public/kibana-plugin-public.httpheadersinit.md b/docs/development/core/public/kibana-plugin-public.httpheadersinit.md index 15877a55fcddc..28177909972db 100644 --- a/docs/development/core/public/kibana-plugin-public.httpheadersinit.md +++ b/docs/development/core/public/kibana-plugin-public.httpheadersinit.md @@ -4,6 +4,7 @@ ## HttpHeadersInit interface +Headers to append to the request. Any headers that begin with `kbn-` are considered private to Core and will cause [HttpHandler](./kibana-plugin-public.httphandler.md) to throw an error. Signature: diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptor.md b/docs/development/core/public/kibana-plugin-public.httpinterceptor.md index cf7288a492ebb..a00a7ab0854fb 100644 --- a/docs/development/core/public/kibana-plugin-public.httpinterceptor.md +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptor.md @@ -16,7 +16,7 @@ export interface HttpInterceptor | Method | Description | | --- | --- | -| [request(request, controller)](./kibana-plugin-public.httpinterceptor.request.md) | Define an interceptor to be executed before a request is sent. | +| [request(fetchOptions, controller)](./kibana-plugin-public.httpinterceptor.request.md) | Define an interceptor to be executed before a request is sent. | | [requestError(httpErrorRequest, controller)](./kibana-plugin-public.httpinterceptor.requesterror.md) | Define an interceptor to be executed if a request interceptor throws an error or returns a rejected Promise. | | [response(httpResponse, controller)](./kibana-plugin-public.httpinterceptor.response.md) | Define an interceptor to be executed after a response is received. | | [responseError(httpErrorResponse, controller)](./kibana-plugin-public.httpinterceptor.responseerror.md) | Define an interceptor to be executed if a response interceptor throws an error or returns a rejected Promise. | diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptor.request.md b/docs/development/core/public/kibana-plugin-public.httpinterceptor.request.md index 4254e4aa8950c..d1d559916b36d 100644 --- a/docs/development/core/public/kibana-plugin-public.httpinterceptor.request.md +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptor.request.md @@ -9,17 +9,17 @@ Define an interceptor to be executed before a request is sent. Signature: ```typescript -request?(request: Request, controller: IHttpInterceptController): Promise | Request | void; +request?(fetchOptions: Readonly, controller: IHttpInterceptController): MaybePromise> | void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| request | Request | | +| fetchOptions | Readonly<HttpFetchOptionsWithPath> | | | controller | IHttpInterceptController | | Returns: -`Promise | Request | void` +`MaybePromise> | void` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptor.requesterror.md b/docs/development/core/public/kibana-plugin-public.httpinterceptor.requesterror.md index af9b8641e7473..fc661d88bf1af 100644 --- a/docs/development/core/public/kibana-plugin-public.httpinterceptor.requesterror.md +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptor.requesterror.md @@ -9,17 +9,17 @@ Define an interceptor to be executed if a request interceptor throws an error or Signature: ```typescript -requestError?(httpErrorRequest: HttpErrorRequest, controller: IHttpInterceptController): Promise | Request | void; +requestError?(httpErrorRequest: HttpInterceptorRequestError, controller: IHttpInterceptController): MaybePromise> | void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| httpErrorRequest | HttpErrorRequest | | +| httpErrorRequest | HttpInterceptorRequestError | | | controller | IHttpInterceptController | | Returns: -`Promise | Request | void` +`MaybePromise> | void` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptor.response.md b/docs/development/core/public/kibana-plugin-public.httpinterceptor.response.md index 3a67dcbad3119..95cf78dd6f8d1 100644 --- a/docs/development/core/public/kibana-plugin-public.httpinterceptor.response.md +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptor.response.md @@ -9,17 +9,17 @@ Define an interceptor to be executed after a response is received. Signature: ```typescript -response?(httpResponse: IHttpResponse, controller: IHttpInterceptController): Promise | IHttpResponseInterceptorOverrides | void; +response?(httpResponse: HttpResponse, controller: IHttpInterceptController): MaybePromise | void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| httpResponse | IHttpResponse | | +| httpResponse | HttpResponse | | | controller | IHttpInterceptController | | Returns: -`Promise | IHttpResponseInterceptorOverrides | void` +`MaybePromise | void` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptor.responseerror.md b/docs/development/core/public/kibana-plugin-public.httpinterceptor.responseerror.md index 476ceba649d40..50e943bc93b07 100644 --- a/docs/development/core/public/kibana-plugin-public.httpinterceptor.responseerror.md +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptor.responseerror.md @@ -9,17 +9,17 @@ Define an interceptor to be executed if a response interceptor throws an error o Signature: ```typescript -responseError?(httpErrorResponse: HttpErrorResponse, controller: IHttpInterceptController): Promise | IHttpResponseInterceptorOverrides | void; +responseError?(httpErrorResponse: HttpInterceptorResponseError, controller: IHttpInterceptController): MaybePromise | void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| httpErrorResponse | HttpErrorResponse | | +| httpErrorResponse | HttpInterceptorResponseError | | | controller | IHttpInterceptController | | Returns: -`Promise | IHttpResponseInterceptorOverrides | void` +`MaybePromise | void` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.error.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.error.md new file mode 100644 index 0000000000000..28fde834d9721 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorRequestError](./kibana-plugin-public.httpinterceptorrequesterror.md) > [error](./kibana-plugin-public.httpinterceptorrequesterror.error.md) + +## HttpInterceptorRequestError.error property + +Signature: + +```typescript +error: Error; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.fetchoptions.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.fetchoptions.md new file mode 100644 index 0000000000000..79c086224ff10 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.fetchoptions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorRequestError](./kibana-plugin-public.httpinterceptorrequesterror.md) > [fetchOptions](./kibana-plugin-public.httpinterceptorrequesterror.fetchoptions.md) + +## HttpInterceptorRequestError.fetchOptions property + +Signature: + +```typescript +fetchOptions: Readonly; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.md new file mode 100644 index 0000000000000..4375719e0802b --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorrequesterror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorRequestError](./kibana-plugin-public.httpinterceptorrequesterror.md) + +## HttpInterceptorRequestError interface + + +Signature: + +```typescript +export interface HttpInterceptorRequestError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [error](./kibana-plugin-public.httpinterceptorrequesterror.error.md) | Error | | +| [fetchOptions](./kibana-plugin-public.httpinterceptorrequesterror.fetchoptions.md) | Readonly<HttpFetchOptionsWithPath> | | + diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.error.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.error.md new file mode 100644 index 0000000000000..a9bdf41b36868 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorResponseError](./kibana-plugin-public.httpinterceptorresponseerror.md) > [error](./kibana-plugin-public.httpinterceptorresponseerror.error.md) + +## HttpInterceptorResponseError.error property + +Signature: + +```typescript +error: Error | IHttpFetchError; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.md new file mode 100644 index 0000000000000..49b4b2545a5f3 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorResponseError](./kibana-plugin-public.httpinterceptorresponseerror.md) + +## HttpInterceptorResponseError interface + + +Signature: + +```typescript +export interface HttpInterceptorResponseError extends HttpResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [error](./kibana-plugin-public.httpinterceptorresponseerror.error.md) | Error | IHttpFetchError | | +| [request](./kibana-plugin-public.httpinterceptorresponseerror.request.md) | Readonly<Request> | | + diff --git a/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.request.md b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.request.md new file mode 100644 index 0000000000000..cb6252ceb8e41 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpinterceptorresponseerror.request.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpInterceptorResponseError](./kibana-plugin-public.httpinterceptorresponseerror.md) > [request](./kibana-plugin-public.httpinterceptorresponseerror.request.md) + +## HttpInterceptorResponseError.request property + +Signature: + +```typescript +request: Readonly; +``` diff --git a/docs/development/core/public/kibana-plugin-public.ihttpresponse.body.md b/docs/development/core/public/kibana-plugin-public.httpresponse.body.md similarity index 61% rename from docs/development/core/public/kibana-plugin-public.ihttpresponse.body.md rename to docs/development/core/public/kibana-plugin-public.httpresponse.body.md index 2f8710ccdc60e..3eb167afaa40e 100644 --- a/docs/development/core/public/kibana-plugin-public.ihttpresponse.body.md +++ b/docs/development/core/public/kibana-plugin-public.httpresponse.body.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) > [body](./kibana-plugin-public.ihttpresponse.body.md) +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpResponse](./kibana-plugin-public.httpresponse.md) > [body](./kibana-plugin-public.httpresponse.body.md) -## IHttpResponse.body property +## HttpResponse.body property Parsed body received, may be undefined if there was an error. diff --git a/docs/development/core/public/kibana-plugin-public.httpresponse.fetchoptions.md b/docs/development/core/public/kibana-plugin-public.httpresponse.fetchoptions.md new file mode 100644 index 0000000000000..65974efe8494e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpresponse.fetchoptions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpResponse](./kibana-plugin-public.httpresponse.md) > [fetchOptions](./kibana-plugin-public.httpresponse.fetchoptions.md) + +## HttpResponse.fetchOptions property + +The original [HttpFetchOptionsWithPath](./kibana-plugin-public.httpfetchoptionswithpath.md) used to send this request. + +Signature: + +```typescript +readonly fetchOptions: Readonly; +``` diff --git a/docs/development/core/public/kibana-plugin-public.httpresponse.md b/docs/development/core/public/kibana-plugin-public.httpresponse.md new file mode 100644 index 0000000000000..74097533f6090 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.httpresponse.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpResponse](./kibana-plugin-public.httpresponse.md) + +## HttpResponse interface + + +Signature: + +```typescript +export interface HttpResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [body](./kibana-plugin-public.httpresponse.body.md) | TResponseBody | Parsed body received, may be undefined if there was an error. | +| [fetchOptions](./kibana-plugin-public.httpresponse.fetchoptions.md) | Readonly<HttpFetchOptionsWithPath> | The original [HttpFetchOptionsWithPath](./kibana-plugin-public.httpfetchoptionswithpath.md) used to send this request. | +| [request](./kibana-plugin-public.httpresponse.request.md) | Readonly<Request> | Raw request sent to Kibana server. | +| [response](./kibana-plugin-public.httpresponse.response.md) | Readonly<Response> | Raw response received, may be undefined if there was an error. | + diff --git a/docs/development/core/public/kibana-plugin-public.ihttpresponse.request.md b/docs/development/core/public/kibana-plugin-public.httpresponse.request.md similarity index 58% rename from docs/development/core/public/kibana-plugin-public.ihttpresponse.request.md rename to docs/development/core/public/kibana-plugin-public.httpresponse.request.md index 12e5405eb5ed4..c2a483033247d 100644 --- a/docs/development/core/public/kibana-plugin-public.ihttpresponse.request.md +++ b/docs/development/core/public/kibana-plugin-public.httpresponse.request.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) > [request](./kibana-plugin-public.ihttpresponse.request.md) +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpResponse](./kibana-plugin-public.httpresponse.md) > [request](./kibana-plugin-public.httpresponse.request.md) -## IHttpResponse.request property +## HttpResponse.request property Raw request sent to Kibana server. diff --git a/docs/development/core/public/kibana-plugin-public.ihttpresponse.response.md b/docs/development/core/public/kibana-plugin-public.httpresponse.response.md similarity index 60% rename from docs/development/core/public/kibana-plugin-public.ihttpresponse.response.md rename to docs/development/core/public/kibana-plugin-public.httpresponse.response.md index 9d0b4b59a638d..3a58a3f35012f 100644 --- a/docs/development/core/public/kibana-plugin-public.ihttpresponse.response.md +++ b/docs/development/core/public/kibana-plugin-public.httpresponse.response.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) > [response](./kibana-plugin-public.ihttpresponse.response.md) +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [HttpResponse](./kibana-plugin-public.httpresponse.md) > [response](./kibana-plugin-public.httpresponse.response.md) -## IHttpResponse.response property +## HttpResponse.response property Raw response received, may be undefined if there was an error. diff --git a/docs/development/core/public/kibana-plugin-public.ihttpresponse.md b/docs/development/core/public/kibana-plugin-public.ihttpresponse.md deleted file mode 100644 index 5ddce0ba2d0f1..0000000000000 --- a/docs/development/core/public/kibana-plugin-public.ihttpresponse.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) - -## IHttpResponse interface - - -Signature: - -```typescript -export interface IHttpResponse -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [body](./kibana-plugin-public.ihttpresponse.body.md) | TResponseBody | Parsed body received, may be undefined if there was an error. | -| [request](./kibana-plugin-public.ihttpresponse.request.md) | Readonly<Request> | Raw request sent to Kibana server. | -| [response](./kibana-plugin-public.ihttpresponse.response.md) | Readonly<Response> | Raw response received, may be undefined if there was an error. | - diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md index ad23bfce4f78c..de6726b34dfab 100644 --- a/docs/development/core/public/kibana-plugin-public.md +++ b/docs/development/core/public/kibana-plugin-public.md @@ -59,14 +59,16 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ErrorToastOptions](./kibana-plugin-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-public.itoasts.md) APIs. | | [FatalErrorInfo](./kibana-plugin-public.fatalerrorinfo.md) | Represents the message and stack of a fatal Error | | [FatalErrorsSetup](./kibana-plugin-public.fatalerrorssetup.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | -| [HttpErrorRequest](./kibana-plugin-public.httperrorrequest.md) | | -| [HttpErrorResponse](./kibana-plugin-public.httperrorresponse.md) | | | [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) | All options that may be used with a [HttpHandler](./kibana-plugin-public.httphandler.md). | +| [HttpFetchOptionsWithPath](./kibana-plugin-public.httpfetchoptionswithpath.md) | Similar to [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) but with the URL path included. | | [HttpFetchQuery](./kibana-plugin-public.httpfetchquery.md) | | -| [HttpHandler](./kibana-plugin-public.httphandler.md) | A function for making an HTTP requests to Kibana's backend. See [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) for options and [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) for the response. | -| [HttpHeadersInit](./kibana-plugin-public.httpheadersinit.md) | | +| [HttpHandler](./kibana-plugin-public.httphandler.md) | A function for making an HTTP requests to Kibana's backend. See [HttpFetchOptions](./kibana-plugin-public.httpfetchoptions.md) for options and [HttpResponse](./kibana-plugin-public.httpresponse.md) for the response. | +| [HttpHeadersInit](./kibana-plugin-public.httpheadersinit.md) | Headers to append to the request. Any headers that begin with kbn- are considered private to Core and will cause [HttpHandler](./kibana-plugin-public.httphandler.md) to throw an error. | | [HttpInterceptor](./kibana-plugin-public.httpinterceptor.md) | An object that may define global interceptor functions for different parts of the request and response lifecycle. See [IHttpInterceptController](./kibana-plugin-public.ihttpinterceptcontroller.md). | +| [HttpInterceptorRequestError](./kibana-plugin-public.httpinterceptorrequesterror.md) | | +| [HttpInterceptorResponseError](./kibana-plugin-public.httpinterceptorresponseerror.md) | | | [HttpRequestInit](./kibana-plugin-public.httprequestinit.md) | Fetch API options available to [HttpHandler](./kibana-plugin-public.httphandler.md)s. | +| [HttpResponse](./kibana-plugin-public.httpresponse.md) | | | [HttpSetup](./kibana-plugin-public.httpsetup.md) | | | [I18nStart](./kibana-plugin-public.i18nstart.md) | I18nStart.Context is required by any localizable React component from @kbn/i18n and @elastic/eui packages and is supposed to be used as the topmost component for any i18n-compatible React tree. | | [IAnonymousPaths](./kibana-plugin-public.ianonymouspaths.md) | APIs for denoting paths as not requiring authentication | @@ -74,7 +76,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [IContextContainer](./kibana-plugin-public.icontextcontainer.md) | An object that handles registration of context providers and configuring handlers with context. | | [IHttpFetchError](./kibana-plugin-public.ihttpfetcherror.md) | | | [IHttpInterceptController](./kibana-plugin-public.ihttpinterceptcontroller.md) | Used to halt a request Promise chain in a [HttpInterceptor](./kibana-plugin-public.httpinterceptor.md). | -| [IHttpResponse](./kibana-plugin-public.ihttpresponse.md) | | | [IHttpResponseInterceptorOverrides](./kibana-plugin-public.ihttpresponseinterceptoroverrides.md) | Properties that can be returned by HttpInterceptor.request to override the response. | | [ImageValidation](./kibana-plugin-public.imagevalidation.md) | | | [IUiSettingsClient](./kibana-plugin-public.iuisettingsclient.md) | Client-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. [IUiSettingsClient](./kibana-plugin-public.iuisettingsclient.md) | diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md b/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md index b05d28899f9d2..589529cf2a7f7 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md @@ -1,17 +1,17 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) > [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md) - -## CoreSetup.getStartServices() method - -Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle. - -Signature: - -```typescript -getStartServices(): Promise<[CoreStart, TPluginsStart]>; -``` -Returns: - -`Promise<[CoreStart, TPluginsStart]>` - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) > [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md) + +## CoreSetup.getStartServices() method + +Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle. + +Signature: + +```typescript +getStartServices(): Promise<[CoreStart, TPluginsStart]>; +``` +Returns: + +`Promise<[CoreStart, TPluginsStart]>` + diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md index c36d649837e8a..325f7216122b5 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.md @@ -1,32 +1,32 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) - -## CoreSetup interface - -Context passed to the plugins `setup` method. - -Signature: - -```typescript -export interface CoreSetup -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | CapabilitiesSetup | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | -| [context](./kibana-plugin-server.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-server.contextsetup.md) | -| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | -| [http](./kibana-plugin-server.coresetup.http.md) | HttpServiceSetup | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | -| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) | -| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | UiSettingsServiceSetup | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) | -| [uuid](./kibana-plugin-server.coresetup.uuid.md) | UuidServiceSetup | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) | - -## Methods - -| Method | Description | -| --- | --- | -| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start. This should only be used inside handlers registered during setup that will only be executed after start lifecycle. | - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) + +## CoreSetup interface + +Context passed to the plugins `setup` method. + +Signature: + +```typescript +export interface CoreSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | CapabilitiesSetup | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | +| [context](./kibana-plugin-server.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-server.contextsetup.md) | +| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | +| [http](./kibana-plugin-server.coresetup.http.md) | HttpServiceSetup | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | +| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) | +| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | UiSettingsServiceSetup | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) | +| [uuid](./kibana-plugin-server.coresetup.uuid.md) | UuidServiceSetup | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) | + +## Methods + +| Method | Description | +| --- | --- | +| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start. This should only be used inside handlers registered during setup that will only be executed after start lifecycle. | + diff --git a/docs/development/core/server/kibana-plugin-server.getauthstate.md b/docs/development/core/server/kibana-plugin-server.getauthstate.md index 47fc38c28f5e0..1980a81ec1910 100644 --- a/docs/development/core/server/kibana-plugin-server.getauthstate.md +++ b/docs/development/core/server/kibana-plugin-server.getauthstate.md @@ -4,13 +4,13 @@ ## GetAuthState type -Get authentication state for a request. Returned by `auth` interceptor. +Gets authentication state for a request. Returned by `auth` interceptor. Signature: ```typescript -export declare type GetAuthState = (request: KibanaRequest | LegacyRequest) => { +export declare type GetAuthState = (request: KibanaRequest | LegacyRequest) => { status: AuthStatus; - state: unknown; + state: T; }; ``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.auth.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.auth.md new file mode 100644 index 0000000000000..f08bb8b638e79 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.auth.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) > [auth](./kibana-plugin-server.httpservicesetup.auth.md) + +## HttpServiceSetup.auth property + +Signature: + +```typescript +auth: { + get: GetAuthState; + isAuthenticated: IsAuthenticated; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index 3b1993841339d..95a95175672c7 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -81,6 +81,7 @@ async (context, request, response) => { | Property | Type | Description | | --- | --- | --- | +| [auth](./kibana-plugin-server.httpservicesetup.auth.md) | {
get: GetAuthState;
isAuthenticated: IsAuthenticated;
} | | | [basePath](./kibana-plugin-server.httpservicesetup.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-server.ibasepath.md). | | [createCookieSessionStorageFactory](./kibana-plugin-server.httpservicesetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | | [createRouter](./kibana-plugin-server.httpservicesetup.createrouter.md) | () => IRouter | Provides ability to declare a handler function for a particular path and HTTP request method. | diff --git a/docs/development/core/server/kibana-plugin-server.isauthenticated.md b/docs/development/core/server/kibana-plugin-server.isauthenticated.md index 15f412710412a..c927b6bea926c 100644 --- a/docs/development/core/server/kibana-plugin-server.isauthenticated.md +++ b/docs/development/core/server/kibana-plugin-server.isauthenticated.md @@ -4,7 +4,7 @@ ## IsAuthenticated type -Return authentication status for a request. +Returns authentication status for a request. Signature: diff --git a/docs/development/core/server/kibana-plugin-server.kibanarequest.issystemrequest.md b/docs/development/core/server/kibana-plugin-server.kibanarequest.issystemrequest.md new file mode 100644 index 0000000000000..f6178d6eee56e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.kibanarequest.issystemrequest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [KibanaRequest](./kibana-plugin-server.kibanarequest.md) > [isSystemRequest](./kibana-plugin-server.kibanarequest.issystemrequest.md) + +## KibanaRequest.isSystemRequest property + +Whether or not the request is a "system request" rather than an application-level request. Can be set on the client using the `HttpFetchOptions#asSystemRequest` option. + +Signature: + +```typescript +readonly isSystemRequest: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.kibanarequest.md b/docs/development/core/server/kibana-plugin-server.kibanarequest.md index 6603de24494d5..bd02c4b9bc155 100644 --- a/docs/development/core/server/kibana-plugin-server.kibanarequest.md +++ b/docs/development/core/server/kibana-plugin-server.kibanarequest.md @@ -25,6 +25,7 @@ export declare class KibanaRequestBody | | | [events](./kibana-plugin-server.kibanarequest.events.md) | | KibanaRequestEvents | Request events [KibanaRequestEvents](./kibana-plugin-server.kibanarequestevents.md) | | [headers](./kibana-plugin-server.kibanarequest.headers.md) | | Headers | Readonly copy of incoming request headers. | +| [isSystemRequest](./kibana-plugin-server.kibanarequest.issystemrequest.md) | | boolean | Whether or not the request is a "system request" rather than an application-level request. Can be set on the client using the HttpFetchOptions#asSystemRequest option. | | [params](./kibana-plugin-server.kibanarequest.params.md) | | Params | | | [query](./kibana-plugin-server.kibanarequest.query.md) | | Query | | | [route](./kibana-plugin-server.kibanarequest.route.md) | | RecursiveReadonly<KibanaRequestRoute<Method>> | matched route details | diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md new file mode 100644 index 0000000000000..fbce46c3f4ad9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.md @@ -0,0 +1,228 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) + +## kibana-plugin-server package + +The Kibana Core APIs for server-side plugins. + +A plugin requires a `kibana.json` file at it's root directory that follows [the manfiest schema](./kibana-plugin-server.pluginmanifest.md) to define static plugin information required to load the plugin. + +A plugin's `server/index` file must contain a named import, `plugin`, that implements [PluginInitializer](./kibana-plugin-server.plugininitializer.md) which returns an object that implements [Plugin](./kibana-plugin-server.plugin.md). + +The plugin integrates with the core system via lifecycle events: `setup`, `start`, and `stop`. In each lifecycle method, the plugin will receive the corresponding core services available (either [CoreSetup](./kibana-plugin-server.coresetup.md) or [CoreStart](./kibana-plugin-server.corestart.md)) and any interfaces returned by dependency plugins' lifecycle method. Anything returned by the plugin's lifecycle method will be exposed to downstream dependencies when their corresponding lifecycle methods are invoked. + +## Classes + +| Class | Description | +| --- | --- | +| [BasePath](./kibana-plugin-server.basepath.md) | Access or manipulate the Kibana base path | +| [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | +| [CspConfig](./kibana-plugin-server.cspconfig.md) | CSP configuration for use in Kibana. | +| [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as body.error.header[WWW-Authenticate] | +| [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. | +| [RouteValidationError](./kibana-plugin-server.routevalidationerror.md) | Error to return when the validation is not successful. | +| [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) | | +| [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | | +| [SavedObjectsRepository](./kibana-plugin-server.savedobjectsrepository.md) | | +| [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md) | Serves the same purpose as "normal" ClusterClient but exposes additional callAsCurrentUser method that doesn't use credentials of the Kibana internal user (as callAsInternalUser does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API.See [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md). | + +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [AuthResultType](./kibana-plugin-server.authresulttype.md) | | +| [AuthStatus](./kibana-plugin-server.authstatus.md) | Status indicating an outcome of the authentication. | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [APICaller](./kibana-plugin-server.apicaller.md) | | +| [AssistanceAPIResponse](./kibana-plugin-server.assistanceapiresponse.md) | | +| [AssistantAPIClientParams](./kibana-plugin-server.assistantapiclientparams.md) | | +| [Authenticated](./kibana-plugin-server.authenticated.md) | | +| [AuthResultParams](./kibana-plugin-server.authresultparams.md) | Result of an incoming request authentication. | +| [AuthToolkit](./kibana-plugin-server.authtoolkit.md) | A tool set defining an outcome of Auth interceptor for incoming request. | +| [CallAPIOptions](./kibana-plugin-server.callapioptions.md) | The set of options that defines how API call should be made and result be processed. | +| [Capabilities](./kibana-plugin-server.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | +| [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | APIs to manage the [Capabilities](./kibana-plugin-server.capabilities.md) that will be used by the application.Plugins relying on capabilities to toggle some of their features should register them during the setup phase using the registerProvider method.Plugins having the responsibility to restrict capabilities depending on a given context should register their capabilities switcher using the registerSwitcher method.Refers to the methods documentation for complete description and examples. | +| [CapabilitiesStart](./kibana-plugin-server.capabilitiesstart.md) | APIs to access the application [Capabilities](./kibana-plugin-server.capabilities.md). | +| [ConfigDeprecationFactory](./kibana-plugin-server.configdeprecationfactory.md) | Provides helpers to generates the most commonly used [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md) when invoking a [ConfigDeprecationProvider](./kibana-plugin-server.configdeprecationprovider.md).See methods documentation for more detailed examples. | +| [ContextSetup](./kibana-plugin-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | +| [CoreSetup](./kibana-plugin-server.coresetup.md) | Context passed to the plugins setup method. | +| [CoreStart](./kibana-plugin-server.corestart.md) | Context passed to the plugins start method. | +| [CustomHttpResponseOptions](./kibana-plugin-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. | +| [DeprecationAPIClientParams](./kibana-plugin-server.deprecationapiclientparams.md) | | +| [DeprecationAPIResponse](./kibana-plugin-server.deprecationapiresponse.md) | | +| [DeprecationInfo](./kibana-plugin-server.deprecationinfo.md) | | +| [DeprecationSettings](./kibana-plugin-server.deprecationsettings.md) | UiSettings deprecation field options. | +| [DiscoveredPlugin](./kibana-plugin-server.discoveredplugin.md) | Small container object used to expose information about discovered plugins that may or may not have been started. | +| [ElasticsearchError](./kibana-plugin-server.elasticsearcherror.md) | | +| [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | | +| [EnvironmentMode](./kibana-plugin-server.environmentmode.md) | | +| [ErrorHttpResponseOptions](./kibana-plugin-server.errorhttpresponseoptions.md) | HTTP response parameters | +| [FakeRequest](./kibana-plugin-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | +| [HttpResponseOptions](./kibana-plugin-server.httpresponseoptions.md) | HTTP response parameters | +| [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to hapi server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. | +| [HttpServiceStart](./kibana-plugin-server.httpservicestart.md) | | +| [IContextContainer](./kibana-plugin-server.icontextcontainer.md) | An object that handles registration of context providers and configuring handlers with context. | +| [ICspConfig](./kibana-plugin-server.icspconfig.md) | CSP configuration for use in Kibana. | +| [IKibanaResponse](./kibana-plugin-server.ikibanaresponse.md) | A response data object, expected to returned as a result of [RequestHandler](./kibana-plugin-server.requesthandler.md) execution | +| [IKibanaSocket](./kibana-plugin-server.ikibanasocket.md) | A tiny abstraction for TCP socket. | +| [ImageValidation](./kibana-plugin-server.imagevalidation.md) | | +| [IndexSettingsDeprecationInfo](./kibana-plugin-server.indexsettingsdeprecationinfo.md) | | +| [IRenderOptions](./kibana-plugin-server.irenderoptions.md) | | +| [IRouter](./kibana-plugin-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-server.routeconfig.md) and [RequestHandler](./kibana-plugin-server.requesthandler.md) for more information about arguments to route registrations. | +| [IScopedRenderingClient](./kibana-plugin-server.iscopedrenderingclient.md) | | +| [IUiSettingsClient](./kibana-plugin-server.iuisettingsclient.md) | Server-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. | +| [KibanaRequestEvents](./kibana-plugin-server.kibanarequestevents.md) | Request events. | +| [KibanaRequestRoute](./kibana-plugin-server.kibanarequestroute.md) | Request specific route information exposed to a handler. | +| [LegacyRequest](./kibana-plugin-server.legacyrequest.md) | | +| [LegacyServiceSetupDeps](./kibana-plugin-server.legacyservicesetupdeps.md) | | +| [LegacyServiceStartDeps](./kibana-plugin-server.legacyservicestartdeps.md) | | +| [Logger](./kibana-plugin-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. | +| [LoggerFactory](./kibana-plugin-server.loggerfactory.md) | The single purpose of LoggerFactory interface is to define a way to retrieve a context-based logger instance. | +| [LogMeta](./kibana-plugin-server.logmeta.md) | Contextual metadata | +| [OnPostAuthToolkit](./kibana-plugin-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. | +| [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. | +| [OnPreResponseExtensions](./kibana-plugin-server.onpreresponseextensions.md) | Additional data to extend a response. | +| [OnPreResponseInfo](./kibana-plugin-server.onpreresponseinfo.md) | Response status code. | +| [OnPreResponseToolkit](./kibana-plugin-server.onpreresponsetoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. | +| [PackageInfo](./kibana-plugin-server.packageinfo.md) | | +| [Plugin](./kibana-plugin-server.plugin.md) | The interface that should be returned by a PluginInitializer. | +| [PluginConfigDescriptor](./kibana-plugin-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. | +| [PluginInitializerContext](./kibana-plugin-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. | +| [PluginManifest](./kibana-plugin-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. | +| [PluginsServiceSetup](./kibana-plugin-server.pluginsservicesetup.md) | | +| [PluginsServiceStart](./kibana-plugin-server.pluginsservicestart.md) | | +| [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients: - [rendering](./kibana-plugin-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | +| [RouteConfig](./kibana-plugin-server.routeconfig.md) | Route specific configuration. | +| [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Additional route options. | +| [RouteConfigOptionsBody](./kibana-plugin-server.routeconfigoptionsbody.md) | Additional body options for a route | +| [RouteValidationResultFactory](./kibana-plugin-server.routevalidationresultfactory.md) | Validation result factory to be used in the custom validation function to return the valid data or validation errorsSee [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md). | +| [RouteValidatorConfig](./kibana-plugin-server.routevalidatorconfig.md) | The configuration object to the RouteValidator class. Set params, query and/or body to specify the validation logic to follow for that property. | +| [RouteValidatorOptions](./kibana-plugin-server.routevalidatoroptions.md) | Additional options for the RouteValidator class to modify its default behaviour. | +| [SavedObject](./kibana-plugin-server.savedobject.md) | | +| [SavedObjectAttributes](./kibana-plugin-server.savedobjectattributes.md) | The data for a Saved Object is stored as an object in the attributes property. | +| [SavedObjectReference](./kibana-plugin-server.savedobjectreference.md) | A reference to another saved object. | +| [SavedObjectsBaseOptions](./kibana-plugin-server.savedobjectsbaseoptions.md) | | +| [SavedObjectsBulkCreateObject](./kibana-plugin-server.savedobjectsbulkcreateobject.md) | | +| [SavedObjectsBulkGetObject](./kibana-plugin-server.savedobjectsbulkgetobject.md) | | +| [SavedObjectsBulkResponse](./kibana-plugin-server.savedobjectsbulkresponse.md) | | +| [SavedObjectsBulkUpdateObject](./kibana-plugin-server.savedobjectsbulkupdateobject.md) | | +| [SavedObjectsBulkUpdateOptions](./kibana-plugin-server.savedobjectsbulkupdateoptions.md) | | +| [SavedObjectsBulkUpdateResponse](./kibana-plugin-server.savedobjectsbulkupdateresponse.md) | | +| [SavedObjectsClientProviderOptions](./kibana-plugin-server.savedobjectsclientprovideroptions.md) | Options to control the creation of the Saved Objects Client. | +| [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) | Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. | +| [SavedObjectsCreateOptions](./kibana-plugin-server.savedobjectscreateoptions.md) | | +| [SavedObjectsDeleteByNamespaceOptions](./kibana-plugin-server.savedobjectsdeletebynamespaceoptions.md) | | +| [SavedObjectsDeleteOptions](./kibana-plugin-server.savedobjectsdeleteoptions.md) | | +| [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) | Options controlling the export operation. | +| [SavedObjectsExportResultDetails](./kibana-plugin-server.savedobjectsexportresultdetails.md) | Structure of the export result details entry | +| [SavedObjectsFindOptions](./kibana-plugin-server.savedobjectsfindoptions.md) | | +| [SavedObjectsFindResponse](./kibana-plugin-server.savedobjectsfindresponse.md) | Return type of the Saved Objects find() method.\*Note\*: this type is different between the Public and Server Saved Objects clients. | +| [SavedObjectsImportConflictError](./kibana-plugin-server.savedobjectsimportconflicterror.md) | Represents a failure to import due to a conflict. | +| [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) | Represents a failure to import. | +| [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) | Represents a failure to import due to missing references. | +| [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) | Options to control the import operation. | +| [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) | The response describing the result of an import. | +| [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) | Describes a retry operation for importing a saved object. | +| [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) | Represents a failure to import due to an unknown reason. | +| [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md) | Represents a failure to import due to having an unsupported saved object type. | +| [SavedObjectsIncrementCounterOptions](./kibana-plugin-server.savedobjectsincrementcounteroptions.md) | | +| [SavedObjectsMigrationLogger](./kibana-plugin-server.savedobjectsmigrationlogger.md) | | +| [SavedObjectsMigrationVersion](./kibana-plugin-server.savedobjectsmigrationversion.md) | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | +| [SavedObjectsRawDoc](./kibana-plugin-server.savedobjectsrawdoc.md) | A raw document as represented directly in the saved object index. | +| [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) | Factory provided when invoking a [client factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) See [SavedObjectsServiceSetup.setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) | +| [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) | Options to control the "resolve import" operation. | +| [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) | Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for creating and registering Saved Object client wrappers. | +| [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. | +| [SavedObjectsUpdateOptions](./kibana-plugin-server.savedobjectsupdateoptions.md) | | +| [SavedObjectsUpdateResponse](./kibana-plugin-server.savedobjectsupdateresponse.md) | | +| [SessionCookieValidationResult](./kibana-plugin-server.sessioncookievalidationresult.md) | Return type from a function to validate cookie contents. | +| [SessionStorage](./kibana-plugin-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. | +| [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. | +| [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request | +| [StringValidationRegex](./kibana-plugin-server.stringvalidationregex.md) | StringValidation with regex object | +| [StringValidationRegexString](./kibana-plugin-server.stringvalidationregexstring.md) | StringValidation as regex string | +| [UiSettingsParams](./kibana-plugin-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. | +| [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) | | +| [UiSettingsServiceStart](./kibana-plugin-server.uisettingsservicestart.md) | | +| [UserProvidedValues](./kibana-plugin-server.userprovidedvalues.md) | Describes the values explicitly set by user. | +| [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) | APIs to access the application's instance uuid. | + +## Variables + +| Variable | Description | +| --- | --- | +| [kibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) | Set of helpers used to create KibanaResponse to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-server.requesthandler.md) execution. | +| [validBodyOutput](./kibana-plugin-server.validbodyoutput.md) | The set of valid body.output | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [AuthenticationHandler](./kibana-plugin-server.authenticationhandler.md) | See [AuthToolkit](./kibana-plugin-server.authtoolkit.md). | +| [AuthHeaders](./kibana-plugin-server.authheaders.md) | Auth Headers map | +| [AuthResult](./kibana-plugin-server.authresult.md) | | +| [CapabilitiesProvider](./kibana-plugin-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | +| [CapabilitiesSwitcher](./kibana-plugin-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | +| [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md) | Configuration deprecation returned from [ConfigDeprecationProvider](./kibana-plugin-server.configdeprecationprovider.md) that handles a single deprecation from the configuration. | +| [ConfigDeprecationLogger](./kibana-plugin-server.configdeprecationlogger.md) | Logger interface used when invoking a [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md) | +| [ConfigDeprecationProvider](./kibana-plugin-server.configdeprecationprovider.md) | A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md).See [ConfigDeprecationFactory](./kibana-plugin-server.configdeprecationfactory.md) for more usage examples. | +| [ConfigPath](./kibana-plugin-server.configpath.md) | | +| [ElasticsearchClientConfig](./kibana-plugin-server.elasticsearchclientconfig.md) | | +| [GetAuthHeaders](./kibana-plugin-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. | +| [GetAuthState](./kibana-plugin-server.getauthstate.md) | Gets authentication state for a request. Returned by auth interceptor. | +| [HandlerContextType](./kibana-plugin-server.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-server.handlerfunction.md) to represent the type of the context. | +| [HandlerFunction](./kibana-plugin-server.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-server.icontextcontainer.md) | +| [HandlerParameters](./kibana-plugin-server.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-server.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-server.handlercontexttype.md). | +| [Headers](./kibana-plugin-server.headers.md) | Http request headers to read. | +| [HttpResponsePayload](./kibana-plugin-server.httpresponsepayload.md) | Data send to the client as a response payload. | +| [IBasePath](./kibana-plugin-server.ibasepath.md) | Access or manipulate the Kibana base path[BasePath](./kibana-plugin-server.basepath.md) | +| [IClusterClient](./kibana-plugin-server.iclusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | +| [IContextProvider](./kibana-plugin-server.icontextprovider.md) | A function that returns a context value for a specific key of given context type. | +| [ICustomClusterClient](./kibana-plugin-server.icustomclusterclient.md) | Represents an Elasticsearch cluster API client created by a plugin. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | +| [IsAuthenticated](./kibana-plugin-server.isauthenticated.md) | Returns authentication status for a request. | +| [ISavedObjectsRepository](./kibana-plugin-server.isavedobjectsrepository.md) | See [SavedObjectsRepository](./kibana-plugin-server.savedobjectsrepository.md) | +| [IScopedClusterClient](./kibana-plugin-server.iscopedclusterclient.md) | Serves the same purpose as "normal" ClusterClient but exposes additional callAsCurrentUser method that doesn't use credentials of the Kibana internal user (as callAsInternalUser does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API.See [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md). | +| [KibanaRequestRouteOptions](./kibana-plugin-server.kibanarequestrouteoptions.md) | Route options: If 'GET' or 'OPTIONS' method, body options won't be returned. | +| [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) | Creates an object containing request response payload, HTTP headers, error details, and other data transmitted to the client. | +| [KnownHeaders](./kibana-plugin-server.knownheaders.md) | Set of well-known HTTP headers. | +| [LifecycleResponseFactory](./kibana-plugin-server.lifecycleresponsefactory.md) | Creates an object containing redirection or error response with error details, HTTP headers, and other data transmitted to the client. | +| [MIGRATION\_ASSISTANCE\_INDEX\_ACTION](./kibana-plugin-server.migration_assistance_index_action.md) | | +| [MIGRATION\_DEPRECATION\_LEVEL](./kibana-plugin-server.migration_deprecation_level.md) | | +| [MutatingOperationRefreshSetting](./kibana-plugin-server.mutatingoperationrefreshsetting.md) | Elasticsearch Refresh setting for mutating operation | +| [OnPostAuthHandler](./kibana-plugin-server.onpostauthhandler.md) | See [OnPostAuthToolkit](./kibana-plugin-server.onpostauthtoolkit.md). | +| [OnPreAuthHandler](./kibana-plugin-server.onpreauthhandler.md) | See [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md). | +| [OnPreResponseHandler](./kibana-plugin-server.onpreresponsehandler.md) | See [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md). | +| [PluginConfigSchema](./kibana-plugin-server.pluginconfigschema.md) | Dedicated type for plugin configuration schema. | +| [PluginInitializer](./kibana-plugin-server.plugininitializer.md) | The plugin export at the root of a plugin's server directory should conform to this interface. | +| [PluginName](./kibana-plugin-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. | +| [PluginOpaqueId](./kibana-plugin-server.pluginopaqueid.md) | | +| [RecursiveReadonly](./kibana-plugin-server.recursivereadonly.md) | | +| [RedirectResponseOptions](./kibana-plugin-server.redirectresponseoptions.md) | HTTP response parameters for redirection response | +| [RequestHandler](./kibana-plugin-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) functions. | +| [RequestHandlerContextContainer](./kibana-plugin-server.requesthandlercontextcontainer.md) | An object that handles registration of http request context providers. | +| [RequestHandlerContextProvider](./kibana-plugin-server.requesthandlercontextprovider.md) | Context provider for request handler. Extends request context object with provided functionality or data. | +| [ResponseError](./kibana-plugin-server.responseerror.md) | Error message and optional data send to the client in case of error. | +| [ResponseErrorAttributes](./kibana-plugin-server.responseerrorattributes.md) | Additional data to provide error details. | +| [ResponseHeaders](./kibana-plugin-server.responseheaders.md) | Http response headers to set. | +| [RouteContentType](./kibana-plugin-server.routecontenttype.md) | The set of supported parseable Content-Types | +| [RouteMethod](./kibana-plugin-server.routemethod.md) | The set of common HTTP methods supported by Kibana routing. | +| [RouteRegistrar](./kibana-plugin-server.routeregistrar.md) | Route handler common definition | +| [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md) | The custom validation function if @kbn/config-schema is not a valid solution for your specific plugin requirements. | +| [RouteValidationSpec](./kibana-plugin-server.routevalidationspec.md) | Allowed property validation options: either @kbn/config-schema validations or custom validation functionsSee [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md) for custom validation. | +| [RouteValidatorFullConfig](./kibana-plugin-server.routevalidatorfullconfig.md) | Route validations config and options merged into one object | +| [SavedObjectAttribute](./kibana-plugin-server.savedobjectattribute.md) | Type definition for a Saved Object attribute value | +| [SavedObjectAttributeSingle](./kibana-plugin-server.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-server.savedobjectattribute.md) | +| [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing plugin state.\#\# SavedObjectsClient errorsSince the SavedObjectsClient has its hands in everything we are a little paranoid about the way we present errors back to to application code. Ideally, all errors will be either:1. Caused by bad implementation (ie. undefined is not a function) and as such unpredictable 2. An error that has been classified and decorated appropriately by the decorators in [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md)Type 1 errors are inevitable, but since all expected/handle-able errors should be Type 2 the isXYZError() helpers exposed at SavedObjectsErrorHelpers should be used to understand and manage error responses from the SavedObjectsClient.Type 2 errors are decorated versions of the source error, so if the elasticsearch client threw an error it will be decorated based on its type. That means that rather than looking for error.body.error.type or doing substring checks on error.body.error.reason, just use the helpers to understand the meaning of the error:\`\`\`js if (SavedObjectsErrorHelpers.isNotFoundError(error)) { // handle 404 }if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) { // 401 handling should be automatic, but in case you wanted to know }// always rethrow the error unless you handle it throw error; \`\`\`\#\#\# 404s from missing indexFrom the perspective of application code and APIs the SavedObjectsClient is a black box that persists objects. One of the internal details that users have no control over is that we use an elasticsearch index for persistance and that index might be missing.At the time of writing we are in the process of transitioning away from the operating assumption that the SavedObjects index is always available. Part of this transition is handling errors resulting from an index missing. These used to trigger a 500 error in most cases, and in others cause 404s with different error messages.From my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The object the request/call was targeting could not be found. This is why \#14141 takes special care to ensure that 404 errors are generic and don't distinguish between index missing or document missing.\#\#\# 503s from missing indexUnlike all other methods, create requests are supposed to succeed even when the Kibana index does not exist because it will be automatically created by elasticsearch. When that is not the case it is because Elasticsearch's action.auto_create_index setting prevents it from being created automatically so we throw a special 503 with the intention of informing the user that their Elasticsearch settings need to be updated.See [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) See [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | +| [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md) | Describes the factory used to create instances of the Saved Objects Client. | +| [SavedObjectsClientFactoryProvider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) | Provider to invoke to retrieve a [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md). | +| [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. | +| [ScopeableRequest](./kibana-plugin-server.scopeablerequest.md) | A user credentials container. It accommodates the necessary auth credentials to impersonate the current user.See [KibanaRequest](./kibana-plugin-server.kibanarequest.md). | +| [SharedGlobalConfig](./kibana-plugin-server.sharedglobalconfig.md) | | +| [StringValidation](./kibana-plugin-server.stringvalidation.md) | Allows regex objects or a regex string | +| [UiSettingsType](./kibana-plugin-server.uisettingstype.md) | UI element type to represent the settings. | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactory.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactory.md index 01c6c6a108b7b..be4ecfb081dad 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactory.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactory.md @@ -1,15 +1,15 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md) - -## SavedObjectsClientFactory type - -Describes the factory used to create instances of the Saved Objects Client. - -Signature: - -```typescript -export declare type SavedObjectsClientFactory = ({ request, }: { - request: KibanaRequest; -}) => SavedObjectsClientContract; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md) + +## SavedObjectsClientFactory type + +Describes the factory used to create instances of the Saved Objects Client. + +Signature: + +```typescript +export declare type SavedObjectsClientFactory = ({ request, }: { + request: KibanaRequest; +}) => SavedObjectsClientContract; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactoryprovider.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactoryprovider.md index 59617b6be443c..d5be055d3c8c9 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactoryprovider.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientfactoryprovider.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientFactoryProvider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) - -## SavedObjectsClientFactoryProvider type - -Provider to invoke to retrieve a [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md). - -Signature: - -```typescript -export declare type SavedObjectsClientFactoryProvider = (repositoryFactory: SavedObjectsRepositoryFactory) => SavedObjectsClientFactory; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientFactoryProvider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) + +## SavedObjectsClientFactoryProvider type + +Provider to invoke to retrieve a [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md). + +Signature: + +```typescript +export declare type SavedObjectsClientFactoryProvider = (repositoryFactory: SavedObjectsRepositoryFactory) => SavedObjectsClientFactory; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md index f429c92209900..579c555a83062 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) - -## SavedObjectsClientWrapperFactory type - -Describes the factory used to create instances of Saved Objects Client Wrappers. - -Signature: - -```typescript -export declare type SavedObjectsClientWrapperFactory = (options: SavedObjectsClientWrapperOptions) => SavedObjectsClientContract; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) + +## SavedObjectsClientWrapperFactory type + +Describes the factory used to create instances of Saved Objects Client Wrappers. + +Signature: + +```typescript +export declare type SavedObjectsClientWrapperFactory = (options: SavedObjectsClientWrapperOptions) => SavedObjectsClientContract; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md index dfff863898a2b..57b60b50313c2 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md @@ -1,21 +1,21 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) - -## SavedObjectsClientWrapperOptions interface - -Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. - -Signature: - -```typescript -export interface SavedObjectsClientWrapperOptions -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [client](./kibana-plugin-server.savedobjectsclientwrapperoptions.client.md) | SavedObjectsClientContract | | -| [request](./kibana-plugin-server.savedobjectsclientwrapperoptions.request.md) | KibanaRequest | | - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) + +## SavedObjectsClientWrapperOptions interface + +Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. + +Signature: + +```typescript +export interface SavedObjectsClientWrapperOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [client](./kibana-plugin-server.savedobjectsclientwrapperoptions.client.md) | SavedObjectsClientContract | | +| [request](./kibana-plugin-server.savedobjectsclientwrapperoptions.request.md) | KibanaRequest | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.request.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.request.md index 89c7e0ed207ff..688defbe47b94 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.request.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.request.md @@ -1,11 +1,11 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) > [request](./kibana-plugin-server.savedobjectsclientwrapperoptions.request.md) - -## SavedObjectsClientWrapperOptions.request property - -Signature: - -```typescript -request: KibanaRequest; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) > [request](./kibana-plugin-server.savedobjectsclientwrapperoptions.request.md) + +## SavedObjectsClientWrapperOptions.request property + +Signature: + +```typescript +request: KibanaRequest; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md index b808d38793fff..9be1583c3e254 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) > [createInternalRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md) - -## SavedObjectsRepositoryFactory.createInternalRepository property - -Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. - -Signature: - -```typescript -createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) > [createInternalRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md) + +## SavedObjectsRepositoryFactory.createInternalRepository property + +Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. + +Signature: + +```typescript +createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md index 20322d809dce7..5dd9bb05f1fbe 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) > [createScopedRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md) - -## SavedObjectsRepositoryFactory.createScopedRepository property - -Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. - -Signature: - -```typescript -createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) > [createScopedRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md) + +## SavedObjectsRepositoryFactory.createScopedRepository property + +Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. + +Signature: + +```typescript +createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.md index fc6c4a516284a..62bcb2d10363e 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepositoryfactory.md @@ -1,21 +1,21 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) - -## SavedObjectsRepositoryFactory interface - -Factory provided when invoking a [client factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) See [SavedObjectsServiceSetup.setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) - -Signature: - -```typescript -export interface SavedObjectsRepositoryFactory -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [createInternalRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | -| [createScopedRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsRepositoryFactory](./kibana-plugin-server.savedobjectsrepositoryfactory.md) + +## SavedObjectsRepositoryFactory interface + +Factory provided when invoking a [client factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) See [SavedObjectsServiceSetup.setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) + +Signature: + +```typescript +export interface SavedObjectsRepositoryFactory +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [createInternalRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | +| [createScopedRepository](./kibana-plugin-server.savedobjectsrepositoryfactory.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md index becff5bd2821e..769be031eca06 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) > [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) - -## SavedObjectsServiceSetup.addClientWrapper property - -Add a [client wrapper factory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) with the given priority. - -Signature: - -```typescript -addClientWrapper: (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) > [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) + +## SavedObjectsServiceSetup.addClientWrapper property + +Add a [client wrapper factory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) with the given priority. + +Signature: + +```typescript +addClientWrapper: (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md index 64fb1f4a5f638..2c421f7fc13a7 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md @@ -1,33 +1,33 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) - -## SavedObjectsServiceSetup interface - -Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for creating and registering Saved Object client wrappers. - -Signature: - -```typescript -export interface SavedObjectsServiceSetup -``` - -## Remarks - -Note: The Saved Object setup API's should only be used for creating and registering client wrappers. Constructing a Saved Objects client or repository for use within your own plugin won't have any of the registered wrappers applied and is considered an anti-pattern. Use the Saved Objects client from the [SavedObjectsServiceStart\#getScopedClient](./kibana-plugin-server.savedobjectsservicestart.md) method or the [route handler context](./kibana-plugin-server.requesthandlercontext.md) instead. - -When plugins access the Saved Objects client, a new client is created using the factory provided to `setClientFactory` and wrapped by all wrappers registered through `addClientWrapper`. To create a factory or wrapper, plugins will have to construct a Saved Objects client. First create a repository by calling `scopedRepository` or `internalRepository` and then use this repository as the argument to the [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) constructor. - -## Example - -import { SavedObjectsClient, CoreSetup } from 'src/core/server'; - -export class Plugin() { setup: (core: CoreSetup) => { core.savedObjects.setClientFactory(({ request: KibanaRequest }) => { return new SavedObjectsClient(core.savedObjects.scopedRepository(request)); }) } } - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void | Add a [client wrapper factory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) with the given priority. | -| [setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) | (clientFactoryProvider: SavedObjectsClientFactoryProvider) => void | Set the default [factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) for creating Saved Objects clients. Only one provider can be set, subsequent calls to this method will fail. | - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) + +## SavedObjectsServiceSetup interface + +Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for creating and registering Saved Object client wrappers. + +Signature: + +```typescript +export interface SavedObjectsServiceSetup +``` + +## Remarks + +Note: The Saved Object setup API's should only be used for creating and registering client wrappers. Constructing a Saved Objects client or repository for use within your own plugin won't have any of the registered wrappers applied and is considered an anti-pattern. Use the Saved Objects client from the [SavedObjectsServiceStart\#getScopedClient](./kibana-plugin-server.savedobjectsservicestart.md) method or the [route handler context](./kibana-plugin-server.requesthandlercontext.md) instead. + +When plugins access the Saved Objects client, a new client is created using the factory provided to `setClientFactory` and wrapped by all wrappers registered through `addClientWrapper`. To create a factory or wrapper, plugins will have to construct a Saved Objects client. First create a repository by calling `scopedRepository` or `internalRepository` and then use this repository as the argument to the [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) constructor. + +## Example + +import { SavedObjectsClient, CoreSetup } from 'src/core/server'; + +export class Plugin() { setup: (core: CoreSetup) => { core.savedObjects.setClientFactory(({ request: KibanaRequest }) => { return new SavedObjectsClient(core.savedObjects.scopedRepository(request)); }) } } + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void | Add a [client wrapper factory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) with the given priority. | +| [setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) | (clientFactoryProvider: SavedObjectsClientFactoryProvider) => void | Set the default [factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) for creating Saved Objects clients. Only one provider can be set, subsequent calls to this method will fail. | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md index ed11255048f19..5b57495198edc 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) > [setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) - -## SavedObjectsServiceSetup.setClientFactoryProvider property - -Set the default [factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) for creating Saved Objects clients. Only one provider can be set, subsequent calls to this method will fail. - -Signature: - -```typescript -setClientFactoryProvider: (clientFactoryProvider: SavedObjectsClientFactoryProvider) => void; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) > [setClientFactoryProvider](./kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md) + +## SavedObjectsServiceSetup.setClientFactoryProvider property + +Set the default [factory provider](./kibana-plugin-server.savedobjectsclientfactoryprovider.md) for creating Saved Objects clients. Only one provider can be set, subsequent calls to this method will fail. + +Signature: + +```typescript +setClientFactoryProvider: (clientFactoryProvider: SavedObjectsClientFactoryProvider) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md index d639a8bc66b7e..c33e1750224d7 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md @@ -1,13 +1,13 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) > [createInternalRepository](./kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) - -## SavedObjectsServiceStart.createInternalRepository property - -Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. - -Signature: - -```typescript -createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; -``` + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) > [createInternalRepository](./kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) + +## SavedObjectsServiceStart.createInternalRepository property + +Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. + +Signature: + +```typescript +createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md index 7683a9e46c51d..e562f7f4e7569 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md @@ -1,18 +1,18 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) > [createScopedRepository](./kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) - -## SavedObjectsServiceStart.createScopedRepository property - -Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. - -Signature: - -```typescript -createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; -``` - -## Remarks - -Prefer using `getScopedClient`. This should only be used when using methods not exposed on [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) > [createScopedRepository](./kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) + +## SavedObjectsServiceStart.createScopedRepository property + +Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. + +Signature: + +```typescript +createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; +``` + +## Remarks + +Prefer using `getScopedClient`. This should only be used when using methods not exposed on [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.md index cf2b4f57a7461..7e4b1fd9158e6 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.md @@ -1,22 +1,22 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) - -## SavedObjectsServiceStart interface - -Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. - -Signature: - -```typescript -export interface SavedObjectsServiceStart -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [createInternalRepository](./kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | -| [createScopedRepository](./kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | -| [getScopedClient](./kibana-plugin-server.savedobjectsservicestart.getscopedclient.md) | (req: KibanaRequest, options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract | Creates a [Saved Objects client](./kibana-plugin-server.savedobjectsclientcontract.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. If other plugins have registered Saved Objects client wrappers, these will be applied to extend the functionality of the client.A client that is already scoped to the incoming request is also exposed from the route handler context see [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md). | - + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) + +## SavedObjectsServiceStart interface + +Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. + +Signature: + +```typescript +export interface SavedObjectsServiceStart +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [createInternalRepository](./kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | +| [createScopedRepository](./kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | +| [getScopedClient](./kibana-plugin-server.savedobjectsservicestart.getscopedclient.md) | (req: KibanaRequest, options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract | Creates a [Saved Objects client](./kibana-plugin-server.savedobjectsclientcontract.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. If other plugins have registered Saved Objects client wrappers, these will be applied to extend the functionality of the client.A client that is already scoped to the incoming request is also exposed from the route handler context see [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md). | + diff --git a/packages/kbn-i18n/src/angular/directive.ts b/packages/kbn-i18n/src/angular/directive.ts index 8a4b724ec0629..5d18a25be71f0 100644 --- a/packages/kbn-i18n/src/angular/directive.ts +++ b/packages/kbn-i18n/src/angular/directive.ts @@ -30,7 +30,13 @@ interface I18nScope extends IScope { const HTML_KEY_PREFIX = 'html_'; const PLACEHOLDER_SEPARATOR = '@I18N@'; -export function i18nDirective( +export const i18nDirective: [string, string, typeof i18nDirectiveFn] = [ + 'i18n', + '$sanitize', + i18nDirectiveFn, +]; + +function i18nDirectiveFn( i18n: I18nServiceType, $sanitize: (html: string) => string ): IDirective { diff --git a/packages/kbn-i18n/src/angular/filter.ts b/packages/kbn-i18n/src/angular/filter.ts index 2f7425a9dd8e0..cc9da0aa51d30 100644 --- a/packages/kbn-i18n/src/angular/filter.ts +++ b/packages/kbn-i18n/src/angular/filter.ts @@ -19,7 +19,9 @@ import { I18nServiceType } from './provider'; -export function i18nFilter(i18n: I18nServiceType) { +export const i18nFilter: [string, typeof i18nFilterFn] = ['i18n', i18nFilterFn]; + +function i18nFilterFn(i18n: I18nServiceType) { return (id: string, { defaultMessage = '', values = {} } = {}) => { return i18n(id, { values, diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 7a15c3bb742c0..9a5fb479276f7 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -30,6 +30,9 @@ export const ElasticEui = require('@elastic/eui'); export const ElasticEuiLibServices = require('@elastic/eui/lib/services'); export const ElasticEuiLightTheme = require('@elastic/eui/dist/eui_theme_light.json'); export const ElasticEuiDarkTheme = require('@elastic/eui/dist/eui_theme_dark.json'); +export const KbnI18n = require('@kbn/i18n'); +export const KbnI18nAngular = require('@kbn/i18n/angular'); +export const KbnI18nReact = require('@kbn/i18n/react'); export const Moment = require('moment'); export const MomentTimezone = require('moment-timezone/moment-timezone'); export const React = require('react'); diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index cef25295b35d7..5f5ac3f1c9c2f 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -30,6 +30,9 @@ exports.externals = { '@elastic/eui/lib/services': '__kbnSharedDeps__.ElasticEuiLibServices', '@elastic/eui/dist/eui_theme_light.json': '__kbnSharedDeps__.ElasticEuiLightTheme', '@elastic/eui/dist/eui_theme_dark.json': '__kbnSharedDeps__.ElasticEuiDarkTheme', + '@kbn/i18n': '__kbnSharedDeps__.KbnI18n', + '@kbn/i18n/angular': '__kbnSharedDeps__.KbnI18nAngular', + '@kbn/i18n/react': '__kbnSharedDeps__.KbnI18nReact', jquery: '__kbnSharedDeps__.Jquery', moment: '__kbnSharedDeps__.Moment', 'moment-timezone': '__kbnSharedDeps__.MomentTimezone', diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 488f57b01e168..47a47449927e4 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -12,6 +12,7 @@ "@elastic/eui": "18.2.1", "@elastic/charts": "^16.1.0", "@kbn/dev-utils": "1.0.0", + "@kbn/i18n": "1.0.0", "@yarnpkg/lockfile": "^1.1.0", "abortcontroller-polyfill": "^1.3.0", "angular": "^1.7.9", diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts index 36bbc8cc82873..83a41a52aca38 100644 --- a/packages/kbn-utility-types/index.ts +++ b/packages/kbn-utility-types/index.ts @@ -21,7 +21,17 @@ import { PromiseType } from 'utility-types'; export { $Values, Required, Optional, Class } from 'utility-types'; /** - * Returns wrapped type of a promise. + * A type that may or may not be a `Promise`. + */ +export type MaybePromise = T | Promise; + +/** + * Converts a type to a `Promise`, unless it is already a `Promise`. Useful when proxying the return value of a possibly async function. + */ +export type ShallowPromise = T extends Promise ? Promise : Promise; + +/** + * Returns wrapped type of a `Promise`. */ export type UnwrapPromise> = PromiseType; @@ -39,11 +49,6 @@ export type UnwrapObservable> = T extends Observab ? U : never; -/** - * Converts a type to a `Promise`, unless it is already a `Promise`. Useful when proxying the return value of a possibly async function. - */ -export type ShallowPromise = T extends Promise ? Promise : Promise; - /** * Ensures T is of type X. */ diff --git a/src/core/public/http/fetch.test.ts b/src/core/public/http/fetch.test.ts index adb3d696a962f..a99b7607d7149 100644 --- a/src/core/public/http/fetch.test.ts +++ b/src/core/public/http/fetch.test.ts @@ -24,7 +24,7 @@ import { join } from 'path'; import { Fetch } from './fetch'; import { BasePath } from './base_path'; -import { IHttpResponse } from './types'; +import { HttpResponse, HttpFetchOptionsWithPath } from './types'; function delay(duration: number) { return new Promise(r => setTimeout(r, duration)); @@ -40,6 +40,19 @@ describe('Fetch', () => { }); describe('http requests', () => { + it('should fail with invalid arguments', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch( + // @ts-ignore + { path: '/', headers: { hello: 'world' } }, + { headers: { hello: 'mars' } } + ) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch arguments, must either be (string, object) or (object, undefined), received (object, object)"` + ); + }); + it('should use supplied request method', async () => { fetchMock.post('*', {}); await fetchInstance.fetch('/my/path', { method: 'POST' }); @@ -56,6 +69,15 @@ describe('Fetch', () => { }); }); + it('should not set Content-Type if undefined', async () => { + fetchMock.get('*', {}); + await fetchInstance.fetch('/my/path', { headers: { 'Content-Type': undefined } }); + + expect(fetchMock.lastOptions()!.headers).toMatchObject({ + 'kbn-version': 'VERSION', + }); + }); + it('should use supplied pathname and querystring', async () => { fetchMock.get('*', {}); await fetchInstance.fetch('/my/path', { query: { a: 'b' } }); @@ -69,13 +91,106 @@ describe('Fetch', () => { headers: { myHeader: 'foo' }, }); - expect(fetchMock.lastOptions()!.headers).toEqual({ + expect(fetchMock.lastOptions()!.headers).toMatchObject({ 'content-type': 'application/json', 'kbn-version': 'VERSION', myheader: 'foo', }); }); + it('should not allow overwriting of kbn-version header', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo', 'kbn-version': 'CUSTOM!' }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-version]"` + ); + }); + + it('should not set kbn-system-request header by default', async () => { + fetchMock.get('*', {}); + await fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo' }, + }); + + expect(fetchMock.lastOptions()!.headers['kbn-system-request']).toBeUndefined(); + }); + + it('should not set kbn-system-request header when asSystemRequest: false', async () => { + fetchMock.get('*', {}); + await fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo' }, + asSystemRequest: false, + }); + + expect(fetchMock.lastOptions()!.headers['kbn-system-request']).toBeUndefined(); + }); + + it('should set kbn-system-request header when asSystemRequest: true', async () => { + fetchMock.get('*', {}); + await fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo' }, + asSystemRequest: true, + }); + + expect(fetchMock.lastOptions()!.headers).toMatchObject({ + 'kbn-system-request': 'true', + myheader: 'foo', + }); + }); + + it('should not allow overwriting of kbn-system-request when asSystemRequest: true', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo', 'kbn-system-request': 'ANOTHER!' }, + asSystemRequest: true, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-system-request]"` + ); + }); + + it('should not allow overwriting of kbn-system-request when asSystemRequest: false', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo', 'kbn-system-request': 'ANOTHER!' }, + asSystemRequest: false, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-system-request]"` + ); + }); + + // Deprecated header used by legacy platform pre-7.7. Remove in 8.x. + it('should not allow overwriting of kbn-system-api when asSystemRequest: true', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo', 'kbn-system-api': 'ANOTHER!' }, + asSystemRequest: true, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-system-api]"` + ); + }); + + // Deprecated header used by legacy platform pre-7.7. Remove in 8.x. + it('should not allow overwriting of kbn-system-api when asSystemRequest: false', async () => { + fetchMock.get('*', {}); + await expect( + fetchInstance.fetch('/my/path', { + headers: { myHeader: 'foo', 'kbn-system-api': 'ANOTHER!' }, + asSystemRequest: false, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-system-api]"` + ); + }); + it('should return response', async () => { fetchMock.get('*', { foo: 'bar' }); const json = await fetchInstance.fetch('/my/path'); @@ -121,11 +236,35 @@ describe('Fetch', () => { const response = await fetchInstance.fetch('/my/path', { asResponse: true }); + expect(response.fetchOptions).toMatchObject({ + path: '/my/path', + asResponse: true, + }); expect(response.request).toBeInstanceOf(Request); expect(response.response).toBeInstanceOf(Response); expect(response.body).toEqual({ foo: 'bar' }); }); + it('should expose asSystemRequest: true on detailed response object when asResponse = true', async () => { + fetchMock.get('*', { foo: 'bar' }); + + const response = await fetchInstance.fetch('/my/path', { + asResponse: true, + asSystemRequest: true, + }); + expect(response.fetchOptions.asSystemRequest).toBe(true); + }); + + it('should expose asSystemRequest: false on detailed response object when asResponse = true', async () => { + fetchMock.get('*', { foo: 'bar' }); + + const response = await fetchInstance.fetch('/my/path', { + asResponse: true, + asSystemRequest: false, + }); + expect(response.fetchOptions.asSystemRequest).toBe(false); + }); + it('should reject on network error', async () => { expect.assertions(1); fetchMock.get('*', { status: 500 }); @@ -245,13 +384,18 @@ describe('Fetch', () => { it('should be able to manipulate request instance', async () => { fetchInstance.intercept({ - request(request) { - request.headers.set('Content-Type', 'CustomContentType'); + request(options) { + return { + headers: { + ...options.headers, + 'Content-Type': 'CustomContentType', + }, + }; }, }); fetchInstance.intercept({ - request(request) { - return new Request('/my/route', request); + request() { + return { path: '/my/route' }; }, }); @@ -262,7 +406,7 @@ describe('Fetch', () => { expect(fetchMock.lastOptions()!.headers).toMatchObject({ 'content-type': 'CustomContentType', }); - expect(fetchMock.lastUrl()).toBe('/my/route'); + expect(fetchMock.lastUrl()).toBe('http://localhost/myBase/my/route'); }); it('should call interceptors in correct order', async () => { @@ -402,8 +546,8 @@ describe('Fetch', () => { fetchInstance.intercept({ request: unusedSpy, - requestError({ request }) { - return new Request('/my/route', request); + requestError() { + return { path: '/my/route' }; }, response: usedSpy, }); @@ -423,16 +567,16 @@ describe('Fetch', () => { it('should accumulate request information', async () => { const routes = ['alpha', 'beta', 'gamma']; - const createRequest = jest.fn( - (request: Request) => new Request(`/api/${routes.shift()}`, request) - ); + const createRequest = jest.fn((options: HttpFetchOptionsWithPath) => ({ + path: `/api/${routes.shift()}`, + })); fetchInstance.intercept({ request: createRequest, }); fetchInstance.intercept({ requestError(httpErrorRequest) { - return httpErrorRequest.request; + return httpErrorRequest.fetchOptions; }, }); fetchInstance.intercept({ @@ -450,15 +594,15 @@ describe('Fetch', () => { await expect(fetchInstance.fetch('/my/route')).resolves.toEqual({ foo: 'bar' }); expect(fetchMock.called()).toBe(true); expect(routes.length).toBe(0); - expect(createRequest.mock.calls[0][0].url).toContain('/my/route'); - expect(createRequest.mock.calls[1][0].url).toContain('/api/alpha'); - expect(createRequest.mock.calls[2][0].url).toContain('/api/beta'); + expect(createRequest.mock.calls[0][0].path).toContain('/my/route'); + expect(createRequest.mock.calls[1][0].path).toContain('/api/alpha'); + expect(createRequest.mock.calls[2][0].path).toContain('/api/beta'); expect(fetchMock.lastCall()!.request.url).toContain('/api/gamma'); }); it('should accumulate response information', async () => { const bodies = ['alpha', 'beta', 'gamma']; - const createResponse = jest.fn((httpResponse: IHttpResponse) => ({ + const createResponse = jest.fn((httpResponse: HttpResponse) => ({ body: bodies.shift(), })); @@ -550,7 +694,7 @@ describe('Fetch', () => { fetchInstance.intercept({ requestError(httpErrorRequest) { - return httpErrorRequest.request; + return httpErrorRequest.fetchOptions; }, response: usedSpy, }); diff --git a/src/core/public/http/fetch.ts b/src/core/public/http/fetch.ts index b7ceaed6e56a7..1043b50dff958 100644 --- a/src/core/public/http/fetch.ts +++ b/src/core/public/http/fetch.ts @@ -20,10 +20,16 @@ import { merge } from 'lodash'; import { format } from 'url'; -import { IBasePath, HttpInterceptor, HttpHandler, HttpFetchOptions, IHttpResponse } from './types'; +import { + IBasePath, + HttpInterceptor, + HttpHandler, + HttpFetchOptions, + HttpResponse, + HttpFetchOptionsWithPath, +} from './types'; import { HttpFetchError } from './http_fetch_error'; import { HttpInterceptController } from './http_intercept_controller'; -import { HttpResponse } from './response'; import { interceptRequest, interceptResponse } from './intercept'; import { HttpInterceptHaltError } from './http_intercept_halt_error'; @@ -60,29 +66,30 @@ export class Fetch { public readonly put = this.shorthand('PUT'); public fetch: HttpHandler = async ( - path: string, - options: HttpFetchOptions = {} + pathOrOptions: string | HttpFetchOptionsWithPath, + options?: HttpFetchOptions ) => { - const initialRequest = this.createRequest(path, options); + const optionsWithPath = validateFetchArguments(pathOrOptions, options); const controller = new HttpInterceptController(); // We wrap the interception in a separate promise to ensure that when // a halt is called we do not resolve or reject, halting handling of the promise. - return new Promise>(async (resolve, reject) => { + return new Promise>(async (resolve, reject) => { try { - const interceptedRequest = await interceptRequest( - initialRequest, + const interceptedOptions = await interceptRequest( + optionsWithPath, this.interceptors, controller ); - const initialResponse = this.fetchResponse(interceptedRequest); + const initialResponse = this.fetchResponse(interceptedOptions); const interceptedResponse = await interceptResponse( + interceptedOptions, initialResponse, this.interceptors, controller ); - if (options.asResponse) { + if (optionsWithPath.asResponse) { resolve(interceptedResponse); } else { resolve(interceptedResponse.body); @@ -95,38 +102,44 @@ export class Fetch { }); }; - private createRequest(path: string, options?: HttpFetchOptions): Request { + private createRequest(options: HttpFetchOptionsWithPath): Request { // Merge and destructure options out that are not applicable to the Fetch API. - const { query, prependBasePath: shouldPrependBasePath, asResponse, ...fetchOptions } = merge( + const { + query, + prependBasePath: shouldPrependBasePath, + asResponse, + asSystemRequest, + ...fetchOptions + } = merge( { method: 'GET', credentials: 'same-origin', prependBasePath: true, + }, + options, + { headers: { - 'kbn-version': this.params.kibanaVersion, 'Content-Type': 'application/json', + ...options.headers, + 'kbn-version': this.params.kibanaVersion, }, - }, - options || {} + } ); const url = format({ - pathname: shouldPrependBasePath ? this.params.basePath.prepend(path) : path, + pathname: shouldPrependBasePath ? this.params.basePath.prepend(options.path) : options.path, query, }); - if ( - options && - options.headers && - 'Content-Type' in options.headers && - options.headers['Content-Type'] === undefined - ) { - delete fetchOptions.headers['Content-Type']; + // Make sure the system request header is only present if `asSystemRequest` is true. + if (asSystemRequest) { + fetchOptions.headers['kbn-system-request'] = 'true'; } return new Request(url, fetchOptions); } - private async fetchResponse(request: Request) { + private async fetchResponse(fetchOptions: HttpFetchOptionsWithPath): Promise> { + const request = this.createRequest(fetchOptions); let response: Response; let body = null; @@ -164,11 +177,46 @@ export class Fetch { throw new HttpFetchError(response.statusText, request, response, body); } - return new HttpResponse({ request, response, body }); + return { fetchOptions, request, response, body }; } - private shorthand(method: string) { - return (path: string, options: HttpFetchOptions = {}) => - this.fetch(path, { ...options, method }); + private shorthand(method: string): HttpHandler { + return (pathOrOptions: string | HttpFetchOptionsWithPath, options?: HttpFetchOptions) => { + const optionsWithPath = validateFetchArguments(pathOrOptions, options); + return this.fetch({ ...optionsWithPath, method }); + }; } } + +/** + * Ensure that the overloaded arguments to `HttpHandler` are valid. + */ +const validateFetchArguments = ( + pathOrOptions: string | HttpFetchOptionsWithPath, + options?: HttpFetchOptions +): HttpFetchOptionsWithPath => { + let fullOptions: HttpFetchOptionsWithPath; + + if (typeof pathOrOptions === 'string' && (typeof options === 'object' || options === undefined)) { + fullOptions = { ...options, path: pathOrOptions }; + } else if (typeof pathOrOptions === 'object' && options === undefined) { + fullOptions = pathOrOptions; + } else { + throw new Error( + `Invalid fetch arguments, must either be (string, object) or (object, undefined), received (${typeof pathOrOptions}, ${typeof options})` + ); + } + + const invalidHeaders = Object.keys(fullOptions.headers ?? {}).filter(headerName => + headerName.startsWith('kbn-') + ); + if (invalidHeaders.length) { + throw new Error( + `Invalid fetch headers, headers beginning with "kbn-" are not allowed: [${invalidHeaders.join( + ',' + )}]` + ); + } + + return fullOptions; +}; diff --git a/src/core/public/http/intercept.ts b/src/core/public/http/intercept.ts index e2a16565c61c4..bacc8748d2680 100644 --- a/src/core/public/http/intercept.ts +++ b/src/core/public/http/intercept.ts @@ -19,28 +19,31 @@ import { HttpInterceptController } from './http_intercept_controller'; import { HttpInterceptHaltError } from './http_intercept_halt_error'; -import { HttpInterceptor, IHttpResponse } from './types'; -import { HttpResponse } from './response'; +import { HttpInterceptor, HttpResponse, HttpFetchOptionsWithPath } from './types'; export async function interceptRequest( - request: Request, + options: HttpFetchOptionsWithPath, interceptors: ReadonlySet, controller: HttpInterceptController -): Promise { - let next = request; +): Promise { + let current: HttpFetchOptionsWithPath; return [...interceptors].reduceRight( (promise, interceptor) => promise.then( - async (current: Request) => { - next = current; + async fetchOptions => { + current = fetchOptions; checkHalt(controller); if (!interceptor.request) { - return current; + return fetchOptions; } - return (await interceptor.request(current, controller)) || current; + const overrides = await interceptor.request(current, controller); + return { + ...current, + ...overrides, + }; }, async error => { checkHalt(controller, error); @@ -49,26 +52,33 @@ export async function interceptRequest( throw error; } - const nextRequest = await interceptor.requestError({ error, request: next }, controller); + const overrides = await interceptor.requestError( + { error, fetchOptions: current }, + controller + ); - if (!nextRequest) { + if (!overrides) { throw error; } - next = nextRequest; - return next; + current = { + ...current, + ...overrides, + }; + return current; } ), - Promise.resolve(request) + Promise.resolve(options) ); } export async function interceptResponse( - responsePromise: Promise, + fetchOptions: HttpFetchOptionsWithPath, + responsePromise: Promise, interceptors: ReadonlySet, controller: HttpInterceptController -): Promise { - let current: IHttpResponse; +): Promise { + let current: HttpResponse; return await [...interceptors].reduce( (promise, interceptor) => @@ -83,10 +93,10 @@ export async function interceptResponse( const interceptorOverrides = (await interceptor.response(httpResponse, controller)) || {}; - return new HttpResponse({ + return { ...httpResponse, ...interceptorOverrides, - }); + }; }, async error => { const request = error.request || (current && current.request); @@ -101,6 +111,7 @@ export async function interceptResponse( const next = await interceptor.responseError( { error, + fetchOptions, request, response: error.response || (current && current.response), body: error.body || (current && current.body), @@ -114,7 +125,7 @@ export async function interceptResponse( throw error; } - return new HttpResponse({ ...next, request }); + return { ...next, request, fetchOptions }; } catch (err) { checkHalt(controller, err); throw err; diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts index 27ffddc79cf65..c38b9da442943 100644 --- a/src/core/public/http/types.ts +++ b/src/core/public/http/types.ts @@ -18,6 +18,7 @@ */ import { Observable } from 'rxjs'; +import { MaybePromise } from '@kbn/utility-types'; /** @public */ export interface HttpSetup { @@ -110,7 +111,11 @@ export interface IAnonymousPaths { register(path: string): void; } -/** @public */ +/** + * Headers to append to the request. Any headers that begin with `kbn-` are considered private to Core and will cause + * {@link HttpHandler} to throw an error. + * @public + */ export interface HttpHeadersInit { [name: string]: any; } @@ -217,30 +222,54 @@ export interface HttpFetchOptions extends HttpRequestInit { headers?: HttpHeadersInit; /** - * When `true` the return type of {@link HttpHandler} will be an {@link IHttpResponse} with detailed request and + * Whether or not the request should include the "system request" header to differentiate an end user request from + * Kibana internal request. + * Can be read on the server-side using KibanaRequest#isSystemRequest. Defaults to `false`. + */ + asSystemRequest?: boolean; + + /** + * When `true` the return type of {@link HttpHandler} will be an {@link HttpResponse} with detailed request and * response information. When `false`, the return type will just be the parsed response body. Defaults to `false`. */ asResponse?: boolean; } +/** + * Similar to {@link HttpFetchOptions} but with the URL path included. + * @public + */ +export interface HttpFetchOptionsWithPath extends HttpFetchOptions { + /* + * The path on the Kibana server to send the request to. Should not include the basePath. + */ + path: string; +} + /** * A function for making an HTTP requests to Kibana's backend. See {@link HttpFetchOptions} for options and - * {@link IHttpResponse} for the response. + * {@link HttpResponse} for the response. * * @param path the path on the Kibana server to send the request to. Should not include the basePath. * @param options {@link HttpFetchOptions} - * @returns a Promise that resolves to a {@link IHttpResponse} + * @returns a Promise that resolves to a {@link HttpResponse} * @public */ export interface HttpHandler { (path: string, options: HttpFetchOptions & { asResponse: true }): Promise< - IHttpResponse + HttpResponse + >; + (options: HttpFetchOptionsWithPath & { asResponse: true }): Promise< + HttpResponse >; (path: string, options?: HttpFetchOptions): Promise; + (options: HttpFetchOptionsWithPath): Promise; } /** @public */ -export interface IHttpResponse { +export interface HttpResponse { + /** The original {@link HttpFetchOptionsWithPath} used to send this request. */ + readonly fetchOptions: Readonly; /** Raw request sent to Kibana server. */ readonly request: Readonly; /** Raw response received, may be undefined if there was an error. */ @@ -276,12 +305,13 @@ export interface IHttpFetchError extends Error { } /** @public */ -export interface HttpErrorResponse extends IHttpResponse { +export interface HttpInterceptorResponseError extends HttpResponse { + request: Readonly; error: Error | IHttpFetchError; } /** @public */ -export interface HttpErrorRequest { - request: Request; +export interface HttpInterceptorRequestError { + fetchOptions: Readonly; error: Error; } @@ -298,39 +328,39 @@ export interface HttpInterceptor { * @param controller {@link IHttpInterceptController} */ request?( - request: Request, + fetchOptions: Readonly, controller: IHttpInterceptController - ): Promise | Request | void; + ): MaybePromise> | void; /** * Define an interceptor to be executed if a request interceptor throws an error or returns a rejected Promise. - * @param httpErrorRequest {@link HttpErrorRequest} + * @param httpErrorRequest {@link HttpInterceptorRequestError} * @param controller {@link IHttpInterceptController} */ requestError?( - httpErrorRequest: HttpErrorRequest, + httpErrorRequest: HttpInterceptorRequestError, controller: IHttpInterceptController - ): Promise | Request | void; + ): MaybePromise> | void; /** * Define an interceptor to be executed after a response is received. - * @param httpResponse {@link IHttpResponse} + * @param httpResponse {@link HttpResponse} * @param controller {@link IHttpInterceptController} */ response?( - httpResponse: IHttpResponse, + httpResponse: HttpResponse, controller: IHttpInterceptController - ): Promise | IHttpResponseInterceptorOverrides | void; + ): MaybePromise | void; /** * Define an interceptor to be executed if a response interceptor throws an error or returns a rejected Promise. - * @param httpErrorResponse {@link HttpErrorResponse} + * @param httpErrorResponse {@link HttpInterceptorResponseError} * @param controller {@link IHttpInterceptController} */ responseError?( - httpErrorResponse: HttpErrorResponse, + httpErrorResponse: HttpInterceptorResponseError, controller: IHttpInterceptController - ): Promise | IHttpResponseInterceptorOverrides | void; + ): MaybePromise | void; } /** diff --git a/src/core/public/index.ts b/src/core/public/index.ts index c57d35343087a..6d756e36d7379 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -143,11 +143,12 @@ export { HttpHeadersInit, HttpRequestInit, HttpFetchOptions, + HttpFetchOptionsWithPath, HttpFetchQuery, - HttpErrorResponse, - HttpErrorRequest, + HttpInterceptorResponseError, + HttpInterceptorRequestError, HttpInterceptor, - IHttpResponse, + HttpResponse, HttpHandler, IBasePath, IAnonymousPaths, diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index a9cea7ae97998..5e36638516e56 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -9,6 +9,7 @@ import { EuiButtonEmptyProps } from '@elastic/eui'; import { EuiGlobalToastListToast } from '@elastic/eui'; import { ExclusiveUnion } from '@elastic/eui'; import { IconType } from '@elastic/eui'; +import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; import React from 'react'; import * as Rx from 'rxjs'; @@ -577,28 +578,21 @@ export type HandlerFunction = (context: T, ...args: any[]) => // @public export type HandlerParameters> = T extends (context: any, ...args: infer U) => any ? U : never; -// @public (undocumented) -export interface HttpErrorRequest { - // (undocumented) - error: Error; - // (undocumented) - request: Request; -} - -// @public (undocumented) -export interface HttpErrorResponse extends IHttpResponse { - // (undocumented) - error: Error | IHttpFetchError; -} - // @public export interface HttpFetchOptions extends HttpRequestInit { asResponse?: boolean; + asSystemRequest?: boolean; headers?: HttpHeadersInit; prependBasePath?: boolean; query?: HttpFetchQuery; } +// @public +export interface HttpFetchOptionsWithPath extends HttpFetchOptions { + // (undocumented) + path: string; +} + // @public (undocumented) export interface HttpFetchQuery { // (undocumented) @@ -610,12 +604,18 @@ export interface HttpHandler { // (undocumented) (path: string, options: HttpFetchOptions & { asResponse: true; - }): Promise>; + }): Promise>; + // (undocumented) + (options: HttpFetchOptionsWithPath & { + asResponse: true; + }): Promise>; // (undocumented) (path: string, options?: HttpFetchOptions): Promise; + // (undocumented) + (options: HttpFetchOptionsWithPath): Promise; } -// @public (undocumented) +// @public export interface HttpHeadersInit { // (undocumented) [name: string]: any; @@ -623,10 +623,26 @@ export interface HttpHeadersInit { // @public export interface HttpInterceptor { - request?(request: Request, controller: IHttpInterceptController): Promise | Request | void; - requestError?(httpErrorRequest: HttpErrorRequest, controller: IHttpInterceptController): Promise | Request | void; - response?(httpResponse: IHttpResponse, controller: IHttpInterceptController): Promise | IHttpResponseInterceptorOverrides | void; - responseError?(httpErrorResponse: HttpErrorResponse, controller: IHttpInterceptController): Promise | IHttpResponseInterceptorOverrides | void; + request?(fetchOptions: Readonly, controller: IHttpInterceptController): MaybePromise> | void; + requestError?(httpErrorRequest: HttpInterceptorRequestError, controller: IHttpInterceptController): MaybePromise> | void; + response?(httpResponse: HttpResponse, controller: IHttpInterceptController): MaybePromise | void; + responseError?(httpErrorResponse: HttpInterceptorResponseError, controller: IHttpInterceptController): MaybePromise | void; +} + +// @public (undocumented) +export interface HttpInterceptorRequestError { + // (undocumented) + error: Error; + // (undocumented) + fetchOptions: Readonly; +} + +// @public (undocumented) +export interface HttpInterceptorResponseError extends HttpResponse { + // (undocumented) + error: Error | IHttpFetchError; + // (undocumented) + request: Readonly; } // @public @@ -647,6 +663,14 @@ export interface HttpRequestInit { window?: null; } +// @public (undocumented) +export interface HttpResponse { + readonly body?: TResponseBody; + readonly fetchOptions: Readonly; + readonly request: Readonly; + readonly response?: Readonly; +} + // @public (undocumented) export interface HttpSetup { addLoadingCountSource(countSource$: Observable): void; @@ -718,13 +742,6 @@ export interface IHttpInterceptController { halted: boolean; } -// @public (undocumented) -export interface IHttpResponse { - readonly body?: TResponseBody; - readonly request: Readonly; - readonly response?: Readonly; -} - // @public export interface IHttpResponseInterceptorOverrides { readonly body?: TResponseBody; diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index aba246ce66fb5..de111e1cb8b9b 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -75,7 +75,7 @@ export class ElasticsearchService implements CoreService = {}) => { const finalConfig = merge({}, config, clientConfig); - return this.createClusterClient(type, finalConfig, deps.http.auth.getAuthHeaders); + return this.createClusterClient(type, finalConfig, deps.http.getAuthHeaders); }, }; } diff --git a/src/core/server/http/auth_state_storage.ts b/src/core/server/http/auth_state_storage.ts index 059dc7f380351..10c8ccca32401 100644 --- a/src/core/server/http/auth_state_storage.ts +++ b/src/core/server/http/auth_state_storage.ts @@ -38,16 +38,16 @@ export enum AuthStatus { } /** - * Get authentication state for a request. Returned by `auth` interceptor. + * Gets authentication state for a request. Returned by `auth` interceptor. * @param request {@link KibanaRequest} - an incoming request. * @public */ -export type GetAuthState = ( +export type GetAuthState = ( request: KibanaRequest | LegacyRequest -) => { status: AuthStatus; state: unknown }; +) => { status: AuthStatus; state: T }; /** - * Return authentication status for a request. + * Returns authentication status for a request. * @param request {@link KibanaRequest} - an incoming request. * @public */ @@ -60,9 +60,9 @@ export class AuthStateStorage { public set = (request: KibanaRequest | LegacyRequest, state: unknown) => { this.storage.set(ensureRawRequest(request), state); }; - public get: GetAuthState = request => { + public get = (request: KibanaRequest | LegacyRequest) => { const key = ensureRawRequest(request); - const state = this.storage.get(key); + const state = this.storage.get(key) as T; const status: AuthStatus = this.storage.has(key) ? AuthStatus.authenticated : this.canBeAuthenticated() diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index df7b4b5af4267..f8ef49b0f6d18 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -1067,130 +1067,6 @@ describe('setup contract', () => { }); }); - describe('#auth.isAuthenticated()', () => { - it('returns true if has been authorized', async () => { - const { registerAuth, registerRouter, server: innerServer, auth } = await server.setup( - config - ); - - const router = new Router('', logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (context, req, res) => - res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) - ); - registerRouter(router); - - await registerAuth((req, res, toolkit) => toolkit.authenticated()); - - await server.start(); - await supertest(innerServer.listener) - .get('/') - .expect(200, { isAuthenticated: true }); - }); - - it('returns false if has not been authorized', async () => { - const { registerAuth, registerRouter, server: innerServer, auth } = await server.setup( - config - ); - - const router = new Router('', logger, enhanceWithContext); - router.get( - { path: '/', validate: false, options: { authRequired: false } }, - (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) - ); - registerRouter(router); - - await registerAuth((req, res, toolkit) => toolkit.authenticated()); - - await server.start(); - await supertest(innerServer.listener) - .get('/') - .expect(200, { isAuthenticated: false }); - }); - - it('returns false if no authorization mechanism has been registered', async () => { - const { registerRouter, server: innerServer, auth } = await server.setup(config); - - const router = new Router('', logger, enhanceWithContext); - router.get( - { path: '/', validate: false, options: { authRequired: false } }, - (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) - ); - registerRouter(router); - - await server.start(); - await supertest(innerServer.listener) - .get('/') - .expect(200, { isAuthenticated: false }); - }); - }); - - describe('#auth.get()', () => { - it('returns authenticated status and allow associate auth state with request', async () => { - const user = { id: '42' }; - const { - createCookieSessionStorageFactory, - registerRouter, - registerAuth, - server: innerServer, - auth, - } = await server.setup(config); - const sessionStorageFactory = await createCookieSessionStorageFactory(cookieOptions); - registerAuth((req, res, toolkit) => { - sessionStorageFactory.asScoped(req).set({ value: user, expires: Date.now() + 1000 }); - return toolkit.authenticated({ state: user }); - }); - - const router = new Router('', logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (context, req, res) => - res.ok({ body: auth.get(req) }) - ); - registerRouter(router); - await server.start(); - - await supertest(innerServer.listener) - .get('/') - .expect(200, { state: user, status: 'authenticated' }); - }); - - it('returns correct authentication unknown status', async () => { - const { registerRouter, server: innerServer, auth } = await server.setup(config); - - const router = new Router('', logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (context, req, res) => - res.ok({ body: auth.get(req) }) - ); - - registerRouter(router); - await server.start(); - await supertest(innerServer.listener) - .get('/') - .expect(200, { status: 'unknown' }); - }); - - it('returns correct unauthenticated status', async () => { - const authenticate = jest.fn(); - - const { registerRouter, registerAuth, server: innerServer, auth } = await server.setup( - config - ); - await registerAuth(authenticate); - const router = new Router('', logger, enhanceWithContext); - router.get( - { path: '/', validate: false, options: { authRequired: false } }, - (context, req, res) => res.ok({ body: auth.get(req) }) - ); - - registerRouter(router); - await server.start(); - - await supertest(innerServer.listener) - .get('/') - .expect(200, { status: 'unauthenticated' }); - - expect(authenticate).not.toHaveBeenCalled(); - }); - }); - describe('#isTlsEnabled', () => { it('returns "true" if TLS enabled', async () => { const { isTlsEnabled } = await server.setup(configWithSSL); diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 6b978b71c6f2b..fdc272041ce35 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -32,7 +32,7 @@ import { SessionStorageCookieOptions, createCookieSessionStorageFactory, } from './cookie_session_storage'; -import { AuthStateStorage, GetAuthState, IsAuthenticated } from './auth_state_storage'; +import { IsAuthenticated, AuthStateStorage, GetAuthState } from './auth_state_storage'; import { AuthHeadersStorage, GetAuthHeaders } from './auth_headers_storage'; import { BasePath } from './base_path_service'; import { HttpServiceSetup } from './types'; @@ -53,10 +53,10 @@ export interface HttpServerSetup { registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; registerOnPreResponse: HttpServiceSetup['registerOnPreResponse']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; + getAuthHeaders: GetAuthHeaders; auth: { get: GetAuthState; isAuthenticated: IsAuthenticated; - getAuthHeaders: GetAuthHeaders; }; } @@ -120,8 +120,8 @@ export class HttpServer { auth: { get: this.authState.get, isAuthenticated: this.authState.isAuthenticated, - getAuthHeaders: this.authRequestHeaders.get, }, + getAuthHeaders: this.authRequestHeaders.get, isTlsEnabled: config.ssl.enabled, // Return server instance with the connection options so that we can properly // bridge core and the "legacy" Kibana internally. Once this bridge isn't diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 6db1ca80ab437..2b2d98d937e85 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -23,6 +23,7 @@ import { mockRouter } from './router/router.mock'; import { configMock } from '../config/config.mock'; import { InternalHttpServiceSetup } from './types'; import { HttpService } from './http_service'; +import { AuthStatus } from './auth_state_storage'; import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { AuthToolkit } from './lifecycle/auth'; import { sessionStorageMock } from './cookie_session_storage.mocks'; @@ -30,6 +31,7 @@ import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; import { OnPreResponseToolkit } from './lifecycle/on_pre_response'; type BasePathMocked = jest.Mocked; +type AuthMocked = jest.Mocked; export type HttpServiceSetupMock = jest.Mocked & { basePath: BasePathMocked; }; @@ -42,6 +44,16 @@ const createBasePathMock = (serverBasePath = '/mock-server-basepath'): BasePathM remove: jest.fn(), }); +const createAuthMock = () => { + const mock: AuthMocked = { + get: jest.fn(), + isAuthenticated: jest.fn(), + }; + mock.get.mockReturnValue({ status: AuthStatus.authenticated, state: {} }); + mock.isAuthenticated.mockReturnValue(true); + return mock; +}; + const createSetupContractMock = () => { const setupContract: HttpServiceSetupMock = { // we can mock other hapi server methods when we need it @@ -62,17 +74,15 @@ const createSetupContractMock = () => { createRouter: jest.fn().mockImplementation(() => mockRouter.create({})), basePath: createBasePathMock(), csp: CspConfig.DEFAULT, - auth: { - get: jest.fn(), - isAuthenticated: jest.fn(), - getAuthHeaders: jest.fn(), - }, + auth: createAuthMock(), + getAuthHeaders: jest.fn(), isTlsEnabled: false, }; setupContract.createCookieSessionStorageFactory.mockResolvedValue( sessionStorageMock.createFactory() ); setupContract.createRouter.mockImplementation(() => mockRouter.create()); + setupContract.getAuthHeaders.mockReturnValue({ authorization: 'authorization-header' }); return setupContract; }; @@ -107,6 +117,7 @@ const createOnPreResponseToolkitMock = (): jest.Mocked => export const httpServiceMock = { create: createHttpServiceMock, createBasePath: createBasePathMock, + createAuth: createAuthMock, createSetupContract: createSetupContractMock, createOnPreAuthToolkit: createOnPreAuthToolkitMock, createOnPostAuthToolkit: createOnPostAuthToolkitMock, diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 65c4f1432721d..65b8ba551cf91 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -32,17 +32,132 @@ interface StorageData { expires: number; } +const cookieOptions = { + name: 'sid', + encryptionKey: 'something_at_least_32_characters', + validate: () => ({ isValid: true }), + isSecure: false, +}; + describe('http service', () => { + describe('auth', () => { + let root: ReturnType; + beforeEach(async () => { + root = kbnTestServer.createRoot(); + }, 30000); + + afterEach(async () => { + await root.shutdown(); + }); + describe('#isAuthenticated()', () => { + it('returns true if has been authorized', async () => { + const { http } = await root.setup(); + const { registerAuth, createRouter, auth } = http; + + await registerAuth((req, res, toolkit) => toolkit.authenticated()); + + const router = createRouter(''); + router.get({ path: '/is-auth', validate: false }, (context, req, res) => + res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) + ); + + await root.start(); + await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: true }); + }); + + it('returns false if has not been authorized', async () => { + const { http } = await root.setup(); + const { registerAuth, createRouter, auth } = http; + + await registerAuth((req, res, toolkit) => toolkit.authenticated()); + + const router = createRouter(''); + router.get( + { path: '/is-auth', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) + ); + + await root.start(); + await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: false }); + }); + + it('returns false if no authorization mechanism has been registered', async () => { + const { http } = await root.setup(); + const { createRouter, auth } = http; + + const router = createRouter(''); + router.get( + { path: '/is-auth', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ body: { isAuthenticated: auth.isAuthenticated(req) } }) + ); + + await root.start(); + await kbnTestServer.request.get(root, '/is-auth').expect(200, { isAuthenticated: false }); + }); + }); + describe('#get()', () => { + it('returns authenticated status and allow associate auth state with request', async () => { + const user = { id: '42' }; + + const { http } = await root.setup(); + const { createCookieSessionStorageFactory, createRouter, registerAuth, auth } = http; + const sessionStorageFactory = await createCookieSessionStorageFactory(cookieOptions); + registerAuth((req, res, toolkit) => { + sessionStorageFactory.asScoped(req).set({ value: user }); + return toolkit.authenticated({ state: user }); + }); + + const router = createRouter(''); + router.get({ path: '/get-auth', validate: false }, (context, req, res) => + res.ok({ body: auth.get<{ id: string }>(req) }) + ); + + await root.start(); + + await kbnTestServer.request + .get(root, '/get-auth') + .expect(200, { state: user, status: 'authenticated' }); + }); + + it('returns correct authentication unknown status', async () => { + const { http } = await root.setup(); + const { createRouter, auth } = http; + + const router = createRouter(''); + router.get({ path: '/get-auth', validate: false }, (context, req, res) => + res.ok({ body: auth.get(req) }) + ); + + await root.start(); + await kbnTestServer.request.get(root, '/get-auth').expect(200, { status: 'unknown' }); + }); + + it('returns correct unauthenticated status', async () => { + const authenticate = jest.fn(); + + const { http } = await root.setup(); + const { createRouter, registerAuth, auth } = http; + await registerAuth(authenticate); + const router = createRouter(''); + router.get( + { path: '/get-auth', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ body: auth.get(req) }) + ); + + await root.start(); + + await kbnTestServer.request + .get(root, '/get-auth') + .expect(200, { status: 'unauthenticated' }); + + expect(authenticate).not.toHaveBeenCalled(); + }); + }); + }); + describe('legacy server', () => { describe('#registerAuth()', () => { const sessionDurationMs = 1000; - const cookieOptions = { - name: 'sid', - encryptionKey: 'something_at_least_32_characters', - validate: () => ({ isValid: true }), - isSecure: false, - path: '/', - }; let root: ReturnType; beforeEach(async () => { diff --git a/src/core/server/http/router/request.test.ts b/src/core/server/http/router/request.test.ts index 51162a2c258e9..032027c234485 100644 --- a/src/core/server/http/router/request.test.ts +++ b/src/core/server/http/router/request.test.ts @@ -66,6 +66,57 @@ describe('KibanaRequest', () => { }); }); + describe('isSytemApi property', () => { + it('is false when no kbn-system-request header is set', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(false); + }); + + it('is true when kbn-system-request header is set to true', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one', 'kbn-system-request': 'true' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(true); + }); + + it('is false when kbn-system-request header is set to false', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one', 'kbn-system-request': 'false' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(false); + }); + + // Remove support for kbn-system-api header in 8.x. Only used by legacy platform. + it('is false when no kbn-system-api header is set', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(false); + }); + + it('is true when kbn-system-api header is set to true', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one', 'kbn-system-api': 'true' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(true); + }); + + it('is false when kbn-system-api header is set to false', () => { + const request = httpServerMock.createRawRequest({ + headers: { custom: 'one', 'kbn-system-api': 'false' }, + }); + const kibanaRequest = KibanaRequest.from(request); + expect(kibanaRequest.isSystemRequest).toBe(false); + }); + }); + describe('RouteSchema type inferring', () => { it('should work with config-schema', () => { const body = Buffer.from('body!'); diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index 22fb2d2643d1c..703571ba53c0a 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -127,6 +127,11 @@ export class KibanaRequest< * This property will contain a `filtered` copy of request headers. */ public readonly headers: Headers; + /** + * Whether or not the request is a "system request" rather than an application-level request. + * Can be set on the client using the `HttpFetchOptions#asSystemRequest` option. + */ + public readonly isSystemRequest: boolean; /** {@link IKibanaSocket} */ public readonly socket: IKibanaSocket; @@ -147,6 +152,10 @@ export class KibanaRequest< ) { this.url = request.url; this.headers = deepFreeze({ ...request.headers }); + this.isSystemRequest = + request.headers['kbn-system-request'] === 'true' || + // Remove support for `kbn-system-api` in 8.x. Used only by legacy platform. + request.headers['kbn-system-api'] === 'true'; // prevent Symbol exposure via Object.getOwnPropertySymbols() Object.defineProperty(this, requestSymbol, { diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 9c8bfc073a524..01b852c26ec93 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -18,6 +18,8 @@ */ import { IContextProvider, IContextContainer } from '../context'; import { ICspConfig } from '../csp'; +import { GetAuthState, IsAuthenticated } from './auth_state_storage'; +import { GetAuthHeaders } from './auth_headers_storage'; import { RequestHandler, IRouter } from './router'; import { HttpServerSetup } from './http_server'; import { SessionStorageCookieOptions } from './cookie_session_storage'; @@ -183,6 +185,19 @@ export interface HttpServiceSetup { */ basePath: IBasePath; + auth: { + /** + * Gets authentication state for a request. Returned by `auth` interceptor. + * {@link GetAuthState} + */ + get: GetAuthState; + /** + * Returns authentication status for a request. + * {@link IsAuthenticated} + */ + isAuthenticated: IsAuthenticated; + }; + /** * The CSP config used for Kibana. */ @@ -245,6 +260,7 @@ export interface InternalHttpServiceSetup auth: HttpServerSetup['auth']; server: HttpServerSetup['server']; createRouter: (path: string, plugin?: PluginOpaqueId) => IRouter; + getAuthHeaders: GetAuthHeaders; registerRouteHandlerContext: ( pluginOpaqueId: PluginOpaqueId, contextName: T, diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 0cb717e3832aa..d0e0453564f94 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -286,6 +286,10 @@ export class LegacyService implements CoreService { registerOnPostAuth: setupDeps.core.http.registerOnPostAuth, registerOnPreResponse: setupDeps.core.http.registerOnPreResponse, basePath: setupDeps.core.http.basePath, + auth: { + get: setupDeps.core.http.auth.get, + isAuthenticated: setupDeps.core.http.auth.isAuthenticated, + }, csp: setupDeps.core.http.csp, isTlsEnabled: setupDeps.core.http.isTlsEnabled, }, diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index c0a8973d98a54..50ce507520d04 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -101,6 +101,10 @@ function createCoreSetupMock() { isTlsEnabled: httpService.isTlsEnabled, createRouter: jest.fn(), registerRouteHandlerContext: jest.fn(), + auth: { + get: httpService.auth.get, + isAuthenticated: httpService.auth.isAuthenticated, + }, }; httpMock.createRouter.mockImplementation(() => httpService.createRouter('')); diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 99cd4eda7374c..30e5209b2fc6a 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -161,6 +161,7 @@ export function createPluginSetupContext( registerOnPostAuth: deps.http.registerOnPostAuth, registerOnPreResponse: deps.http.registerOnPreResponse, basePath: deps.http.basePath, + auth: { get: deps.http.auth.get, isAuthenticated: deps.http.auth.isAuthenticated }, csp: deps.http.csp, isTlsEnabled: deps.http.isTlsEnabled, }, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a179e1f35a937..e4ea06769007a 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -706,9 +706,9 @@ export interface FakeRequest { export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; // @public -export type GetAuthState = (request: KibanaRequest | LegacyRequest) => { +export type GetAuthState = (request: KibanaRequest | LegacyRequest) => { status: AuthStatus; - state: unknown; + state: T; }; // @public @@ -738,6 +738,11 @@ export type HttpResponsePayload = undefined | string | Record | Buf // @public export interface HttpServiceSetup { + // (undocumented) + auth: { + get: GetAuthState; + isAuthenticated: IsAuthenticated; + }; basePath: IBasePath; createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; createRouter: () => IRouter; @@ -883,6 +888,7 @@ export class KibanaRequest(req: Request, routeSchemas?: RouteValidator | RouteValidatorFullConfig, withoutSecretHeaders?: boolean): KibanaRequest; readonly headers: Headers; + readonly isSystemRequest: boolean; // (undocumented) readonly params: Params; // (undocumented) diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts index ad5c91d2e19de..c9c72a7be9a14 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts @@ -21,13 +21,7 @@ import _ from 'lodash'; import { Subscription } from 'rxjs'; import { State } from 'ui/state_management/state'; import { FilterManager, esFilters } from '../../../../../../plugins/data/public'; - -import { - compareFilters, - COMPARE_ALL_OPTIONS, - // this whole file will soon be deprecated by new state management. - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/data/public/query/filter_manager/lib/compare_filters'; +import { compareFilters, COMPARE_ALL_OPTIONS } from '../../../../../../plugins/data/public'; type GetAppStateFunc = () => { filters?: esFilters.Filter[]; save?: () => void } | undefined | null; diff --git a/src/legacy/core_plugins/data/public/search/fetch/types.ts b/src/legacy/core_plugins/data/public/search/fetch/types.ts index fba14119d83c3..c7f32cf08aa94 100644 --- a/src/legacy/core_plugins/data/public/search/fetch/types.ts +++ b/src/legacy/core_plugins/data/public/search/fetch/types.ts @@ -19,16 +19,6 @@ import { ISearchStart } from 'src/plugins/data/public'; import { IUiSettingsClient } from '../../../../../../core/public'; -import { SearchRequest, SearchResponse } from '../types'; - -export interface ApiCaller { - search: (searchRequest: SearchRequest) => ApiCallerResponse; - msearch: (searchRequest: SearchRequest) => ApiCallerResponse; -} - -export interface ApiCallerResponse extends Promise { - abort: () => void; -} export interface FetchOptions { abortSignal?: AbortSignal; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 85701187fb31d..77203da9ce585 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -19,7 +19,7 @@ import { Plugin, CoreSetup, CoreStart } from '../../../../../core/public'; import { SearchSource } from './search_source'; -import { defaultSearchStrategy } from './search_strategy'; +import { defaultSearchStrategy, addSearchStrategy } from './search_strategy'; import { SearchStrategyProvider } from './search_strategy/types'; export interface SearchSetup {} // eslint-disable-line @typescript-eslint/no-empty-interface @@ -41,6 +41,8 @@ export class SearchService implements Plugin { } public start(core: CoreStart): SearchStart { + addSearchStrategy(defaultSearchStrategy); + return { defaultSearchStrategy, SearchSource, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts index 244a58e8a65e5..97d165b6b5c23 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -27,7 +27,6 @@ import chrome from 'ui/chrome'; export const legacyChrome = chrome; -export { State } from 'ui/state_management/state'; export { SavedObjectSaveOpts } from 'ui/saved_objects/types'; export { npSetup, npStart } from 'ui/new_platform'; export { IPrivate } from 'ui/private'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index 429a7f7279996..d9c0579638047 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -19,32 +19,31 @@ import { EuiConfirmModal, EuiIcon } from '@elastic/eui'; import angular, { IModule } from 'angular'; +import { History } from 'history'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { AppMountContext, ChromeStart, + IUiSettingsClient, LegacyCoreStart, SavedObjectsClientContract, - IUiSettingsClient, } from 'kibana/public'; -import { Storage } from '../../../../../../plugins/kibana_utils/public'; +import { IKbnUrlStateStorage, Storage } from '../../../../../../plugins/kibana_utils/public'; import { - GlobalStateProvider, - StateManagementConfigProvider, - PrivateProvider, - EventsProvider, - PersistedState, + configureAppAngularModule, + confirmModalFactory, createTopNavDirective, createTopNavHelper, - PromiseServiceCreator, + EventsProvider, + IPrivate, KbnUrlProvider, + PersistedState, + PrivateProvider, + PromiseServiceCreator, RedirectWhenMissingProvider, - confirmModalFactory, - configureAppAngularModule, SavedObjectLoader, - IPrivate, + StateManagementConfigProvider, } from '../legacy_imports'; - // @ts-ignore import { initDashboardApp } from './legacy_app'; import { IEmbeddableStart } from '../../../../../../plugins/embeddable/public'; @@ -67,6 +66,8 @@ export interface RenderDeps { embeddables: IEmbeddableStart; localStorage: Storage; share: SharePluginStart; + history: History; + kbnUrlStateStorage: IKbnUrlStateStorage; } let angularModuleInstance: IModule | null = null; @@ -79,7 +80,9 @@ export const renderApp = (element: HTMLElement, appBasePath: string, deps: Rende // custom routing stuff initDashboardApp(angularModuleInstance, deps); } + const $injector = mountDashboardApp(appBasePath, element); + return () => { $injector.get('$rootScope').$destroy(); }; @@ -146,17 +149,13 @@ function createLocalConfirmModalModule() { } function createLocalStateModule() { - angular - .module('app/dashboard/State', [ - 'app/dashboard/Private', - 'app/dashboard/Config', - 'app/dashboard/KbnUrl', - 'app/dashboard/Promise', - 'app/dashboard/PersistedState', - ]) - .service('globalState', function(Private: any) { - return Private(GlobalStateProvider); - }); + angular.module('app/dashboard/State', [ + 'app/dashboard/Private', + 'app/dashboard/Config', + 'app/dashboard/KbnUrl', + 'app/dashboard/Promise', + 'app/dashboard/PersistedState', + ]); } function createLocalPersistedStateModule() { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx index f56990ae82e56..367db6644ff57 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx @@ -96,14 +96,12 @@ export function initDashboardAppDirective(app: any, deps: RenderDeps) { $route: any, $routeParams: { id?: string; - }, - globalState: any + } ) => new DashboardAppController({ $route, $scope, $routeParams, - globalState, config, confirmModal, indexPatterns: deps.npDataStart.indexPatterns, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 4da445166df45..e85054cd7fb34 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -23,7 +23,7 @@ import React from 'react'; import angular from 'angular'; import { Subscription } from 'rxjs'; -import { createHashHistory } from 'history'; +import { map } from 'rxjs/operators'; import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen'; import { @@ -32,16 +32,17 @@ import { SavedObjectSaveOpts, SaveResult, showSaveModal, - State, subscribeWithScope, } from '../legacy_imports'; -import { FilterStateManager } from '../../../../data/public'; import { - esFilters, + COMPARE_ALL_OPTIONS, + compareFilters, IndexPattern, IndexPatternsContract, Query, SavedQuery, + syncAppFilters, + syncQuery, } from '../../../../../../plugins/data/public'; import { @@ -82,7 +83,6 @@ export interface DashboardAppControllerDependencies extends RenderDeps { $scope: DashboardAppScope; $route: any; $routeParams: any; - globalState: State; indexPatterns: IndexPatternsContract; dashboardConfig: any; config: any; @@ -99,7 +99,6 @@ export class DashboardAppController { $scope, $route, $routeParams, - globalState, dashboardConfig, localStorage, indexPatterns, @@ -109,15 +108,21 @@ export class DashboardAppController { embeddables, share, dashboardCapabilities, - npDataStart: { - query: { - filterManager, - timefilter: { timefilter }, - }, - }, + npDataStart: { query: queryService }, core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects, http }, + history, + kbnUrlStateStorage, }: DashboardAppControllerDependencies) { + const filterManager = queryService.filterManager; const queryFilter = filterManager; + const timefilter = queryService.timefilter.timefilter; + + // starts syncing `_g` portion of url with query services + // note: dashboard_state_manager.ts syncs `_a` portion of url + const { + stop: stopSyncingGlobalStateWithUrl, + hasInheritedQueryFromUrl: hasInheritedGlobalStateFromUrl, + } = syncQuery(queryService, kbnUrlStateStorage); let lastReloadRequestTime = 0; @@ -126,34 +131,23 @@ export class DashboardAppController { chrome.docTitle.change(dash.title); } - const history = createHashHistory(); const dashboardStateManager = new DashboardStateManager({ savedDashboard: dash, - useHashedUrl: config.get('state:storeInSessionStorage'), hideWriteControls: dashboardConfig.getHideWriteControls(), kibanaVersion: injectedMetadata.getKibanaVersion(), + kbnUrlStateStorage, history, }); - const filterStateManager = new FilterStateManager( - globalState, - () => { - // Temporary AppState replacement - return { - set filters(_filters: esFilters.Filter[]) { - dashboardStateManager.setFilters(_filters); - }, - get filters() { - return dashboardStateManager.appState.filters; - }, - }; - }, - filterManager - ); + const stopSyncingAppFilters = syncAppFilters(filterManager, { + set: filters => dashboardStateManager.setFilters(filters), + get: () => dashboardStateManager.appState.filters, + state$: dashboardStateManager.appState$.pipe(map(state => state.filters)), + }); // The hash check is so we only update the time filter on dashboard open, not during // normal cross app navigation. - if (dashboardStateManager.getIsTimeSavedWithDashboard() && !globalState.$inheritedGlobalState) { + if (dashboardStateManager.getIsTimeSavedWithDashboard() && !hasInheritedGlobalStateFromUrl) { dashboardStateManager.syncTimefilterWithDashboard(timefilter); } $scope.showSaveQuery = dashboardCapabilities.saveQuery as boolean; @@ -316,8 +310,14 @@ export class DashboardAppController { // This has to be first because handleDashboardContainerChanges causes // appState.save which will cause refreshDashboardContainer to be called. - // Add filters modifies the object passed to it, hence the clone deep. - if (!_.isEqual(container.getInput().filters, queryFilter.getFilters())) { + if ( + !compareFilters( + container.getInput().filters, + queryFilter.getFilters(), + COMPARE_ALL_OPTIONS + ) + ) { + // Add filters modifies the object passed to it, hence the clone deep. queryFilter.addFilters(_.cloneDeep(container.getInput().filters)); dashboardStateManager.applyFilters($scope.model.query, container.getInput().filters); @@ -411,19 +411,27 @@ export class DashboardAppController { const containerInput = dashboardContainer.getInput(); const differences: Partial = {}; - Object.keys(containerInput).forEach(key => { + + // Filters shouldn't be compared using regular isEqual + if ( + !compareFilters(containerInput.filters, appStateDashboardInput.filters, COMPARE_ALL_OPTIONS) + ) { + differences.filters = appStateDashboardInput.filters; + } + + Object.keys(_.omit(containerInput, 'filters')).forEach(key => { const containerValue = (containerInput as { [key: string]: unknown })[key]; const appStateValue = ((appStateDashboardInput as unknown) as { [key: string]: unknown })[ key ]; if (!_.isEqual(containerValue, appStateValue)) { - // cloneDeep hack is needed, as there are multiple place, where container's input mutated, - // but values from appStateValue are deeply frozen, as they can't be mutated directly - (differences as { [key: string]: unknown })[key] = _.cloneDeep(appStateValue); + (differences as { [key: string]: unknown })[key] = appStateValue; } }); - return Object.values(differences).length === 0 ? undefined : differences; + // cloneDeep hack is needed, as there are multiple place, where container's input mutated, + // but values from appStateValue are deeply frozen, as they can't be mutated directly + return Object.values(differences).length === 0 ? undefined : _.cloneDeep(differences); }; const refreshDashboardContainer = () => { @@ -878,6 +886,8 @@ export class DashboardAppController { $scope.$on('$destroy', () => { updateSubscription.unsubscribe(); + stopSyncingGlobalStateWithUrl(); + stopSyncingAppFilters(); visibleSubscription.unsubscribe(); $scope.timefilterSubscriptions$.unsubscribe(); @@ -891,9 +901,6 @@ export class DashboardAppController { if (dashboardContainer) { dashboardContainer.destroy(); } - if (filterStateManager) { - filterStateManager.destroy(); - } }); } } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts index 8806684aab13c..152cd84b7c38d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts @@ -23,6 +23,7 @@ import { DashboardStateManager } from './dashboard_state_manager'; import { getSavedDashboardMock } from '../__tests__'; import { InputTimeRange, TimefilterContract, TimeRange } from 'src/plugins/data/public'; import { ViewMode } from 'src/plugins/embeddable/public'; +import { createKbnUrlStateStorage } from 'src/plugins/kibana_utils/public'; jest.mock('ui/agg_types', () => ({ aggTypes: { @@ -48,9 +49,9 @@ describe('DashboardState', function() { function initDashboardState() { dashboardState = new DashboardStateManager({ savedDashboard, - useHashedUrl: false, hideWriteControls: false, kibanaVersion: '7.0.0', + kbnUrlStateStorage: createKbnUrlStateStorage(), history: createBrowserHistory(), }); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts index 451e7c8ff96db..987afd65bb67b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts @@ -19,9 +19,9 @@ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { History } from 'history'; -import { Subscription } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { Moment } from 'moment'; +import { History } from 'history'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; import { ViewMode } from '../../../../../../plugins/embeddable/public'; @@ -44,7 +44,6 @@ import { SavedDashboardPanel, } from './types'; import { - createKbnUrlStateStorage, createStateContainer, IKbnUrlStateStorage, ISyncStateRef, @@ -76,6 +75,10 @@ export class DashboardStateManager { return this.stateContainer.get(); } + public get appState$(): Observable { + return this.stateContainer.state$; + } + private readonly stateContainer: ReduxLikeStateContainer< DashboardAppState, DashboardAppStateTransitions @@ -97,13 +100,13 @@ export class DashboardStateManager { savedDashboard, hideWriteControls, kibanaVersion, - useHashedUrl, + kbnUrlStateStorage, history, }: { savedDashboard: SavedObjectDashboard; hideWriteControls: boolean; kibanaVersion: string; - useHashedUrl: boolean; + kbnUrlStateStorage: IKbnUrlStateStorage; history: History; }) { this.history = history; @@ -117,7 +120,7 @@ export class DashboardStateManager { kibanaVersion ); - this.kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: useHashedUrl, history }); + this.kbnUrlStateStorage = kbnUrlStateStorage; // setup initial state by merging defaults with state from url // also run migration, as state in url could be of older version diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/global_state_sync.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/global_state_sync.ts deleted file mode 100644 index 1a6c2b09ee3fc..0000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/global_state_sync.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { State } from '../legacy_imports'; -import { DataPublicPluginStart as NpDataStart } from '../../../../../../plugins/data/public'; - -/** - * Helper function to sync the global state with the various state providers - * when a local angular application mounts. There are three different ways - * global state can be passed into the application: - * * parameter in the URL hash - e.g. shared link - * * in-memory state in the data plugin exports (timefilter and filterManager) - e.g. default values - * - * This function looks up the three sources (earlier in the list means it takes precedence), - * puts it into the globalState object and syncs it with the url. - * - * Currently the legacy chrome takes care of restoring the global state when navigating from - * one app to another - to migrate away from that it will become necessary to also write the current - * state to local storage - */ -export function syncOnMount( - globalState: State, - { - query: { - filterManager, - timefilter: { timefilter }, - }, - }: NpDataStart -) { - // pull in global state information from the URL - globalState.fetch(); - // remember whether there were info in the URL - const hasGlobalURLState = Boolean(Object.keys(globalState.toObject()).length); - - // sync kibana platform state with the angular global state - if (!globalState.time) { - globalState.time = timefilter.getTime(); - } - if (!globalState.refreshInterval) { - globalState.refreshInterval = timefilter.getRefreshInterval(); - } - if (!globalState.filters && filterManager.getGlobalFilters().length > 0) { - globalState.filters = filterManager.getGlobalFilters(); - } - // only inject cross app global state if there is none in the url itself (that takes precedence) - if (hasGlobalURLState) { - // set flag the global state is set from the URL - globalState.$inheritedGlobalState = true; - } - globalState.save(); -} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js index 7dc408ea4b801..7ba404d52d9a6 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js @@ -22,20 +22,16 @@ import { i18n } from '@kbn/i18n'; import dashboardTemplate from './dashboard_app.html'; import dashboardListingTemplate from './listing/dashboard_listing_ng_wrapper.html'; -import { - ensureDefaultIndexPattern, - registerTimefilterWithGlobalStateFactory, -} from '../legacy_imports'; +import { ensureDefaultIndexPattern } from '../legacy_imports'; import { initDashboardAppDirective } from './dashboard_app'; -import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; +import { createDashboardEditUrl, DashboardConstants } from './dashboard_constants'; import { InvalidJSONProperty, SavedObjectNotFound, } from '../../../../../../plugins/kibana_utils/public'; import { DashboardListing, EMPTY_FILTER } from './listing/dashboard_listing'; import { addHelpMenuToAppChrome } from './help_menu/help_menu_util'; -import { syncOnMount } from './global_state_sync'; -import { createHashHistory } from 'history'; +import { syncQuery } from '../../../../../../plugins/data/public'; export function initDashboardApp(app, deps) { initDashboardAppDirective(app, deps); @@ -61,16 +57,9 @@ export function initDashboardApp(app, deps) { addHelpMenuToAppChrome(deps.chrome, deps.core.docLinks); } - app.run(globalState => { - syncOnMount(globalState, deps.npDataStart); - }); - - app.run((globalState, $rootScope) => { - registerTimefilterWithGlobalStateFactory( - deps.npDataStart.query.timefilter.timefilter, - globalState, - $rootScope - ); + app.config(stateManagementConfigProvider => { + // Dashboard state management is handled by state containers and state_sync utilities + stateManagementConfigProvider.disable(); }); app.config(function($routeProvider) { @@ -100,10 +89,15 @@ export function initDashboardApp(app, deps) { template: dashboardListingTemplate, controller($injector, $location, $scope) { const service = deps.savedDashboards; - const kbnUrl = $injector.get('kbnUrl'); const dashboardConfig = deps.dashboardConfig; + // syncs `_g` portion of url with query services + const { stop: stopSyncingGlobalStateWithUrl } = syncQuery( + deps.npDataStart.query, + deps.kbnUrlStateStorage + ); + $scope.listingLimit = deps.uiSettings.get('savedObjects:listingLimit'); $scope.create = () => { kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL); @@ -131,6 +125,10 @@ export function initDashboardApp(app, deps) { ]); addHelpMenuToAppChrome(deps.chrome, deps.core.docLinks); $scope.core = deps.core; + + $scope.$on('$destroy', () => { + stopSyncingGlobalStateWithUrl(); + }); }, resolve: { dash: function($rootScope, $route, redirectWhenMissing, kbnUrl) { @@ -218,7 +216,7 @@ export function initDashboardApp(app, deps) { // See https://github.com/elastic/kibana/issues/10951 for more context. if (error instanceof SavedObjectNotFound && id === 'create') { // Note preserve querystring part is necessary so the state is preserved through the redirect. - const history = createHashHistory(); + const history = deps.history; history.replace({ ...history.location, // preserve query, pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts index ca4b18a37504c..732fbd525ae37 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts @@ -25,12 +25,13 @@ import { Plugin, SavedObjectsClientContract, } from 'kibana/public'; +import { createHashHistory } from 'history'; import { i18n } from '@kbn/i18n'; import { RenderDeps } from './np_ready/application'; import { DataStart } from '../../../data/public'; import { DataPublicPluginStart as NpDataStart } from '../../../../../plugins/data/public'; import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; -import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { createKbnUrlStateStorage, Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { DashboardConstants } from './np_ready/dashboard_constants'; import { @@ -96,6 +97,12 @@ export class DashboardPlugin implements Plugin { overlays: contextCore.overlays, }); + const history = createHashHistory(); + const kbnUrlStateStorage = createKbnUrlStateStorage({ + history, + useHash: core.uiSettings.get('state:storeInSessionStorage'), + }); + const deps: RenderDeps = { core: contextCore as LegacyCoreStart, ...angularDependencies, @@ -111,6 +118,8 @@ export class DashboardPlugin implements Plugin { embeddables, dashboardCapabilities: contextCore.application.capabilities.dashboard, localStorage: new Storage(localStorage), + history, + kbnUrlStateStorage, }; const { renderApp } = await import('./np_ready/application'); return renderApp(params.element, params.appBasePath, deps); diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index 1cefabe08c2d5..fea834686eb4f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -31,8 +31,6 @@ import { EventsProvider } from 'ui/events'; import { PersistedState } from 'ui/persisted_state'; // @ts-ignore import { PromiseServiceCreator } from 'ui/promises/promises'; -// @ts-ignore -import { createEsService } from 'ui/es'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; // @ts-ignore import { PrivateProvider } from 'ui/private/private'; @@ -66,7 +64,7 @@ import { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; // @ts-ignore import { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; import { configureAppAngularModule } from 'ui/legacy_compat'; -import { IndexPatterns } from '../../../../../plugins/data/public'; +import { IndexPatterns, DataPublicPluginStart } from '../../../../../plugins/data/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { createDocTableDirective } from './np_ready/angular/doc_table/doc_table'; @@ -94,7 +92,7 @@ import { DiscoverStartPlugins } from './plugin'; * needs to render, so in the end the current 'kibana' angular module is no longer necessary */ export function getInnerAngularModule(name: string, core: CoreStart, deps: DiscoverStartPlugins) { - const module = initializeInnerAngularModule(name, core, deps.navigation); + const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data); configureAppAngularModule(module, core as LegacyCoreStart, true); return module; } @@ -107,7 +105,7 @@ export function getInnerAngularModuleEmbeddable( core: CoreStart, deps: DiscoverStartPlugins ) { - const module = initializeInnerAngularModule(name, core, deps.navigation, true); + const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data, true); configureAppAngularModule(module, core as LegacyCoreStart, true); return module; } @@ -118,6 +116,7 @@ export function initializeInnerAngularModule( name = 'app/discover', core: CoreStart, navigation: NavigationStart, + data: DataPublicPluginStart, embeddable = false ) { if (!initialized) { @@ -131,7 +130,7 @@ export function initializeInnerAngularModule( createLocalGlobalStateModule(); createLocalAppStateModule(); createLocalStorageModule(); - createElasticSearchModule(); + createElasticSearchModule(data); createIndexPatternsModule(); createPagerFactoryModule(); createDocTableModule(); @@ -163,7 +162,6 @@ export function initializeInnerAngularModule( 'ngRoute', 'react', 'ui.bootstrap', - 'elasticsearch', 'discoverConfig', 'discoverI18n', 'discoverPrivate', @@ -298,11 +296,13 @@ const createLocalStorageService = function(type: string) { }; }; -function createElasticSearchModule() { +function createElasticSearchModule(data: DataPublicPluginStart) { angular - .module('discoverEs', ['elasticsearch', 'discoverConfig']) + .module('discoverEs', ['discoverConfig']) // Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy - .service('es', createEsService); + .service('es', () => { + return data.search.__LEGACY.esClient; + }); } function createIndexPatternsModule() { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js index 754fda2a6fe5a..d1087b4575e82 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js @@ -17,7 +17,6 @@ * under the License. */ -import { SavedObjectsClientProvider } from 'ui/saved_objects'; import uiRoutes from 'ui/routes'; import angularTemplate from './angular_template.html'; import { npStart } from 'ui/new_platform'; @@ -32,18 +31,17 @@ uiRoutes.when('/management/kibana/index_pattern', { controller: function($scope, $injector) { // Wait for the directives to execute const kbnUrl = $injector.get('kbnUrl'); - const Private = $injector.get('Private'); $scope.$$postDigest(() => { const $routeParams = $injector.get('$routeParams'); const indexPatternCreationType = managementSetup.indexPattern.creation.getType( $routeParams.type ); const services = { - config: $injector.get('config'), - es: $injector.get('es'), + config: npStart.core.uiSettings, + es: npStart.plugins.data.search.__LEGACY.esClient, indexPatterns: npStart.plugins.data.indexPatterns, - $http: $injector.get('$http'), - savedObjectsClient: Private(SavedObjectsClientProvider), + $http: npStart.core.http, + savedObjectsClient: npStart.core.savedObjects.client, indexPatternCreationType, confirmModalPromise: $injector.get('confirmModalPromise'), changeUrl: url => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js index a663e9d626f88..8ab26f8c0d1c8 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js @@ -26,7 +26,7 @@ import { uiModules } from 'ui/modules'; import indexTemplate from './index.html'; import indexPatternListTemplate from './list.html'; import { IndexPatternTable } from './index_pattern_table'; -import { SavedObjectsClientProvider } from 'ui/saved_objects'; +import { npStart } from 'ui/new_platform'; import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory, @@ -67,8 +67,8 @@ export const destroyIndexPatternList = () => { }; const indexPatternsResolutions = { - indexPatterns: function(Private) { - const savedObjectsClient = Private(SavedObjectsClientProvider); + indexPatterns: function() { + const savedObjectsClient = npStart.core.savedObjects.client; return savedObjectsClient .find({ diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js index 6d3980d9d53f9..c9698f6e1f48b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js @@ -21,7 +21,6 @@ import { savedObjectManagementRegistry } from '../../saved_object_registry'; import objectIndexHTML from './_objects.html'; import uiRoutes from 'ui/routes'; import chrome from 'ui/chrome'; -import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { uiModules } from 'ui/modules'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; @@ -35,14 +34,13 @@ import { getIndexBreadcrumbs } from './breadcrumbs'; const REACT_OBJECTS_TABLE_DOM_ELEMENT_ID = 'reactSavedObjectsTable'; function updateObjectsTable($scope, $injector) { - const Private = $injector.get('Private'); const indexPatterns = npStart.plugins.data.indexPatterns; const $http = $injector.get('$http'); const kbnUrl = $injector.get('kbnUrl'); const config = $injector.get('config'); const confirmModalPromise = $injector.get('confirmModalPromise'); - const savedObjectsClient = Private(SavedObjectsClientProvider); + const savedObjectsClient = npStart.core.savedObjects.client; const services = savedObjectManagementRegistry.all().map(obj => $injector.get(obj.service)); const uiCapabilites = npStart.core.application.capabilities; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js index f7e654fd3c76d..540f25c63a861 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js @@ -28,9 +28,9 @@ import uiRoutes from 'ui/routes'; import { uiModules } from 'ui/modules'; import { fatalError, toastNotifications } from 'ui/notify'; import 'ui/accessibility/kbn_ui_ace_keyboard_mode'; -import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { isNumeric } from './lib/numeric'; import { canViewInApp } from './lib/in_app_url'; +import { npStart } from 'ui/new_platform'; import { castEsToKbnFieldTypeName } from '../../../../../../../plugins/data/public'; @@ -56,12 +56,11 @@ uiModules $location, $window, $rootScope, - Private, uiCapabilities ) { const serviceObj = savedObjectManagementRegistry.get($routeParams.service); const service = $injector.get(serviceObj.service); - const savedObjectsClient = Private(SavedObjectsClientProvider); + const savedObjectsClient = npStart.core.savedObjects.client; /** * Creates a field definition and pushes it to the memo stack. This function @@ -177,11 +176,13 @@ uiModules // sorts twice since we want numerical sort to prioritize over name, // and sortBy will do string comparison if trying to match against strings const nameSortedFields = _.sortBy(fields, 'name'); - $scope.fields = _.sortBy(nameSortedFields, field => { - const orderIndex = service.Class.fieldOrder - ? service.Class.fieldOrder.indexOf(field.name) - : -1; - return orderIndex > -1 ? orderIndex : Infinity; + $scope.$evalAsync(() => { + $scope.fields = _.sortBy(nameSortedFields, field => { + const orderIndex = service.Class.fieldOrder + ? service.Class.fieldOrder.indexOf(field.name) + : -1; + return orderIndex > -1 ? orderIndex : Infinity; + }); }); }) .catch(error => fatalError(error, location)); diff --git a/src/legacy/core_plugins/kibana/server/lib/system_api.js b/src/legacy/core_plugins/kibana/server/lib/system_api.js index 24c901a3db0a4..3e2ab667dd98b 100644 --- a/src/legacy/core_plugins/kibana/server/lib/system_api.js +++ b/src/legacy/core_plugins/kibana/server/lib/system_api.js @@ -24,6 +24,7 @@ const SYSTEM_API_HEADER_NAME = 'kbn-system-api'; * * @param request HAPI request object * @return true if request is a system API request; false, otherwise + * @deprecated Use KibanaRequest#isSystemApi */ export function isSystemApiRequest(request) { return !!request.headers[SYSTEM_API_HEADER_NAME]; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/markdown_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/markdown_editor.js index c1d767656f451..21fd9d530558c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/markdown_editor.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/markdown_editor.js @@ -134,7 +134,7 @@ export class MarkdownEditor extends Component { values={{ handlebarLink: ( diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index 0c12b3d928815..60f1bed35b518 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -43,6 +43,7 @@ import { SearchCache } from '../data_model/search_cache'; import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; import { createVegaTypeDefinition } from '../vega_type'; +import { npStart } from 'ui/new_platform'; const THRESHOLD = 0.1; const PIXEL_DIFF = 30; @@ -60,10 +61,9 @@ describe('VegaVisualizations', () => { beforeEach( ngMock.inject((Private, $injector) => { vegaVisualizationDependencies = { - es: $injector.get('es'), serviceSettings: $injector.get('serviceSettings'), core: { - uiSettings: $injector.get('config'), + uiSettings: npStart.core.uiSettings, }, plugins: { data: { @@ -72,6 +72,9 @@ describe('VegaVisualizations', () => { timefilter: {}, }, }, + __LEGACY: { + esClient: npStart.plugins.data.search.__LEGACY.esClient, + }, }, }, }; diff --git a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts index af4425e3d5548..5cf65d62a6aed 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts @@ -19,12 +19,10 @@ import chrome from 'ui/chrome'; import 'ui/vis/map/service_settings'; -import 'ui/es'; // required for $injector.get('es') below import { CoreStart, Plugin } from 'kibana/public'; /** @internal */ export interface LegacyDependenciesPluginSetup { - es: any; serviceSettings: any; } @@ -34,9 +32,6 @@ export class LegacyDependenciesPlugin const $injector = await chrome.dangerouslyGetActiveInjector(); return { - // Client of Elastic Search. - es: $injector.get('es'), - // Settings for EMSClient. // EMSClient, which currently lives in the tile_map vis, // will probably end up being exposed from the future vis_type_maps plugin, diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts index 089cc3215d87d..576786567a6f9 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts @@ -16,6 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getSearchService } from '../../../../plugins/data/public/services'; import { esFilters, esQuery, TimeRange, Query } from '../../../../plugins/data/public'; // @ts-ignore @@ -36,13 +39,13 @@ interface VegaRequestHandlerParams { } export function createVegaRequestHandler({ - es, - plugins, + plugins: { data }, core: { uiSettings }, serviceSettings, }: VegaVisualizationDependencies) { - const searchCache = new SearchCache(es, { max: 10, maxAge: 4 * 1000 }); - const { timefilter } = plugins.data.query.timefilter; + const { esClient } = getSearchService().__LEGACY; + const searchCache = new SearchCache(esClient, { max: 10, maxAge: 4 * 1000 }); + const { timefilter } = data.query.timefilter; const timeCache = new TimeCache(timefilter, 3 * 1000); return ({ timeRange, filters, query, visParams }: VegaRequestHandlerParams) => { diff --git a/src/legacy/ui/public/autoload/modules.js b/src/legacy/ui/public/autoload/modules.js index e1d897236297e..938796ed279ea 100644 --- a/src/legacy/ui/public/autoload/modules.js +++ b/src/legacy/ui/public/autoload/modules.js @@ -20,7 +20,6 @@ import 'angular'; import '../chrome'; import '../config'; -import '../es'; import '../notify'; import '../private'; import '../promises'; diff --git a/src/legacy/ui/public/es.js b/src/legacy/ui/public/es.js deleted file mode 100644 index c734717b82438..0000000000000 --- a/src/legacy/ui/public/es.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * @name es - * - * @description This is the result of calling esFactory. esFactory is exposed by the - * elasticsearch.angular.js client. - */ - -import 'elasticsearch-browser'; -import _ from 'lodash'; -import { uiModules } from './modules'; - -const plugins = [ - function(Client, config) { - // esFactory automatically injects the AngularConnector to the config - // https://github.com/elastic/elasticsearch-js/blob/master/src/lib/connectors/angular.js - class CustomAngularConnector extends config.connectionClass { - request = _.wrap(this.request, function(request, params, cb) { - if (String(params.method).toUpperCase() === 'GET') { - params.query = _.defaults({ _: Date.now() }, params.query); - } - - return request.call(this, params, cb); - }); - } - - config.connectionClass = CustomAngularConnector; - }, -]; - -export function createEsService(esFactory, esUrl, esApiVersion, esRequestTimeout) { - return esFactory({ - host: esUrl, - log: 'info', - requestTimeout: esRequestTimeout, - apiVersion: esApiVersion, - plugins, - }); -} - -uiModules - .get('kibana', ['elasticsearch', 'kibana/config']) - //Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy - .service('es', createEsService); diff --git a/src/legacy/ui/public/kfetch/kfetch.ts b/src/legacy/ui/public/kfetch/kfetch.ts index cb96e03eb1328..02be7a32db296 100644 --- a/src/legacy/ui/public/kfetch/kfetch.ts +++ b/src/legacy/ui/public/kfetch/kfetch.ts @@ -32,6 +32,7 @@ export interface KFetchQuery { export interface KFetchOptions extends HttpRequestInit { pathname: string; query?: KFetchQuery; + asSystemRequest?: boolean; } export interface KFetchKibanaOptions { diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index dfd26bc4be039..6f4b20c425fc9 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -102,6 +102,12 @@ export const npSetup = { getSavedQueryCount: sinon.fake(), }, }, + __LEGACY: { + esClient: { + search: sinon.fake(), + msearch: sinon.fake(), + }, + }, fieldFormats: getFieldFormatsRegistry(mockCore), }, share: { diff --git a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js index d9622ac3dc6d2..ace87a15f7b58 100644 --- a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js +++ b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js @@ -34,7 +34,6 @@ describe('Saved Object', function() { require('test_utils/no_digest_promises').activateForSuite(); let SavedObject; - let esDataStub; let savedObjectsClientStub; let window; @@ -90,10 +89,6 @@ describe('Saved Object', function() { obj[fName].restore && obj[fName].restore(); } - const mock409FetchError = { - res: { status: 409 }, - }; - beforeEach( ngMock.module( 'kibana', @@ -108,10 +103,9 @@ describe('Saved Object', function() { ); beforeEach( - ngMock.inject(function(es, $window) { + ngMock.inject(function($window) { savedObjectsClientStub = npStart.core.savedObjects.client; SavedObject = createSavedObjectClass({ savedObjectsClient: savedObjectsClientStub }); - esDataStub = es; window = $window; }) ); @@ -130,7 +124,6 @@ describe('Saved Object', function() { describe('with confirmOverwrite', function() { function stubConfirmOverwrite() { window.confirm = sinon.stub().returns(true); - sinon.stub(esDataStub, 'create').returns(Bluebird.reject(mock409FetchError)); } it('when false does not request overwrite', function() { diff --git a/src/legacy/ui/public/saved_objects/index.ts b/src/legacy/ui/public/saved_objects/index.ts index 129938ebe0509..03c5a64fdd45e 100644 --- a/src/legacy/ui/public/saved_objects/index.ts +++ b/src/legacy/ui/public/saved_objects/index.ts @@ -17,6 +17,5 @@ * under the License. */ -export { SavedObjectsClientProvider } from './saved_objects_client_provider'; export { SavedObjectLoader } from './saved_object_loader'; export { findObjectByTitle } from './helpers/find_object_by_title'; diff --git a/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts b/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts deleted file mode 100644 index 0375eb21c19f0..0000000000000 --- a/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { SavedObjectsClient } from 'src/core/public'; -import chrome from '../chrome'; -import { PromiseService } from '../promises'; - -type Args any> = T extends (...args: infer X) => any ? X : never; - -// Provide an angular wrapper around savedObjectClient so all actions get resolved in an Angular Promise -// If you do not need the promise to execute in an angular digest cycle then you should not use this -// and get savedObjectClient directly from chrome. -export function SavedObjectsClientProvider(Promise: PromiseService) { - const savedObjectsClient = chrome.getSavedObjectsClient(); - - return { - create: (...args: Args) => { - return Promise.resolve(savedObjectsClient.create(...args)); - }, - bulkCreate: (...args: Args) => { - return Promise.resolve(savedObjectsClient.bulkCreate(...args)); - }, - delete: (...args: Args) => { - return Promise.resolve(savedObjectsClient.delete(...args)); - }, - find: (...args: Args) => { - return Promise.resolve(savedObjectsClient.find(...args)); - }, - get: (...args: Args) => { - return Promise.resolve(savedObjectsClient.get(...args)); - }, - bulkGet: (...args: Args) => { - return Promise.resolve(savedObjectsClient.bulkGet(...args)); - }, - update: (...args: Args) => { - return Promise.resolve(savedObjectsClient.update(...args)); - }, - }; -} diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js index a62ebdc25ca2c..1cb23d2ad2a23 100644 --- a/src/legacy/ui/ui_exports/ui_export_defaults.js +++ b/src/legacy/ui/ui_exports/ui_export_defaults.js @@ -47,6 +47,5 @@ export const UI_EXPORT_DEFAULTS = { appExtensions: { fieldFormatEditors: ['ui/field_editor/components/field_format_editor/register'], - search: ['ui/courier/search_strategy/default_search_strategy'], }, }; diff --git a/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx index 36313353e3c33..7b3842bd33dbd 100644 --- a/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx @@ -76,7 +76,7 @@ export class ReplacePanelFlyout extends React.Component { // add the new view const newObj = await this.props.container.addSavedObjectEmbeddable(type, id); - const finalPanels = this.props.container.getInput().panels; + const finalPanels = _.cloneDeep(this.props.container.getInput().panels); (finalPanels[newObj.id] as DashboardPanelState).gridData.w = nnw; (finalPanels[newObj.id] as DashboardPanelState).gridData.h = nnh; (finalPanels[newObj.id] as DashboardPanelState).gridData.x = nnx; @@ -86,7 +86,7 @@ export class ReplacePanelFlyout extends React.Component { delete finalPanels[this.props.panelToRemove.id]; // apply changes - this.props.container.updateInput(finalPanels); + this.props.container.updateInput({ panels: finalPanels }); this.props.container.reload(); this.showToast(name); diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index f44d40f533eed..0f1c9eeb9d11a 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -59,6 +59,12 @@ const createSetupContract = (): Setup => { search: searchSetupMock, fieldFormats: fieldFormatsMock as FieldFormatsSetup, query: querySetupMock, + __LEGACY: { + esClient: { + search: jest.fn(), + msearch: jest.fn(), + }, + }, }; return setupContract; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index ce8ca0317bd7d..2077e899d1a01 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -76,7 +76,7 @@ export class DataPublicPlugin implements Plugin { }).length ).toBe(3); }); + + test('should set app filters and remove any duplicated global filters', async function() { + filterManager.addFilters(readyFilters, true); + const appFilter1 = _.cloneDeep(readyFilters[1]); + const appFilter2 = _.cloneDeep(readyFilters[2]); + + filterManager.setAppFilters([appFilter1, appFilter2]); + + const newGlobalFilters = filterManager.getGlobalFilters(); + const newAppFilters = filterManager.getAppFilters(); + + expect(newGlobalFilters).toHaveLength(1); + expect(newAppFilters).toHaveLength(2); + }); + + test('should set global filters and remove any duplicated app filters', async function() { + filterManager.addFilters(readyFilters, false); + const globalFilter1 = _.cloneDeep(readyFilters[1]); + const globalFilter2 = _.cloneDeep(readyFilters[2]); + + filterManager.setGlobalFilters([globalFilter1, globalFilter2]); + + const newGlobalFilters = filterManager.getGlobalFilters(); + const newAppFilters = filterManager.getAppFilters(); + + expect(newGlobalFilters).toHaveLength(2); + expect(newAppFilters).toHaveLength(1); + }); }); describe('add filters', () => { diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index 18bb619f6a137..6c5cdbaffce5e 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -22,7 +22,7 @@ import { Subject } from 'rxjs'; import { IUiSettingsClient } from 'src/core/public'; -import { compareFilters, COMPARE_ALL_OPTIONS } from './lib/compare_filters'; +import { COMPARE_ALL_OPTIONS, compareFilters } from './lib/compare_filters'; import { sortFilters } from './lib/sort_filters'; import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; import { uniqFilters } from './lib/uniq_filters'; @@ -164,6 +164,48 @@ export class FilterManager { this.handleStateUpdate(mergedFilters); } + /** + * Sets new global filters and leaves app filters untouched, + * Removes app filters for which there is a duplicate within new global filters + * @param newGlobalFilters + */ + public setGlobalFilters(newGlobalFilters: esFilters.Filter[]) { + newGlobalFilters = mapAndFlattenFilters(newGlobalFilters); + FilterManager.setFiltersStore(newGlobalFilters, esFilters.FilterStateStore.GLOBAL_STATE, true); + const { appFilters: currentAppFilters } = this.getPartitionedFilters(); + // remove duplicates from current app filters, to make sure global will take precedence + const filteredAppFilters = currentAppFilters.filter( + appFilter => !newGlobalFilters.find(globalFilter => compareFilters(globalFilter, appFilter)) + ); + const newFilters = this.mergeIncomingFilters({ + appFilters: filteredAppFilters, + globalFilters: newGlobalFilters, + }); + + this.handleStateUpdate(newFilters); + } + + /** + * Sets new app filters and leaves global filters untouched, + * Removes app filters for which there is a duplicate within new global filters + * @param newAppFilters + */ + public setAppFilters(newAppFilters: esFilters.Filter[]) { + newAppFilters = mapAndFlattenFilters(newAppFilters); + FilterManager.setFiltersStore(newAppFilters, esFilters.FilterStateStore.APP_STATE, true); + const { globalFilters: currentGlobalFilters } = this.getPartitionedFilters(); + // remove duplicates from current global filters, to make sure app will take precedence + const filteredGlobalFilters = currentGlobalFilters.filter( + globalFilter => !newAppFilters.find(appFilter => compareFilters(appFilter, globalFilter)) + ); + + const newFilters = this.mergeIncomingFilters({ + globalFilters: filteredGlobalFilters, + appFilters: newAppFilters, + }); + this.handleStateUpdate(newFilters); + } + public removeFilter(filter: esFilters.Filter) { const filterIndex = _.findIndex(this.filters, item => { return _.isEqual(item.meta, filter.meta) && _.isEqual(item.query, filter.query); @@ -180,10 +222,15 @@ export class FilterManager { this.setFilters([]); } - public static setFiltersStore(filters: esFilters.Filter[], store: esFilters.FilterStateStore) { + public static setFiltersStore( + filters: esFilters.Filter[], + store: esFilters.FilterStateStore, + shouldOverrideStore = false + ) { _.map(filters, (filter: esFilters.Filter) => { // Override status only for filters that didn't have state in the first place. - if (filter.$state === undefined) { + // or if shouldOverrideStore is explicitly true + if (shouldOverrideStore || filter.$state === undefined) { filter.$state = { store }; } }); diff --git a/src/plugins/data/public/query/filter_manager/index.ts b/src/plugins/data/public/query/filter_manager/index.ts index ce7a479151797..09990adacde45 100644 --- a/src/plugins/data/public/query/filter_manager/index.ts +++ b/src/plugins/data/public/query/filter_manager/index.ts @@ -23,3 +23,4 @@ export { uniqFilters } from './lib/uniq_filters'; export { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; export { onlyDisabledFiltersChanged } from './lib/only_disabled'; export { generateFilters } from './lib/generate_filters'; +export { compareFilters, COMPARE_ALL_OPTIONS } from './lib/compare_filters'; diff --git a/src/plugins/data/public/query/index.tsx b/src/plugins/data/public/query/index.tsx index dd3d11b4ac25c..35e58575d8870 100644 --- a/src/plugins/data/public/query/index.tsx +++ b/src/plugins/data/public/query/index.tsx @@ -24,3 +24,4 @@ export * from './filter_manager'; export * from './timefilter'; export * from './saved_query'; export * from './persisted_log'; +export * from './state_sync'; diff --git a/src/legacy/ui/public/courier/search_strategy/default_search_strategy.ts b/src/plugins/data/public/query/state_sync/index.ts similarity index 84% rename from src/legacy/ui/public/courier/search_strategy/default_search_strategy.ts rename to src/plugins/data/public/query/state_sync/index.ts index 55dee19cae32a..7eefda0d0aec1 100644 --- a/src/legacy/ui/public/courier/search_strategy/default_search_strategy.ts +++ b/src/plugins/data/public/query/state_sync/index.ts @@ -17,8 +17,5 @@ * under the License. */ -import { addSearchStrategy, defaultSearchStrategy } from '../index'; - -addSearchStrategy(defaultSearchStrategy); - -export { defaultSearchStrategy }; +export { syncQuery } from './sync_query'; +export { syncAppFilters } from './sync_app_filters'; diff --git a/src/plugins/data/public/query/state_sync/sync_app_filters.test.ts b/src/plugins/data/public/query/state_sync/sync_app_filters.test.ts new file mode 100644 index 0000000000000..61270ecc09979 --- /dev/null +++ b/src/plugins/data/public/query/state_sync/sync_app_filters.test.ts @@ -0,0 +1,197 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subscription } from 'rxjs'; +import { FilterManager } from '../filter_manager'; +import { getFilter } from '../filter_manager/test_helpers/get_stub_filter'; +import { esFilters } from '../../../common'; +import { syncAppFilters } from './sync_app_filters'; +import { coreMock } from '../../../../../core/public/mocks'; +import { BaseStateContainer, createStateContainer } from '../../../../kibana_utils/public'; + +const setupMock = coreMock.createSetup(); + +setupMock.uiSettings.get.mockImplementation((key: string) => { + return true; +}); + +describe('sync_app_filters', () => { + let filterManager: FilterManager; + let appState: BaseStateContainer; + let appStateSub: Subscription; + let appStateChangeTriggered = jest.fn(); + let filterManagerChangeSub: Subscription; + let filterManagerChangeTriggered = jest.fn(); + + let gF1: esFilters.Filter; + let gF2: esFilters.Filter; + let aF1: esFilters.Filter; + let aF2: esFilters.Filter; + + beforeEach(() => { + filterManager = new FilterManager(setupMock.uiSettings); + appState = createStateContainer([] as esFilters.Filter[]); + appStateChangeTriggered = jest.fn(); + appStateSub = appState.state$.subscribe(appStateChangeTriggered); + + filterManagerChangeTriggered = jest.fn(); + filterManagerChangeSub = filterManager.getUpdates$().subscribe(filterManagerChangeTriggered); + + gF1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, true, true, 'key1', 'value1'); + gF2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'key2', 'value2'); + aF1 = getFilter(esFilters.FilterStateStore.APP_STATE, true, true, 'key3', 'value3'); + aF2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'key4', 'value4'); + }); + afterEach(() => { + appStateSub.unsubscribe(); + filterManagerChangeSub.unsubscribe(); + }); + + describe('sync from filterManager to app state', () => { + test('should sync app filters to app state when new app filters set to filterManager', () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, aF1]); + + expect(appState.get()).toHaveLength(1); + stop(); + }); + + test('should not sync global filters to app state ', () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, gF2]); + + expect(appState.get()).toHaveLength(0); + stop(); + }); + + test("should not trigger changes when app filters didn't change", () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, aF1]); + + filterManager.setFilters([gF2, aF1]); + + expect(appStateChangeTriggered).toBeCalledTimes(1); + expect(appState.get()).toHaveLength(1); + + stop(); + }); + + test('should trigger changes when app filters change', () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, aF1]); + filterManager.setFilters([gF1, aF2]); + + expect(appStateChangeTriggered).toBeCalledTimes(2); + expect(appState.get()).toHaveLength(1); + + stop(); + }); + + test('resetting filters should sync to app state', () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, aF1]); + + expect(appState.get()).toHaveLength(1); + + filterManager.removeAll(); + + expect(appState.get()).toHaveLength(0); + + stop(); + }); + + test("shouldn't sync filters when syncing is stopped", () => { + const stop = syncAppFilters(filterManager, appState); + + filterManager.setFilters([gF1, aF1]); + + expect(appState.get()).toHaveLength(1); + + stop(); + + filterManager.removeAll(); + + expect(appState.get()).toHaveLength(1); + }); + }); + describe('sync from app state to filterManager', () => { + test('should pick up initial state from app state', () => { + appState.set([aF1]); + filterManager.setFilters([gF1]); + + const stop = syncAppFilters(filterManager, appState); + expect(filterManager.getFilters()).toHaveLength(2); + expect(appStateChangeTriggered).toBeCalledTimes(1); + + stop(); + }); + + test('changes to app state should be synced to app filters', () => { + filterManager.setFilters([gF1]); + const stop = syncAppFilters(filterManager, appState); + + appState.set([aF1]); + + expect(filterManager.getFilters()).toHaveLength(2); + expect(filterManager.getAppFilters()).toHaveLength(1); + expect(filterManager.getGlobalFilters()).toHaveLength(1); + expect(appStateChangeTriggered).toBeCalledTimes(1); + stop(); + }); + + test('global filters should remain untouched', () => { + filterManager.setFilters([gF1, gF2, aF1, aF2]); + const stop = syncAppFilters(filterManager, appState); + + appState.set([]); + + expect(filterManager.getFilters()).toHaveLength(2); + expect(filterManager.getGlobalFilters()).toHaveLength(2); + expect(appStateChangeTriggered).toBeCalledTimes(1); + stop(); + }); + + test("if filters are not changed, filterManager shouldn't trigger update", () => { + filterManager.setFilters([gF1, gF2, aF1, aF2]); + filterManagerChangeTriggered.mockClear(); + + appState.set([aF1, aF2]); + const stop = syncAppFilters(filterManager, appState); + appState.set([aF1, aF2]); + + expect(filterManagerChangeTriggered).toBeCalledTimes(0); + stop(); + }); + + test('stop() should stop syncing', () => { + filterManager.setFilters([gF1, gF2, aF1, aF2]); + const stop = syncAppFilters(filterManager, appState); + appState.set([]); + expect(filterManager.getFilters()).toHaveLength(2); + stop(); + appState.set([aF1]); + expect(filterManager.getFilters()).toHaveLength(2); + }); + }); +}); diff --git a/src/plugins/data/public/query/state_sync/sync_app_filters.ts b/src/plugins/data/public/query/state_sync/sync_app_filters.ts new file mode 100644 index 0000000000000..7954729cd8665 --- /dev/null +++ b/src/plugins/data/public/query/state_sync/sync_app_filters.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import _ from 'lodash'; +import { filter, map } from 'rxjs/operators'; +import { COMPARE_ALL_OPTIONS, compareFilters } from '../filter_manager/lib/compare_filters'; +import { esFilters } from '../../../common'; +import { FilterManager } from '../filter_manager'; +import { BaseStateContainer } from '../../../../../plugins/kibana_utils/public'; + +/** + * Helper utility to sync application's state filters, with filter manager + * @param filterManager + * @param appState + */ +export function syncAppFilters( + filterManager: FilterManager, + appState: BaseStateContainer +) { + // make sure initial app filters are picked by filterManager + filterManager.setAppFilters(_.cloneDeep(appState.get())); + + const subs = [ + filterManager + .getUpdates$() + .pipe( + map(() => filterManager.getAppFilters()), + filter( + // continue only if app state filters updated + appFilters => !compareFilters(appFilters, appState.get(), COMPARE_ALL_OPTIONS) + ) + ) + .subscribe(appFilters => { + appState.set(appFilters); + }), + + // if appFilters in dashboardStateManager changed (e.g browser history update), + // sync it to filterManager + appState.state$.subscribe(() => { + if (!compareFilters(appState.get(), filterManager.getAppFilters(), COMPARE_ALL_OPTIONS)) { + filterManager.setAppFilters(_.cloneDeep(appState.get())); + } + }), + ]; + + return () => { + subs.forEach(s => s.unsubscribe()); + }; +} diff --git a/src/plugins/data/public/query/state_sync/sync_query.test.ts b/src/plugins/data/public/query/state_sync/sync_query.test.ts new file mode 100644 index 0000000000000..0973af13cacd5 --- /dev/null +++ b/src/plugins/data/public/query/state_sync/sync_query.test.ts @@ -0,0 +1,166 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subscription } from 'rxjs'; +import { createBrowserHistory, History } from 'history'; +import { FilterManager } from '../filter_manager'; +import { getFilter } from '../filter_manager/test_helpers/get_stub_filter'; +import { esFilters } from '../../../common'; +import { coreMock } from '../../../../../core/public/mocks'; +import { + createKbnUrlStateStorage, + IKbnUrlStateStorage, + Storage, +} from '../../../../kibana_utils/public'; +import { QueryService, QueryStart } from '../query_service'; +import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; +import { TimefilterContract } from '../timefilter'; +import { QuerySyncState, syncQuery } from './sync_query'; + +const setupMock = coreMock.createSetup(); +const startMock = coreMock.createStart(); + +setupMock.uiSettings.get.mockImplementation((key: string) => { + switch (key) { + case 'filters:pinnedByDefault': + return true; + case 'timepicker:timeDefaults': + return { from: 'now-15m', to: 'now' }; + case 'timepicker:refreshIntervalDefaults': + return { pause: false, value: 0 }; + default: + throw new Error(`sync_query test: not mocked uiSetting: ${key}`); + } +}); + +describe('sync_query', () => { + let queryServiceStart: QueryStart; + let filterManager: FilterManager; + let timefilter: TimefilterContract; + let kbnUrlStateStorage: IKbnUrlStateStorage; + let history: History; + + let filterManagerChangeSub: Subscription; + let filterManagerChangeTriggered = jest.fn(); + + let gF: esFilters.Filter; + let aF: esFilters.Filter; + + const pathWithFilter = + "/#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!t,index:'logstash-*',key:query,negate:!t,type:custom,value:'%7B%22match%22:%7B%22key1%22:%22value1%22%7D%7D'),query:(match:(key1:value1)))),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"; + + beforeEach(() => { + const queryService = new QueryService(); + queryService.setup({ + uiSettings: setupMock.uiSettings, + storage: new Storage(new StubBrowserStorage()), + }); + queryServiceStart = queryService.start(startMock.savedObjects); + filterManager = queryServiceStart.filterManager; + timefilter = queryServiceStart.timefilter.timefilter; + + filterManagerChangeTriggered = jest.fn(); + filterManagerChangeSub = filterManager.getUpdates$().subscribe(filterManagerChangeTriggered); + + window.location.href = '/'; + history = createBrowserHistory(); + kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history }); + + gF = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, true, true, 'key1', 'value1'); + aF = getFilter(esFilters.FilterStateStore.APP_STATE, true, true, 'key3', 'value3'); + }); + afterEach(() => { + filterManagerChangeSub.unsubscribe(); + }); + + test('url is actually changed when data in services changes', () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + filterManager.setFilters([gF, aF]); + kbnUrlStateStorage.flush(); // sync force location change + expect(history.location.hash).toMatchInlineSnapshot( + `"#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!t,index:'logstash-*',key:query,negate:!t,type:custom,value:'%7B%22match%22:%7B%22key1%22:%22value1%22%7D%7D'),query:(match:(key1:value1)))),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))"` + ); + stop(); + }); + + test('when filters change, global filters synced to urlStorage', () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + filterManager.setFilters([gF, aF]); + expect(kbnUrlStateStorage.get('_g')?.filters).toHaveLength(1); + stop(); + }); + + test('when time range changes, time synced to urlStorage', () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + timefilter.setTime({ from: 'now-30m', to: 'now' }); + expect(kbnUrlStateStorage.get('_g')?.time).toEqual({ + from: 'now-30m', + to: 'now', + }); + stop(); + }); + + test('when refresh interval changes, refresh interval is synced to urlStorage', () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + timefilter.setRefreshInterval({ pause: true, value: 100 }); + expect(kbnUrlStateStorage.get('_g')?.refreshInterval).toEqual({ + pause: true, + value: 100, + }); + stop(); + }); + + test('when url is changed, filters synced back to filterManager', () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + kbnUrlStateStorage.cancel(); // stop initial syncing pending update + history.push(pathWithFilter); + expect(filterManager.getGlobalFilters()).toHaveLength(1); + stop(); + }); + + test('initial url should be synced with services', () => { + history.push(pathWithFilter); + + const { stop, hasInheritedQueryFromUrl } = syncQuery(queryServiceStart, kbnUrlStateStorage); + expect(hasInheritedQueryFromUrl).toBe(true); + expect(filterManager.getGlobalFilters()).toHaveLength(1); + stop(); + }); + + test("url changes shouldn't trigger services updates if data didn't change", () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + filterManagerChangeTriggered.mockClear(); + + history.push(pathWithFilter); + history.push(pathWithFilter); + history.push(pathWithFilter); + + expect(filterManagerChangeTriggered).not.toBeCalled(); + stop(); + }); + + test("if data didn't change, kbnUrlStateStorage.set shouldn't be called", () => { + const { stop } = syncQuery(queryServiceStart, kbnUrlStateStorage); + filterManager.setFilters([gF, aF]); + const spy = jest.spyOn(kbnUrlStateStorage, 'set'); + filterManager.setFilters([gF]); // global filters didn't change + expect(spy).not.toBeCalled(); + stop(); + }); +}); diff --git a/src/plugins/data/public/query/state_sync/sync_query.ts b/src/plugins/data/public/query/state_sync/sync_query.ts new file mode 100644 index 0000000000000..be641e89f9b76 --- /dev/null +++ b/src/plugins/data/public/query/state_sync/sync_query.ts @@ -0,0 +1,167 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subscription } from 'rxjs'; +import _ from 'lodash'; +import { filter, map } from 'rxjs/operators'; +import { + createStateContainer, + IKbnUrlStateStorage, + syncState, +} from '../../../../kibana_utils/public'; +import { COMPARE_ALL_OPTIONS, compareFilters } from '../filter_manager/lib/compare_filters'; +import { esFilters, RefreshInterval, TimeRange } from '../../../common'; +import { QueryStart } from '../query_service'; + +const GLOBAL_STATE_STORAGE_KEY = '_g'; + +export interface QuerySyncState { + time?: TimeRange; + refreshInterval?: RefreshInterval; + filters?: esFilters.Filter[]; +} + +/** + * Helper utility to set up syncing between query services and url's '_g' query param + */ +export const syncQuery = ( + { timefilter: { timefilter }, filterManager }: QueryStart, + urlStateStorage: IKbnUrlStateStorage +) => { + const defaultState: QuerySyncState = { + time: timefilter.getTime(), + refreshInterval: timefilter.getRefreshInterval(), + filters: filterManager.getGlobalFilters(), + }; + + // retrieve current state from `_g` url + const initialStateFromUrl = urlStateStorage.get(GLOBAL_STATE_STORAGE_KEY); + + // remember whether there were info in the URL + const hasInheritedQueryFromUrl = Boolean( + initialStateFromUrl && Object.keys(initialStateFromUrl).length + ); + + // prepare initial state, whatever was in URL takes precedences over current state in services + const initialState: QuerySyncState = { + ...defaultState, + ...initialStateFromUrl, + }; + + // create state container, which will be used for syncing with syncState() util + const querySyncStateContainer = createStateContainer( + initialState, + { + setTime: (state: QuerySyncState) => (time: TimeRange) => ({ ...state, time }), + setRefreshInterval: (state: QuerySyncState) => (refreshInterval: RefreshInterval) => ({ + ...state, + refreshInterval, + }), + setFilters: (state: QuerySyncState) => (filters: esFilters.Filter[]) => ({ + ...state, + filters, + }), + }, + { + time: (state: QuerySyncState) => () => state.time, + refreshInterval: (state: QuerySyncState) => () => state.refreshInterval, + filters: (state: QuerySyncState) => () => state.filters, + } + ); + + const subs: Subscription[] = [ + timefilter.getTimeUpdate$().subscribe(() => { + querySyncStateContainer.transitions.setTime(timefilter.getTime()); + }), + timefilter.getRefreshIntervalUpdate$().subscribe(() => { + querySyncStateContainer.transitions.setRefreshInterval(timefilter.getRefreshInterval()); + }), + filterManager + .getUpdates$() + .pipe( + map(() => filterManager.getGlobalFilters()), // we need to track only global filters here + filter(newGlobalFilters => { + // continue only if global filters changed + // and ignore app state filters + const oldGlobalFilters = querySyncStateContainer.get().filters; + return ( + !oldGlobalFilters || + !compareFilters(newGlobalFilters, oldGlobalFilters, COMPARE_ALL_OPTIONS) + ); + }) + ) + .subscribe(newGlobalFilters => { + querySyncStateContainer.transitions.setFilters(newGlobalFilters); + }), + querySyncStateContainer.state$.subscribe( + ({ time, filters: globalFilters, refreshInterval }) => { + // cloneDeep is required because services are mutating passed objects + // and state in state container is frozen + if (time && !_.isEqual(time, timefilter.getTime())) { + timefilter.setTime(_.cloneDeep(time)); + } + + if (refreshInterval && !_.isEqual(refreshInterval, timefilter.getRefreshInterval())) { + timefilter.setRefreshInterval(_.cloneDeep(refreshInterval)); + } + + if ( + globalFilters && + !compareFilters(globalFilters, filterManager.getGlobalFilters(), COMPARE_ALL_OPTIONS) + ) { + filterManager.setGlobalFilters(_.cloneDeep(globalFilters)); + } + } + ), + ]; + + // if there weren't any initial state in url, + // then put _g key into url + if (!initialStateFromUrl) { + urlStateStorage.set(GLOBAL_STATE_STORAGE_KEY, initialState, { + replace: true, + }); + } + + // trigger initial syncing from state container to services if needed + querySyncStateContainer.set(initialState); + + const { start, stop } = syncState({ + stateStorage: urlStateStorage, + stateContainer: { + ...querySyncStateContainer, + set: state => { + if (state) { + // syncState utils requires to handle incoming "null" value + querySyncStateContainer.set(state); + } + }, + }, + storageKey: GLOBAL_STATE_STORAGE_KEY, + }); + + start(); + return { + stop: () => { + subs.forEach(s => s.unsubscribe()); + stop(); + }, + hasInheritedQueryFromUrl, + }; +}; diff --git a/src/plugins/data/public/search/es_client/index.ts b/src/plugins/data/public/search/es_client/index.ts index bf1a3f5d6e7c4..78ac83af642d8 100644 --- a/src/plugins/data/public/search/es_client/index.ts +++ b/src/plugins/data/public/search/es_client/index.ts @@ -18,3 +18,4 @@ */ export { getEsClient } from './get_es_client'; +export { LegacyApiCaller } from './types'; diff --git a/src/core/public/http/response.ts b/src/plugins/data/public/search/es_client/types.ts similarity index 63% rename from src/core/public/http/response.ts rename to src/plugins/data/public/search/es_client/types.ts index 706e7caaca976..3ca0513a14238 100644 --- a/src/core/public/http/response.ts +++ b/src/plugins/data/public/search/es_client/types.ts @@ -17,24 +17,14 @@ * under the License. */ -import { IHttpResponse } from './types'; +export type SearchRequest = any; +export type SearchResponse = any; -export class HttpResponse implements IHttpResponse { - public readonly request: Request; - public readonly response?: Response; - public readonly body?: TResponseBody; +export interface LegacyApiCaller { + search: (searchRequest: SearchRequest) => LegacyApiCallerResponse; + msearch: (searchRequest: SearchRequest) => LegacyApiCallerResponse; +} - constructor({ - request, - response, - body, - }: { - request: Request; - response?: Response; - body?: TResponseBody; - }) { - this.request = request; - this.response = response; - this.body = body; - } +interface LegacyApiCallerResponse extends Promise { + abort: () => void; } diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index d36202debd9b9..7d62b3823771d 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -40,3 +40,5 @@ export { SYNC_SEARCH_STRATEGY } from './sync_search_strategy'; export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search'; export { ISearchStart } from './search_service'; + +export { LegacyApiCaller } from './es_client'; diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts index 69e05e8df48e2..9ed02e1d61018 100644 --- a/src/plugins/data/public/search/search_service.test.ts +++ b/src/plugins/data/public/search/search_service.test.ts @@ -33,7 +33,9 @@ describe('Search service', () => { describe('setup()', () => { it('exposes proper contract', async () => { - const setup = searchService.setup(mockCoreSetup); + const setup = searchService.setup(mockCoreSetup, { + version: '8', + } as any); expect(setup).toHaveProperty('registerSearchStrategyContext'); expect(setup).toHaveProperty('registerSearchStrategyProvider'); }); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 6f3e228939d6d..dad2b3d078aa0 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -39,7 +39,7 @@ import { import { TStrategyTypes } from './strategy_types'; import { esSearchService } from './es_search'; import { ISearchGeneric } from './i_search'; -import { getEsClient } from './es_client'; +import { getEsClient, LegacyApiCaller } from './es_client'; /** * Extends the AppMountContext so other plugins have access @@ -54,7 +54,7 @@ declare module 'kibana/public' { export interface ISearchStart { search: ISearchGeneric; __LEGACY: { - esClient: any; + esClient: LegacyApiCaller; }; } @@ -78,13 +78,13 @@ export class SearchService implements Plugin { * Exposes context to the search strategies. */ private contextContainer?: IContextContainer>; - + private esClient?: LegacyApiCaller; private search?: ISearchGeneric; private readonly loadingCount$ = new BehaviorSubject(0); constructor(private initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): ISearchSetup { + public setup(core: CoreSetup, packageInfo: PackageInfo): ISearchSetup { core.http.addLoadingCountSource(this.loadingCount$); const search = (this.search = createAppMountSearchContext( this.searchStrategies, @@ -95,6 +95,7 @@ export class SearchService implements Plugin { }); this.contextContainer = core.context.createContextContainer(); + this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo, this.loadingCount$); const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = < T extends TStrategyTypes @@ -109,6 +110,9 @@ export class SearchService implements Plugin { const api = { registerSearchStrategyContext: this.contextContainer!.registerContext, registerSearchStrategyProvider, + __LEGACY: { + esClient: this.esClient, + }, }; api.registerSearchStrategyContext(this.initializerContext.opaqueId, 'core', () => core); @@ -126,14 +130,14 @@ export class SearchService implements Plugin { return api; } - public start(core: CoreStart, packageInfo: PackageInfo) { + public start(core: CoreStart): ISearchStart { if (!this.search) { throw new Error('Search should always be defined'); } return { search: this.search, __LEGACY: { - esClient: getEsClient(core.injectedMetadata, core.http, packageInfo, this.loadingCount$), + esClient: this.esClient!, }, }; } diff --git a/src/plugins/data/public/search/sync_search_strategy.test.ts b/src/plugins/data/public/search/sync_search_strategy.test.ts index 2737a4033a015..cd19c4d84dce1 100644 --- a/src/plugins/data/public/search/sync_search_strategy.test.ts +++ b/src/plugins/data/public/search/sync_search_strategy.test.ts @@ -44,10 +44,8 @@ describe('Sync search strategy', () => { }, {} ); - expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toBe( - `/internal/search/${SYNC_SEARCH_STRATEGY}` - ); - expect(mockCoreSetup.http.fetch.mock.calls[0][1]).toEqual({ + expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({ + path: `/internal/search/${SYNC_SEARCH_STRATEGY}`, body: JSON.stringify({ serverStrategy: 'SYNC_SEARCH_STRATEGY', }), diff --git a/src/plugins/data/public/search/sync_search_strategy.ts b/src/plugins/data/public/search/sync_search_strategy.ts index 3885a97a98571..65fe10f39aaa0 100644 --- a/src/plugins/data/public/search/sync_search_strategy.ts +++ b/src/plugins/data/public/search/sync_search_strategy.ts @@ -36,14 +36,12 @@ export const syncSearchStrategyProvider: TSearchStrategyProvider { - const response: Promise = context.core.http.fetch( - `/internal/search/${request.serverStrategy}`, - { - method: 'POST', - body: JSON.stringify(request), - signal: options.signal, - } - ); + const response: Promise = context.core.http.fetch({ + path: `/internal/search/${request.serverStrategy}`, + method: 'POST', + body: JSON.stringify(request), + signal: options.signal, + }); return from(response); }; diff --git a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx index bda1557bdaf91..39290b066fab7 100644 --- a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx +++ b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/public'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; import { CorePluginAPluginSetup } from '../../core_plugin_a/public/plugin'; declare global { @@ -52,7 +52,17 @@ export class CorePluginBPlugin }; } - public start() {} + public async start(core: CoreStart, deps: {}) { + return { + sendSystemRequest: async (asSystemRequest: boolean) => { + const response = await core.http.post('/core_plugin_b/system_request', { + asSystemRequest, + }); + return `/core_plugin_b/system_request says: "${response}"`; + }, + }; + } + public stop() {} } diff --git a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts index 91a4fe89c1c57..d2bb9222a23fa 100644 --- a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts @@ -54,6 +54,16 @@ export class CorePluginBPlugin implements Plugin { return res.ok({ body: `ID: ${req.query.id} - ${req.body.bar.toUpperCase()}` }); } ); + + router.post( + { + path: '/core_plugin_b/system_request', + validate: false, + }, + async (context, req, res) => { + return res.ok({ body: `System request? ${req.isSystemRequest}` }); + } + ); } public start() {} diff --git a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts index eb232b1458991..f5b45d36944e1 100644 --- a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts @@ -54,5 +54,15 @@ export default function({ getService }: PluginFunctionalProviderContext) { statusCode: 400, }); }); + + it('sets request.isSystemRequest when kbn-system-request header is set', async () => { + await supertest + .post('/core_plugin_b/system_request') + .set('kbn-xsrf', 'anything') + .set('kbn-system-request', 'true') + .send() + .expect(200) + .expect('System request? true'); + }); }); } diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts index b76463ee76739..82267d73782af 100644 --- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts @@ -64,7 +64,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider }); }); - describe('have env data provided', function describeIndexTests() { + describe('have env data provided', () => { before(async () => { await PageObjects.common.navigateToApp('bar'); }); @@ -75,5 +75,27 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider expect(envData.packageInfo.version).to.be.a('string'); }); }); + + describe('http fetching', () => { + before(async () => { + await PageObjects.common.navigateToApp('settings'); + }); + + it('should send kbn-system-request header when asSystemRequest: true', async () => { + expect( + await browser.executeAsync(async cb => { + window.__coreProvider.start.plugins.core_plugin_b.sendSystemRequest(true).then(cb); + }) + ).to.be('/core_plugin_b/system_request says: "System request? true"'); + }); + + it('should not send kbn-system-request header when asSystemRequest: false', async () => { + expect( + await browser.executeAsync(async cb => { + window.__coreProvider.start.plugins.core_plugin_b.sendSystemRequest(false).then(cb); + }) + ).to.be('/core_plugin_b/system_request says: "System request? false"'); + }); + }); }); } diff --git a/x-pack/legacy/plugins/alerting/common/index.ts b/x-pack/legacy/plugins/alerting/common/index.ts new file mode 100644 index 0000000000000..9f4141dbcae7d --- /dev/null +++ b/x-pack/legacy/plugins/alerting/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './types'; diff --git a/x-pack/legacy/plugins/alerting/common/types.ts b/x-pack/legacy/plugins/alerting/common/types.ts new file mode 100644 index 0000000000000..54bf04d0765d6 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/common/types.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectAttributes } from 'kibana/server'; +import { AlertActionParams } from '../server/types'; + +export interface IntervalSchedule extends SavedObjectAttributes { + interval: string; +} + +export interface AlertAction { + group: string; + id: string; + actionTypeId: string; + params: AlertActionParams; +} + +export interface Alert { + id: string; + enabled: boolean; + name: string; + tags: string[]; + alertTypeId: string; + consumer: string; + schedule: IntervalSchedule; + actions: AlertAction[]; + params: Record; + scheduledTaskId?: string; + createdBy: string | null; + updatedBy: string | null; + createdAt: Date; + updatedAt: Date; + apiKey: string | null; + apiKeyOwner: string | null; + throttle: string | null; + muteAll: boolean; + mutedInstanceIds: string[]; +} + +export type SanitizedAlert = Omit; diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts index 7801e8f478712..a6ba936b76570 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts @@ -21,6 +21,7 @@ import { AlertAction, AlertType, IntervalSchedule, + SanitizedAlert, } from './types'; import { validateAlertTypeParams } from './lib'; import { @@ -74,7 +75,7 @@ export interface FindResult { page: number; perPage: number; total: number; - data: Alert[]; + data: SanitizedAlert[]; } interface CreateOptions { @@ -198,7 +199,7 @@ export class AlertsClient { ); } - public async get({ id }: { id: string }): Promise { + public async get({ id }: { id: string }): Promise { const result = await this.savedObjectsClient.get('alert', id); return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references); } diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/legacy/plugins/alerting/server/types.ts index def86cd46e590..9c4a64ff02105 100644 --- a/x-pack/legacy/plugins/alerting/server/types.ts +++ b/x-pack/legacy/plugins/alerting/server/types.ts @@ -8,6 +8,9 @@ import { AlertInstance } from './alert_instance'; import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../../src/core/server'; +import { Alert } from '../common'; + +export * from '../common'; export type State = Record; export type Context = Record; @@ -52,13 +55,6 @@ export interface AlertType { export type AlertActionParams = SavedObjectAttributes; -export interface AlertAction { - group: string; - id: string; - actionTypeId: string; - params: AlertActionParams; -} - export interface RawAlertAction extends SavedObjectAttributes { group: string; actionRef: string; @@ -66,32 +62,6 @@ export interface RawAlertAction extends SavedObjectAttributes { params: AlertActionParams; } -export interface IntervalSchedule extends SavedObjectAttributes { - interval: string; -} - -export interface Alert { - id: string; - enabled: boolean; - name: string; - tags: string[]; - alertTypeId: string; - consumer: string; - schedule: IntervalSchedule; - actions: AlertAction[]; - params: Record; - scheduledTaskId?: string; - createdBy: string | null; - updatedBy: string | null; - createdAt: Date; - updatedAt: Date; - apiKey: string | null; - apiKeyOwner: string | null; - throttle: string | null; - muteAll: boolean; - mutedInstanceIds: string[]; -} - export type PartialAlert = Pick & Partial>; export interface RawAlert extends SavedObjectAttributes { diff --git a/x-pack/legacy/plugins/canvas/__tests__/fixtures/function_specs.ts b/x-pack/legacy/plugins/canvas/__tests__/fixtures/function_specs.ts index 899fbf3d3d91b..8cb0b26565ef3 100644 --- a/x-pack/legacy/plugins/canvas/__tests__/fixtures/function_specs.ts +++ b/x-pack/legacy/plugins/canvas/__tests__/fixtures/function_specs.ts @@ -6,8 +6,9 @@ // @ts-ignore Untyped Library import { Fn } from '@kbn/interpreter/common'; +import { uniq } from 'lodash'; import { functions as browserFns } from '../../canvas_plugin_src/functions/browser'; import { functions as commonFns } from '../../canvas_plugin_src/functions/common'; import { functions as serverFns } from '../../canvas_plugin_src/functions/server'; -export const functionSpecs = [...browserFns, ...commonFns, ...serverFns].map(fn => new Fn(fn())); +export const functionSpecs = uniq([...browserFns, ...commonFns, ...serverFns], 'name').map(fn => new Fn(fn())); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts index 777d2e61133a7..1c7013834cbe4 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/markdown/index.ts @@ -26,7 +26,7 @@ The data table contains **{{name}}** {{/each}} -You can use standard Markdown in here, but you can also access your piped-in data using Handlebars. If you want to know more, check out the [Handlebars documentation](http://handlebarsjs.com/expressions.html). +You can use standard Markdown in here, but you can also access your piped-in data using Handlebars. If you want to know more, check out the [Handlebars documentation](https://handlebarsjs.com/guide/expressions.html). #### Enjoy!" | render`, }); diff --git a/x-pack/legacy/plugins/canvas/common/lib/autocomplete.test.ts b/x-pack/legacy/plugins/canvas/common/lib/autocomplete.test.ts index 88bb32c846c24..dbe81deced36d 100644 --- a/x-pack/legacy/plugins/canvas/common/lib/autocomplete.test.ts +++ b/x-pack/legacy/plugins/canvas/common/lib/autocomplete.test.ts @@ -7,7 +7,11 @@ jest.mock('ui/new_platform'); import { functionSpecs } from '../../__tests__/fixtures/function_specs'; -import { getAutocompleteSuggestions, getFnArgDefAtPosition } from './autocomplete'; +import { + FunctionSuggestion, + getAutocompleteSuggestions, + getFnArgDefAtPosition, +} from './autocomplete'; describe('autocomplete', () => { describe('getFnArgDefAtPosition', () => { @@ -27,15 +31,6 @@ describe('autocomplete', () => { expect(suggestions[0].end).toBe(0); }); - it('should suggest functions filtered by text', () => { - const expression = 'pl'; - const suggestions = getAutocompleteSuggestions(functionSpecs, expression, 0); - const nonmatching = suggestions.map(s => s.text).filter(text => !text.includes(expression)); - expect(nonmatching.length).toBe(0); - expect(suggestions[0].start).toBe(0); - expect(suggestions[0].end).toBe(expression.length); - }); - it('should suggest arguments', () => { const expression = 'plot '; const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); @@ -45,16 +40,6 @@ describe('autocomplete', () => { expect(suggestions[0].end).toBe(expression.length); }); - it('should suggest arguments filtered by text', () => { - const expression = 'plot axis'; - const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); - const plotFn = functionSpecs.find(spec => spec.name === 'plot'); - const matchingArgs = Object.keys(plotFn.args).filter(key => key.includes('axis')); - expect(suggestions.length).toBe(matchingArgs.length); - expect(suggestions[0].start).toBe('plot '.length); - expect(suggestions[0].end).toBe('plot axis'.length); - }); - it('should suggest values', () => { const expression = 'shape shape='; const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); @@ -64,16 +49,6 @@ describe('autocomplete', () => { expect(suggestions[0].end).toBe(expression.length); }); - it('should suggest values filtered by text', () => { - const expression = 'shape shape=ar'; - const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); - const shapeFn = functionSpecs.find(spec => spec.name === 'shape'); - const matchingValues = shapeFn.args.shape.options.filter((key: string) => key.includes('ar')); - expect(suggestions.length).toBe(matchingValues.length); - expect(suggestions[0].start).toBe(expression.length - 'ar'.length); - expect(suggestions[0].end).toBe(expression.length); - }); - it('should suggest functions inside an expression', () => { const expression = 'if {}'; const suggestions = getAutocompleteSuggestions( @@ -86,6 +61,52 @@ describe('autocomplete', () => { expect(suggestions[0].end).toBe(expression.length - 1); }); + it('should rank functions inside an expression by their return type first', () => { + const expression = 'plot defaultStyle={}'; + const suggestions = getAutocompleteSuggestions( + functionSpecs, + expression, + expression.length - 1 + ) as FunctionSuggestion[]; + expect(suggestions.length).toBe(functionSpecs.length); + expect(suggestions[0].fnDef.name).toBe('seriesStyle'); + }); + + it('should rank functions inside an expression with matching return types and contexts before just return type', () => { + const expression = 'staticColumn "hello" | ply expression={}'; + const suggestions = getAutocompleteSuggestions( + functionSpecs, + expression, + expression.length - 1 + ) as FunctionSuggestion[]; + expect(suggestions.length).toBe(functionSpecs.length); + + expect(suggestions[0].fnDef.type).toBe('datatable'); + expect(suggestions[0].fnDef.context && suggestions[0].fnDef.context.types).toEqual([ + 'datatable', + ]); + + const withReturnOnly = suggestions.findIndex( + suggestion => + suggestion.fnDef.type === 'datatable' && + suggestion.fnDef.context && + suggestion.fnDef.context.types && + !(suggestion.fnDef.context.types as string[]).includes('datatable') + ); + + const withNeither = suggestions.findIndex( + suggestion => + suggestion.fnDef.type !== 'datatable' && + (!suggestion.fnDef.context || + !(suggestion.fnDef.context.types as string[]).includes('datatable')) + ); + + expect(suggestions[0].fnDef.type).toBe('datatable'); + expect(suggestions[0].fnDef.context?.types).toEqual(['datatable']); + + expect(withReturnOnly).toBeLessThan(withNeither); + }); + it('should suggest arguments inside an expression', () => { const expression = 'if {lt }'; const suggestions = getAutocompleteSuggestions( @@ -120,22 +141,11 @@ describe('autocomplete', () => { expression.length - 1 ); const shapeFn = functionSpecs.find(spec => spec.name === 'shape'); - const matchingValues = shapeFn.args.shape.options.filter((key: string) => key.includes('ar')); - expect(suggestions.length).toBe(matchingValues.length); + expect(suggestions.length).toBe(shapeFn.args.shape.options.length); expect(suggestions[0].start).toBe(expression.length - '"ar"'.length); expect(suggestions[0].end).toBe(expression.length); }); - it('should prioritize functions that start with text', () => { - const expression = 't'; - const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); - const tableIndex = suggestions.findIndex(suggestion => suggestion.text.includes('table')); - const alterColumnIndex = suggestions.findIndex(suggestion => - suggestion.text.includes('alterColumn') - ); - expect(tableIndex).toBeLessThan(alterColumnIndex); - }); - it('should prioritize functions that match the previous function type', () => { const expression = 'plot | '; const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); @@ -152,16 +162,6 @@ describe('autocomplete', () => { expect(anyIndex).toBeLessThan(metricIndex); }); - it('should prioritize arguments that start with text', () => { - const expression = 'plot y'; - const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); - const yaxisIndex = suggestions.findIndex(suggestion => suggestion.text.includes('yaxis')); - const defaultStyleIndex = suggestions.findIndex(suggestion => - suggestion.text.includes('defaultStyle') - ); - expect(yaxisIndex).toBeLessThan(defaultStyleIndex); - }); - it('should prioritize unnamed arguments', () => { const expression = 'case '; const suggestions = getAutocompleteSuggestions(functionSpecs, expression, expression.length); diff --git a/x-pack/legacy/plugins/canvas/common/lib/autocomplete.ts b/x-pack/legacy/plugins/canvas/common/lib/autocomplete.ts index 79da309b80905..96917e3e7ed2c 100644 --- a/x-pack/legacy/plugins/canvas/common/lib/autocomplete.ts +++ b/x-pack/legacy/plugins/canvas/common/lib/autocomplete.ts @@ -24,7 +24,7 @@ interface BaseSuggestion { end: number; } -interface FunctionSuggestion extends BaseSuggestion { +export interface FunctionSuggestion extends BaseSuggestion { type: 'function'; fnDef: CanvasFunction; } @@ -52,6 +52,13 @@ interface FnArgAtPosition { argIndex?: number; argStart?: number; argEnd?: number; + + // If this function is a sub-expression function, we need the parent function and argument + // name to determine the return type of the function + parentFn?: string; + // If this function is a sub-expression function, the context could either be local or it + // could be the parent's previous function. + contextFn?: string | null; } // If you parse an expression with the "addMeta" option it completely @@ -155,9 +162,17 @@ export function getAutocompleteSuggestions( const text = expression.substr(0, position) + MARKER + expression.substr(position); try { const ast = parse(text, { addMeta: true }) as ExpressionASTWithMeta; - const { ast: newAst, fnIndex, argName, argIndex } = getFnArgAtPosition(ast, position); + const { ast: newAst, fnIndex, argName, argIndex, parentFn, contextFn } = getFnArgAtPosition( + ast, + position + ); const fn = newAst.node.chain[fnIndex].node; + if (parentFn && fn.function.includes(MARKER) && argName) { + // We are in a sub-function like `plot font={}` + return getSubFnNameSuggestions(specs, newAst, fnIndex, parentFn, argName, contextFn); + } + if (fn.function.includes(MARKER)) { return getFnNameSuggestions(specs, newAst, fnIndex); } @@ -184,6 +199,16 @@ export function getAutocompleteSuggestions( It returns which function the cursor is in, as well as which argument for that function the cursor is in if any. + + If the expression is found in a sub-expression, this identifies the argument name for return type checking. + It also identifies the context function, which may not be the same as the current function. + In this example: + + `math "random()" | progress label={as "value" | math "divide(value, 2)" | formatnumber "0%"}` + + The return value of the `label` function is always expected to be a string or boolean. + The context function for the first expression in the chain is `math`, since it's the parent's previous + item. The context function for `formatnumber` is the return of `math "divide(value, 2)"`. */ function getFnArgAtPosition(ast: ExpressionASTWithMeta, position: number): FnArgAtPosition { const fnIndex = ast.node.chain.findIndex(fn => fn.start <= position && position <= fn.end); @@ -215,7 +240,25 @@ function getFnArgAtPosition(ast: ExpressionASTWithMeta, position: number): FnArg isExpression(value) && (argName === '_' || !(argStart <= position && position <= argStart + argName.length + 1)) ) { - return getFnArgAtPosition(value, position); + const result = getFnArgAtPosition(value, position); + if (!result.argName) { + const contextFn = + result.fnIndex === 0 + ? fnIndex > 0 + ? ast.node.chain[fnIndex - 1].node.function + : null + : result.ast.node.chain[result.fnIndex - 1].node.function; + return { + ...result, + argName, + argIndex, + argStart, + argEnd, + parentFn: fn.node.function, + contextFn, + }; + } + return result; } return { ast, fnIndex, argName, argIndex, argStart, argEnd }; } @@ -230,28 +273,129 @@ function getFnNameSuggestions( fnIndex: number ): FunctionSuggestion[] { // Filter the list of functions by the text at the marker - const { start, end, node: fn } = ast.node.chain[fnIndex]; - const query = fn.function.replace(MARKER, ''); - const matchingFnDefs = specs.filter(({ name }) => textMatches(name, query)); + const { start, end } = ast.node.chain[fnIndex]; // Sort by whether or not the function expects the previous function's return type, then by // whether or not the function name starts with the text at the marker, then alphabetically const prevFn = ast.node.chain[fnIndex - 1]; + const nextFn = ast.node.chain.length > fnIndex + 1 ? ast.node.chain[fnIndex + 1] : null; const prevFnDef = prevFn && getByAlias(specs, prevFn.node.function); const prevFnType = prevFnDef && prevFnDef.type; - const comparator = combinedComparator( - prevFnTypeComparator(prevFnType), - invokeWithProp(startsWithComparator(query), 'name'), - invokeWithProp(alphanumericalComparator, 'name') - ); - const fnDefs = matchingFnDefs.sort(comparator); + + const nextFnDef = nextFn && getByAlias(specs, nextFn.node.function); + const nextFnContext = nextFnDef && nextFnDef.context && nextFnDef.context.types; + + const fnDefs = specs.sort((a: CanvasFunction, b: CanvasFunction): number => { + const aScore = getScore(a, prevFnType, nextFnContext, false); + const bScore = getScore(b, prevFnType, nextFnContext, false); + + if (aScore === bScore) { + return a.name < b.name ? -1 : 1; + } + return aScore > bScore ? -1 : 1; + }); + + return fnDefs.map(fnDef => { + return { type: 'function', text: `${fnDef.name} `, start, end: end - MARKER.length, fnDef }; + }); +} + +function getSubFnNameSuggestions( + specs: CanvasFunction[], + ast: ExpressionASTWithMeta, + fnIndex: number, + parentFn: string, + parentFnArgName: string, + contextFn?: string | null +): FunctionSuggestion[] { + // Filter the list of functions by the text at the marker + const { start, end, node: fn } = ast.node.chain[fnIndex]; + const query = fn.function.replace(MARKER, ''); + const matchingFnDefs = specs.filter(({ name }) => textMatches(name, query)); + + const parentFnDef = getByAlias(specs, parentFn); + const matchingArgDef = getByAlias(parentFnDef.args, parentFnArgName); + + if (!matchingArgDef) { + return []; + } + + const contextFnDef = contextFn ? getByAlias(specs, contextFn) : null; + const contextFnType = contextFnDef && contextFnDef.type; + + const expectedReturnTypes = matchingArgDef.types; + + const fnDefs = matchingFnDefs.sort((a: CanvasFunction, b: CanvasFunction) => { + const aScore = getScore(a, contextFnType, expectedReturnTypes, true); + const bScore = getScore(b, contextFnType, expectedReturnTypes, true); + + if (aScore === bScore) { + return a.name < b.name ? -1 : 1; + } + return aScore > bScore ? -1 : 1; + }); return fnDefs.map(fnDef => { return { type: 'function', text: fnDef.name + ' ', start, end: end - MARKER.length, fnDef }; }); } +function getScore( + func: CanvasFunction, + contextType: any, + returnTypes?: any[] | null, + isSubFunc?: boolean +) { + let score = 0; + if (!contextType) { + contextType = 'null'; + } + + let funcContextTypes = []; + if (func.context && func.context.types && func.context.types.length) { + funcContextTypes = func.context.types; + } + + if (isSubFunc) { + if (returnTypes && func.type) { + // If in a sub-expression, favor types that match the expected return type for the argument + // with top results matching the passed in context + if (returnTypes.length && returnTypes.includes(func.type)) { + score++; + + if (funcContextTypes.includes(contextType)) { + score++; + } + } + } + } else { + if (func.context && func.context.types) { + const expectsNull = (funcContextTypes as string[]).includes('null'); + + if (!expectsNull && contextType !== 'null') { + // If not in a sub-expression and there's a preceding function, + // favor functions that expect a context with top results matching the passed in context + score++; + + if (func.context.types.includes(contextType)) { + score++; + } + } else if (expectsNull && contextType === 'null') { + // If not in a sub-expression and there's NOT a preceding function, + // favor functions that don't expect anything being passed in with top results returning a non-null + score++; + + if (func.type && func.type !== 'null') { + score++; + } + } + } + } + + return score; +} + function getArgNameSuggestions( specs: CanvasFunction[], ast: ExpressionASTWithMeta, @@ -267,13 +411,7 @@ function getArgNameSuggestions( } // We use the exact text instead of the value because it is always a string and might be quoted - const { text, start, end } = fn.arguments[argName][argIndex]; - - // Filter the list of args by the text at the marker - const query = text.replace(MARKER, ''); - const matchingArgDefs = Object.entries(fnDef.args).filter(([name]) => - textMatches(name, query) - ); + const { start, end } = fn.arguments[argName][argIndex]; // Filter the list of args by those which aren't already present (unless they allow multi) const argEntries = Object.entries(fn.arguments).map<[string, ExpressionArgASTWithMeta[]]>( @@ -282,32 +420,21 @@ function getArgNameSuggestions( } ); - const unusedArgDefs = matchingArgDefs.filter(([matchingArgName, matchingArgDef]) => { - if (matchingArgDef.multi) { - return true; + const unusedArgDefs = Object.entries(fnDef.args).filter( + ([matchingArgName, matchingArgDef]) => { + if (matchingArgDef.multi) { + return true; + } + return !argEntries.some(([name, values]) => { + return ( + values.length > 0 && + (name === matchingArgName || (matchingArgDef.aliases || []).includes(name)) + ); + }); } - return !argEntries.some(([name, values]) => { - return ( - values.length > 0 && - (name === matchingArgName || (matchingArgDef.aliases || []).includes(name)) - ); - }); - }); - - // Sort by whether or not the arg is also the unnamed, then by whether or not the arg name starts - // with the text at the marker, then alphabetically - const comparator = combinedComparator( - unnamedArgComparator, - invokeWithProp( - startsWithComparator(query), - 'name' - ), - invokeWithProp( - alphanumericalComparator, - 'name' - ) ); - const argDefs = unusedArgDefs.map(([name, arg]) => ({ name, ...arg })).sort(comparator); + + const argDefs = unusedArgDefs.map(([name, arg]) => ({ name, ...arg })).sort(unnamedArgComparator); return argDefs.map(argDef => { return { type: 'argument', text: argDef.name + '=', start, end: end - MARKER.length, argDef }; @@ -337,25 +464,15 @@ function getArgValueSuggestions( if (typeof node !== 'string') { return []; } - const query = node.replace(MARKER, ''); const argOptions = argDef.options ? argDef.options : []; - let suggestions = [...argOptions]; + const suggestions = [...argOptions]; if (argDef.default !== undefined) { suggestions.push(argDef.default); } - suggestions = uniq(suggestions); - - // Filter the list of suggestions by the text at the marker - const filtered = suggestions.filter(option => textMatches(String(option), query)); - - // Sort by whether or not the value starts with the text at the marker, then alphabetically - const comparator = combinedComparator(startsWithComparator(query), alphanumericalComparator); - const sorted = filtered.sort(comparator); - - return sorted.map(value => { + return uniq(suggestions).map(value => { const text = maybeQuote(value) + ' '; return { start, end: end - MARKER.length, type: 'value', text }; }); @@ -375,55 +492,8 @@ function maybeQuote(value: any) { return value; } -function prevFnTypeComparator(prevFnType: any) { - return (a: CanvasFunction, b: CanvasFunction): number => { - return ( - (b.context && b.context.types && b.context.types.includes(prevFnType) ? 1 : 0) - - (a.context && a.context.types && a.context.types.includes(prevFnType) ? 1 : 0) - ); - }; -} - function unnamedArgComparator(a: CanvasArgValue, b: CanvasArgValue): number { return ( (b.aliases && b.aliases.includes('_') ? 1 : 0) - (a.aliases && a.aliases.includes('_') ? 1 : 0) ); } - -function alphanumericalComparator(a: any, b: any): number { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; -} - -function startsWithComparator(query: string) { - return (a: any, b: any) => - (String(b).startsWith(query) ? 1 : 0) - (String(a).startsWith(query) ? 1 : 0); -} - -type Comparator = (a: T, b: T) => number; - -function combinedComparator(...comparators: Array>): Comparator { - return (a: T, b: T) => - comparators.reduce((acc: number, comparator) => { - if (acc !== 0) { - return acc; - } - return comparator(a, b); - }, 0); -} - -function invokeWithProp< - PropType, - PropName extends string, - ArgType extends { [key in PropName]: PropType }, - FnReturnType ->(fn: (...args: PropType[]) => FnReturnType, prop: PropName): (...args: ArgType[]) => FnReturnType { - return (...args: Array<{ [key in PropName]: PropType }>) => { - return fn(...args.map(arg => arg[prop])); - }; -} diff --git a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx index 4a8d3638d4266..c33e91064dc84 100644 --- a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx @@ -115,7 +115,11 @@ export class ExpressionInput extends React.Component { this.props.onChange(value); }; - provideSuggestions = (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => { + provideSuggestions = ( + model: monacoEditor.editor.ITextModel, + position: monacoEditor.Position, + context: monacoEditor.languages.CompletionContext + ) => { const text = model.getValue(); const textRange = model.getFullModelRange(); @@ -126,25 +130,52 @@ export class ExpressionInput extends React.Component { endColumn: textRange.endColumn, }); - const wordUntil = model.getWordUntilPosition(position); - const wordRange = new monacoEditor.Range( - position.lineNumber, - wordUntil.startColumn, - position.lineNumber, - wordUntil.endColumn - ); + let wordRange: monacoEditor.Range; + let aSuggestions; + + if (context.triggerCharacter === '{') { + const wordUntil = model.getWordAtPosition(position.delta(0, -3)); + if (wordUntil) { + wordRange = new monacoEditor.Range( + position.lineNumber, + position.column, + position.lineNumber, + position.column + ); + + // Retrieve suggestions for subexpressions + // TODO: make this work for expressions nested more than one level deep + aSuggestions = getAutocompleteSuggestions( + this.props.functionDefinitions, + text.substring(0, text.length - lengthAfterPosition) + '}', + text.length - lengthAfterPosition + ); + } + } else { + const wordUntil = model.getWordUntilPosition(position); + wordRange = new monacoEditor.Range( + position.lineNumber, + wordUntil.startColumn, + position.lineNumber, + wordUntil.endColumn + ); + aSuggestions = getAutocompleteSuggestions( + this.props.functionDefinitions, + text, + text.length - lengthAfterPosition + ); + } - const aSuggestions = getAutocompleteSuggestions( - this.props.functionDefinitions, - text, - text.length - lengthAfterPosition - ); + if (!aSuggestions) { + return { suggestions: [] }; + } - const suggestions = aSuggestions.map((s: AutocompleteSuggestion) => { + const suggestions = aSuggestions.map((s: AutocompleteSuggestion, index) => { + const sortText = String.fromCharCode(index); if (s.type === 'argument') { return { label: s.argDef.name, - kind: monacoEditor.languages.CompletionItemKind.Field, + kind: monacoEditor.languages.CompletionItemKind.Variable, documentation: { value: getArgReferenceStr(s.argDef), isTrusted: true }, insertText: s.text, command: { @@ -152,6 +183,7 @@ export class ExpressionInput extends React.Component { id: 'editor.action.triggerSuggest', }, range: wordRange, + sortText, }; } else if (s.type === 'value') { return { @@ -163,6 +195,7 @@ export class ExpressionInput extends React.Component { id: 'editor.action.triggerSuggest', }, range: wordRange, + sortText, }; } else { return { @@ -178,6 +211,7 @@ export class ExpressionInput extends React.Component { id: 'editor.action.triggerSuggest', }, range: wordRange, + sortText, }; } }); @@ -270,10 +304,18 @@ export class ExpressionInput extends React.Component {
)}
- {series.rows.length > 0 ? ( + {metrics.length && series.rows.length > 0 ? ( {metrics.map((metric, id) => ( { function testProps() { const setState = jest.fn(); - core.http.get.mockImplementation(async (url: string) => { - const parts = url.split('/'); + core.http.get.mockImplementation(async ({ path }) => { + const parts = path.split('/'); const indexPatternTitle = parts[parts.length - 1]; return { indexPatternTitle: `${indexPatternTitle}_testtitle`, @@ -397,7 +397,8 @@ describe('IndexPattern Data Panel', () => { expect(setState).toHaveBeenCalledTimes(2); expect(core.http.get).toHaveBeenCalledTimes(2); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { + expect(core.http.get).toHaveBeenCalledWith({ + path: '/api/lens/existing_fields/a', query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -405,7 +406,8 @@ describe('IndexPattern Data Panel', () => { }, }); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { + expect(core.http.get).toHaveBeenCalledWith({ + path: '/api/lens/existing_fields/a', query: { fromDate: '2019-01-01', toDate: '2020-01-02', @@ -436,7 +438,8 @@ describe('IndexPattern Data Panel', () => { expect(setState).toHaveBeenCalledTimes(2); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { + expect(core.http.get).toHaveBeenCalledWith({ + path: '/api/lens/existing_fields/a', query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -444,7 +447,8 @@ describe('IndexPattern Data Panel', () => { }, }); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/b', { + expect(core.http.get).toHaveBeenCalledWith({ + path: '/api/lens/existing_fields/b', query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -484,13 +488,13 @@ describe('IndexPattern Data Panel', () => { let overlapCount = 0; const props = testProps(); - core.http.get.mockImplementation((url: string) => { + core.http.get.mockImplementation(({ path }) => { if (queryCount) { ++overlapCount; } ++queryCount; - const parts = url.split('/'); + const parts = path.split('/'); const indexPatternTitle = parts[parts.length - 1]; const result = Promise.resolve({ indexPatternTitle, diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts index 6bea13c32830f..f8961c30d14ee 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts @@ -537,8 +537,8 @@ describe('loader', () => { describe('syncExistingFields', () => { it('should call once for each index pattern', async () => { const setState = jest.fn(); - const fetchJson = jest.fn(async (url: string) => { - const indexPatternTitle = _.last(url.split('/')); + const fetchJson = jest.fn(({ path }: { path: string }) => { + const indexPatternTitle = _.last(path.split('/')); return { indexPatternTitle, existingFieldNames: ['field_1', 'field_2'].map( diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts index c196cb886e575..5b994890deb7a 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts @@ -234,7 +234,8 @@ export async function syncExistingFields({ query.timeFieldName = pattern.timeFieldName; } - return fetchJson(`${BASE_API_URL}/existing_fields/${pattern.id}`, { + return fetchJson({ + path: `${BASE_API_URL}/existing_fields/${pattern.id}`, query, }) as Promise; }) diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts index ed73405134224..819db630c0609 100644 --- a/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts +++ b/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts @@ -12,11 +12,11 @@ import { forkJoin, of, Observable, Subject } from 'rxjs'; import { mergeMap, switchMap, tap } from 'rxjs/operators'; import { anomalyDataChange } from '../explorer_charts/explorer_charts_container_service'; -import { VIEW_BY_JOB_LABEL } from '../explorer_constants'; import { explorerService } from '../explorer_dashboard_service'; import { getDateFormatTz, getSelectionInfluencers, + getSelectionJobIds, getSelectionTimeRange, loadAnnotationsTableData, loadAnomaliesTableData, @@ -114,12 +114,7 @@ function loadExplorerData(config: LoadExplorerDataConfig): Observable d.id); - + const jobIds = getSelectionJobIds(selectedCells, selectedJobs); const timerange = getSelectionTimeRange( selectedCells, swimlaneBucketInterval.asSeconds(), diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/explorer.js b/x-pack/legacy/plugins/ml/public/application/explorer/explorer.js index 6a1c5339de1f5..8d26fafe8057c 100644 --- a/x-pack/legacy/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/legacy/plugins/ml/public/application/explorer/explorer.js @@ -120,8 +120,12 @@ export class Explorer extends React.Component { this.disableDragSelectOnMouseLeave = true; }, - onDragStart() { - if (ALLOW_CELL_RANGE_SELECTION) { + onDragStart(e) { + let target = e.target; + while (target && target !== document.body && !target.classList.contains('sl-cell')) { + target = target.parentNode; + } + if (ALLOW_CELL_RANGE_SELECTION && target !== document.body) { dragSelect$.next({ action: DRAG_SELECT_ACTION.DRAG_START, }); diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts index 0ab75b1db2972..c60b2d55d8686 100644 --- a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts +++ b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts @@ -35,10 +35,15 @@ export declare const getDefaultSwimlaneData: () => SwimlaneData; export declare const getInfluencers: (selectedJobs: any[]) => string[]; +export declare const getSelectionJobIds: ( + selectedCells: AppStateSelectedCells | undefined, + selectedJobs: ExplorerJob[] +) => string[]; + export declare const getSelectionInfluencers: ( selectedCells: AppStateSelectedCells | undefined, fieldName: string -) => any[]; +) => string[]; interface SelectionTimeRange { earliestMs: number; diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js index 14d356c0d1c81..4818856b8a8d2 100644 --- a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js @@ -222,6 +222,19 @@ export function getSelectionInfluencers(selectedCells, fieldName) { return []; } +export function getSelectionJobIds(selectedCells, selectedJobs) { + if ( + selectedCells !== undefined && + selectedCells.type !== SWIMLANE_TYPE.OVERALL && + selectedCells.viewByFieldName !== undefined && + selectedCells.viewByFieldName === VIEW_BY_JOB_LABEL + ) { + return selectedCells.lanes; + } + + return selectedJobs.map(d => d.id); +} + export function getSwimlaneBucketInterval(selectedJobs, swimlaneContainerWidth) { // Bucketing interval should be the maximum of the chart related interval (i.e. time range related) // and the max bucket span for the jobs shown in the chart. @@ -587,10 +600,7 @@ export async function loadAnomaliesTableData( tableSeverity, influencersFilterQuery ) { - const jobIds = - selectedCells !== undefined && selectedCells.viewByFieldName === VIEW_BY_JOB_LABEL - ? selectedCells.lanes - : selectedJobs.map(d => d.id); + const jobIds = getSelectionJobIds(selectedCells, selectedJobs); const influencers = getSelectionInfluencers(selectedCells, fieldName); const timeRange = getSelectionTimeRange(selectedCells, interval, bounds); diff --git a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 4b2d640b99bb3..7ee1c64e189a7 100644 --- a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -1623,8 +1623,20 @@ const TimeseriesChartIntl = injectI18n( }); } + let xOffset = LINE_CHART_ANOMALY_RADIUS * 2; + + // When the annotation area is hovered + if (circle.tagName.toLowerCase() === 'rect') { + const x = Number(circle.getAttribute('x')); + if (x < 0) { + // The beginning of the annotation area is outside of the focus chart, + // hence we need to adjust the x offset of a tooltip. + xOffset = Math.abs(x); + } + } + mlChartTooltipService.show(tooltipData, circle, { - x: LINE_CHART_ANOMALY_RADIUS * 2, + x: xOffset, y: 0, }); } diff --git a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 44b9fbc71f71a..75815f8bc48a7 100644 --- a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1203,7 +1203,7 @@ export class TimeSeriesExplorer extends React.Component { (fullRefresh === false || loading === false) && hasResults === true && (
- {/* Make sure ChartTooltip is inside this plain wrapping element without padding so positioning can be infered correctly. */} + {/* Make sure ChartTooltip is inside this plain wrapping element without padding so positioning can be inferred correctly. */} diff --git a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts index 173a4e31cfef6..0f68c56a18bf6 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts @@ -5,8 +5,6 @@ */ import { kfetch } from 'ui/kfetch'; -// @ts-ignore -import { addSystemApiHeader } from 'ui/system_api'; const API_BASE_URL = '/api/reporting/jobs'; @@ -64,7 +62,7 @@ class JobQueueClient { method: 'GET', pathname: `${API_BASE_URL}/list`, query, - headers: addSystemApiHeader({}), + asSystemRequest: true, }); }; @@ -72,7 +70,7 @@ class JobQueueClient { return kfetch({ method: 'GET', pathname: `${API_BASE_URL}/count`, - headers: addSystemApiHeader({}), + asSystemRequest: true, }); } @@ -80,7 +78,7 @@ class JobQueueClient { return kfetch({ method: 'GET', pathname: `${API_BASE_URL}/output/${jobId}`, - headers: addSystemApiHeader({}), + asSystemRequest: true, }); } @@ -88,7 +86,7 @@ class JobQueueClient { return kfetch({ method: 'GET', pathname: `${API_BASE_URL}/info/${jobId}`, - headers: addSystemApiHeader({}), + asSystemRequest: true, }); } } diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index 415cbeb7c2440..8c342ad2d037f 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -7,6 +7,8 @@ import React from 'react'; import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts'; import { getOr, get, isNumber } from 'lodash/fp'; +import deepmerge from 'deepmerge'; + import { useTimeZone } from '../../hooks'; import { AutoSizer } from '../auto_sizer'; import { ChartPlaceHolder } from './chart_place_holder'; @@ -52,8 +54,7 @@ export const BarChartBaseComponent = ({ const yAxisId = `stat-items-barchart-${data[0].key}-y`; const settings = { ...chartDefaultSettings, - theme, - ...get('configs.settings', chartConfigs), + ...deepmerge(get('configs.settings', chartConfigs), { theme }), }; return chartConfigs.width && chartConfigs.height ? ( diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 163b345da40bd..f0f28f1dc246c 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -5,6 +5,7 @@ */ import { EuiPanel } from '@elastic/eui'; +import deepEqual from 'fast-deep-equal'; import { getOr, isEmpty, isEqual, union } from 'lodash/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -34,6 +35,7 @@ import { IIndexPattern, Query, } from '../../../../../../../src/plugins/data/public'; +import { inputsModel } from '../../store'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 500; @@ -67,7 +69,7 @@ interface Props { sort: Sort; timelineTypeContext: TimelineTypeContextProps; toggleColumn: (column: ColumnHeader) => void; - utilityBar?: (totalCount: number) => React.ReactNode; + utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; } const EventsViewerComponent: React.FC = ({ @@ -171,7 +173,7 @@ const EventsViewerComponent: React.FC = ({ {headerFilterGroup} - {utilityBar?.(totalCountMinusDeleted)} + {utilityBar?.(refetch, totalCountMinusDeleted)}
= ({ export const EventsViewer = React.memo( EventsViewerComponent, (prevProps, nextProps) => - prevProps.browserFields === nextProps.browserFields && + isEqual(prevProps.browserFields, nextProps.browserFields) && prevProps.columns === nextProps.columns && prevProps.dataProviders === nextProps.dataProviders && prevProps.deletedEventIds === nextProps.deletedEventIds && prevProps.end === nextProps.end && - isEqual(prevProps.filters, nextProps.filters) && + deepEqual(prevProps.filters, nextProps.filters) && prevProps.height === nextProps.height && prevProps.id === nextProps.id && - prevProps.indexPattern === nextProps.indexPattern && + deepEqual(prevProps.indexPattern, nextProps.indexPattern) && prevProps.isLive === nextProps.isLive && prevProps.itemsPerPage === nextProps.itemsPerPage && prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions && diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx index 99d174d74f3f8..d56898cae7d23 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx @@ -5,7 +5,7 @@ */ import { isEqual } from 'lodash/fp'; -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useMemo, useEffect } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store'; @@ -35,7 +35,7 @@ export interface OwnProps { headerFilterGroup?: React.ReactNode; pageFilters?: esFilters.Filter[]; timelineTypeContext?: TimelineTypeContextProps; - utilityBar?: (totalCount: number) => React.ReactNode; + utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; } interface StateReduxProps { @@ -84,6 +84,10 @@ interface DispatchProps { type Props = OwnProps & StateReduxProps & DispatchProps; +const defaultTimelineTypeContext = { + loadingText: i18n.LOADING_EVENTS, +}; + const StatefulEventsViewerComponent: React.FC = ({ createTimeline, columns, @@ -99,16 +103,14 @@ const StatefulEventsViewerComponent: React.FC = ({ itemsPerPage, itemsPerPageOptions, kqlMode, - pageFilters = [], + pageFilters, query, removeColumn, start, showCheckboxes, showRowRenderers, sort, - timelineTypeContext = { - loadingText: i18n.LOADING_EVENTS, - }, + timelineTypeContext = defaultTimelineTypeContext, updateItemsPerPage, upsertColumn, utilityBar, @@ -153,18 +155,20 @@ const StatefulEventsViewerComponent: React.FC = ({ [columns, id, upsertColumn, removeColumn] ); + const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]); + return ( { const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getEvents = timelineSelectors.getEventsByIdSelector(); - const mapStateToProps = (state: State, { id, pageFilters = [], defaultModel }: OwnProps) => { + const mapStateToProps = (state: State, { id, defaultModel }: OwnProps) => { const input: inputsModel.InputsRange = getInputsTimeline(state); const events: TimelineModel = getEvents(state, id) ?? defaultModel; const { @@ -205,7 +209,7 @@ const makeMapStateToProps = () => { columns, dataProviders, deletedEventIds, - filters: [...getGlobalFiltersQuerySelector(state), ...pageFilters], + filters: getGlobalFiltersQuerySelector(state), id, isLive: input.policy.kind === 'interval', itemsPerPage, diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx index 528f02f0a845b..9fb59e2034b08 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx @@ -17,7 +17,6 @@ import { FlyoutButton } from './button'; import { Pane } from './pane'; import { timelineActions } from '../../store/actions'; import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/helpers'; -import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../lib/track_usage'; /** The height in pixels of the flyout header, exported for use in height calculations */ export const flyoutHeaderHeight: number = 60; @@ -101,10 +100,7 @@ export const FlyoutComponent = React.memo( dataProviders={dataProviders!} show={!show} timelineId={timelineId} - onOpen={() => { - track(METRIC_TYPE.LOADED, TELEMETRY_EVENT.TIMELINE_OPENED); - showTimeline({ id: timelineId, show: true }); - }} + onOpen={() => showTimeline({ id: timelineId, show: true })} /> ) diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap index aaefb4a83ded4..25374c63fa897 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap @@ -9,62 +9,11 @@ exports[`HeaderGlobal it renders 1`] = ` justifyContent="spaceBetween" wrap={true} > - - - - - - - - - - - - - - - - - - - - - - Add data - - - - + + `; diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx index 5a286532fabfc..a12fab8a4f5d9 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx @@ -39,55 +39,59 @@ interface HeaderGlobalProps { export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => ( - - - - - - - + + {({ indicesExist }) => ( + <> + + + + + + + - - - {({ indicesExist }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - key !== SiemPageName.detections, navTabs) - : navTabs - } - /> - ) : ( - key === SiemPageName.overview, navTabs)} - /> - ) - } - - - - + + {indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + key !== SiemPageName.detections, navTabs) + : navTabs + } + /> + ) : ( + key === SiemPageName.overview, navTabs)} + /> + )} + + + - - - - - + + + {indicesExistOrDataTemporarilyUnavailable(indicesExist) && ( + + + + )} - - - {i18n.BUTTON_ADD_DATA} - - - - + + + {i18n.BUTTON_ADD_DATA} + + + + + + )} + )); diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx index 32e71b3db575f..405a8f060948e 100644 --- a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx @@ -24,7 +24,12 @@ export const InspectButtonContainer = styled.div<{ show?: boolean }>` display: flex; flex-grow: 1; + > * { + max-width: 100%; + } + .${BUTTON_CLASS} { + pointer-events: none; opacity: 0; transition: opacity ${props => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease; } @@ -33,6 +38,7 @@ export const InspectButtonContainer = styled.div<{ show?: boolean }>` show && css` &:hover .${BUTTON_CLASS} { + pointer-events: auto; opacity: 1; } `} diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index cdd62c430a50c..f8853deeaed52 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -7,18 +7,14 @@ import React, { useState, useEffect, useCallback } from 'react'; import { ScaleType } from '@elastic/charts'; -import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; -import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingContent, EuiSelect } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSelect } from '@elastic/eui'; import { noop } from 'lodash/fp'; import * as i18n from './translations'; import { BarChart } from '../charts/barchart'; import { HeaderSection } from '../header_section'; -import { DEFAULT_DARK_MODE } from '../../../common/constants'; -import { useUiSetting$ } from '../../lib/kibana'; -import { Loader } from '../loader'; +import { MatrixLoader } from './matrix_loader'; import { Panel } from '../panel'; -import { getBarchartConfigs, getCustomChartData } from '../../components/matrix_histogram/utils'; +import { getBarchartConfigs, getCustomChartData } from './utils'; import { useQuery } from '../../containers/matrix_histogram/utils'; import { MatrixHistogramProps, @@ -31,7 +27,6 @@ import { InspectButtonContainer } from '../inspect'; export const MatrixHistogramComponent: React.FC = ({ - activePage, dataKey, defaultStackByOption, endDate, @@ -59,7 +54,6 @@ export const MatrixHistogramComponent: React.FC { const barchartConfigs = getBarchartConfigs({ from: startDate, @@ -70,20 +64,7 @@ export const MatrixHistogramComponent: React.FC(DEFAULT_DARK_MODE); - - const handleOnMouseEnter = useCallback(() => { - if (!showInspect) { - setShowInspect(true); - } - }, [showInspect, setShowInspect]); - const handleOnMouseLeave = useCallback(() => { - if (showInspect) { - setShowInspect(false); - } - }, [showInspect, setShowInspect]); - + const [isInitialLoading, setIsInitialLoading] = useState(true); const [selectedStackByOption, setSelectedStackByOption] = useState( defaultStackByOption ); @@ -127,12 +108,8 @@ export const MatrixHistogramComponent: React.FC - - = 0 ? subtitleWithCounts : null)} - > - - - {stackByOptions?.length > 1 && ( - - )} - - {headerChildren} - - - {loading ? ( - + if (hideHistogram) { + return null; + } + + return ( + + + {loading && !isInitialLoading && ( + + )} + + {isInitialLoading ? ( + <> + + + ) : ( <> + = 0 ? subtitleWithCounts : null)} + > + + + {stackByOptions?.length > 1 && ( + + )} + + {headerChildren} + + - - {loading && ( - - )} )} - ) : null; + ); }; export const MatrixHistogram = React.memo(MatrixHistogramComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/matrix_loader.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/matrix_loader.tsx new file mode 100644 index 0000000000000..769ef170898b0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/matrix_loader.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import styled from 'styled-components'; + +const StyledEuiFlexGroup = styled(EuiFlexGroup)` + height: 350px; /* to avoid jump when histogram loads */ +`; + +const MatrixLoaderComponent = () => ( + + + + + +); + +export const MatrixLoader = React.memo(MatrixLoaderComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts index 48ff9c94d9099..6e932f0c87347 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts @@ -3,11 +3,22 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ScaleType, niceTimeFormatter, Position } from '@elastic/charts'; +import { ScaleType, Position } from '@elastic/charts'; import { get, groupBy, map, toPairs } from 'lodash/fp'; import { UpdateDateRange, ChartSeriesData } from '../charts/common'; import { MatrixHistogramDataTypes, MatrixHistogramMappingTypes } from './types'; +import { histogramDateTimeFormatter } from '../utils'; + +interface GetBarchartConfigsProps { + from: number; + legendPosition?: Position; + to: number; + scaleType: ScaleType; + onBrushEnd: UpdateDateRange; + yTickFormatter?: (value: number) => string; + showLegend?: boolean; +} export const getBarchartConfigs = ({ from, @@ -17,22 +28,15 @@ export const getBarchartConfigs = ({ onBrushEnd, yTickFormatter, showLegend, -}: { - from: number; - legendPosition?: Position; - to: number; - scaleType: ScaleType; - onBrushEnd: UpdateDateRange; - yTickFormatter?: (value: number) => string; - showLegend?: boolean; -}) => ({ +}: GetBarchartConfigsProps) => ({ series: { xScaleType: scaleType || ScaleType.Time, yScaleType: ScaleType.Linear, stackAccessors: ['g'], }, axis: { - xTickFormatter: scaleType === ScaleType.Time ? niceTimeFormatter([from, to]) : undefined, + xTickFormatter: + scaleType === ScaleType.Time ? histogramDateTimeFormatter([from, to]) : undefined, yTickFormatter: yTickFormatter != null ? yTickFormatter diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx index 0d503e2db3d9d..39c48413737e2 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx @@ -21,7 +21,7 @@ StaticSwitch.displayName = 'StaticSwitch'; export interface JobSwitchProps { job: SiemJob; isSiemJobsLoading: boolean; - onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => void; + onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise; } // Based on ML Job/Datafeed States from x-pack/legacy/plugins/ml/common/constants/states.js @@ -48,9 +48,10 @@ export const JobSwitchComponent = ({ }: JobSwitchProps) => { const [isLoading, setIsLoading] = useState(false); const handleChange = useCallback( - e => { + async e => { setIsLoading(true); - onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); + await onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); + setIsLoading(false); }, [job, setIsLoading, onJobStateChange] ); @@ -58,7 +59,7 @@ export const JobSwitchComponent = ({ return ( - {isSiemJobsLoading || isLoading || isJobLoading(job.jobState, job.datafeedId) ? ( + {isSiemJobsLoading || isLoading || isJobLoading(job.jobState, job.datafeedState) ? ( ) : ( void + onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise ) => [ { name: i18n.COLUMN_JOB_NAME, @@ -92,7 +92,7 @@ const getPaginatedItems = (items: SiemJob[], pageIndex: number, pageSize: number export interface JobTableProps { isLoading: boolean; jobs: SiemJob[]; - onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => void; + onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise; } export const JobsTableComponent = ({ isLoading, jobs, onJobStateChange }: JobTableProps) => { diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx index 307be06424ee3..a41e84c163663 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx @@ -11,7 +11,7 @@ import React, { useContext, useReducer, useState } from 'react'; import styled from 'styled-components'; import { useKibana } from '../../lib/kibana'; -import { METRIC_TYPE, TELEMETRY_EVENT, trackUiAction as track } from '../../lib/track_usage'; +import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../lib/telemetry'; import { errorToToaster } from '../ml/api/error_to_toaster'; import { hasMlAdminPermissions } from '../ml/permissions/has_ml_admin_permissions'; import { MlCapabilitiesContext } from '../ml/permissions/ml_capabilities_provider'; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index b653624ec1f67..cebf9b90656ca 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -7,7 +7,7 @@ import { EuiTab, EuiTabs } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import React, { useEffect, useState, useCallback, useMemo } from 'react'; -import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage'; +import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/telemetry'; import { getSearch } from '../helpers'; import { TabNavigationProps, TabNavigationItemProps } from './types'; diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap index a05fb513dd7ef..69596ba8f3325 100644 --- a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap @@ -49,7 +49,7 @@ exports[`Stat Items Component disable charts it renders the default widget 1`] = show={true} >
( diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx index ff556a1a9bdfc..a224e0355b5d3 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx @@ -166,7 +166,7 @@ const StatefulTimelineComponent = React.memo( updateItemsPerPage, upsertColumn, }) => { - const [loading, signalIndexExists, signalIndexName] = useSignalIndex(); + const { loading, signalIndexExists, signalIndexName } = useSignalIndex(); const indexToAdd = useMemo(() => { if (signalIndexExists && signalIndexName != null && ['signal', 'all'].includes(eventType)) { diff --git a/x-pack/legacy/plugins/siem/public/components/utils.ts b/x-pack/legacy/plugins/siem/public/components/utils.ts new file mode 100644 index 0000000000000..42dd5b7c011aa --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/utils.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { niceTimeFormatByDay, timeFormatter } from '@elastic/charts'; +import moment from 'moment-timezone'; + +export const getDaysDiff = (minDate: moment.Moment, maxDate: moment.Moment) => { + const diff = maxDate.diff(minDate, 'days'); + + if (diff <= 1 && !minDate.isSame(maxDate)) { + return 2; // to return proper pattern from niceTimeFormatByDay + } + + return diff; +}; + +export const histogramDateTimeFormatter = (domain: [number, number] | null, fixedDiff?: number) => { + const diff = fixedDiff ?? getDaysDiff(moment(domain![0]), moment(domain![1])); + const format = niceTimeFormatByDay(diff); + return timeFormatter(format); +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts index 22fb837ffb801..6b3578bacf24c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts @@ -389,6 +389,7 @@ export const getPrePackagedRulesStatus = async ({ }: { signal: AbortSignal; }): Promise<{ + rules_custom_installed: number; rules_installed: number; rules_not_installed: number; rules_not_updated: number; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx index f7a30766ad7d8..d376a1d6ad178 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx @@ -22,11 +22,11 @@ import { useApolloClient } from '../../../utils/apollo_context'; import * as i18n from './translations'; interface FetchIndexPatternReturn { - browserFields: BrowserFields | null; + browserFields: BrowserFields; isLoading: boolean; indices: string[]; indicesExists: boolean; - indexPatterns: IIndexPattern | null; + indexPatterns: IIndexPattern; } type Return = [FetchIndexPatternReturn, Dispatch>]; @@ -35,8 +35,8 @@ export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return => const apolloClient = useApolloClient(); const [indices, setIndices] = useState(defaultIndices); const [indicesExists, setIndicesExists] = useState(false); - const [indexPatterns, setIndexPatterns] = useState(null); - const [browserFields, setBrowserFields] = useState(null); + const [indexPatterns, setIndexPatterns] = useState({ fields: [], title: '' }); + const [browserFields, setBrowserFields] = useState({}); const [isLoading, setIsLoading] = useState(false); const [, dispatchToaster] = useStateToaster(); diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index 2e776738547df..b30c3b211b1b8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -30,7 +30,7 @@ export const NewRuleSchema = t.intersection([ rule_id: t.string, saved_id: t.string, tags: t.array(t.string), - threats: t.array(t.unknown), + threat: t.array(t.unknown), to: t.string, updated_by: t.string, }), @@ -73,7 +73,7 @@ export const RuleSchema = t.intersection([ tags: t.array(t.string), type: t.string, to: t.string, - threats: t.array(t.unknown), + threat: t.array(t.unknown), updated_at: t.string, updated_by: t.string, }), diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx index ee34cad873021..14d40f9ffbc37 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState } from 'react'; import { useStateToaster, displaySuccessToast } from '../../../components/toasters'; import { errorToToaster } from '../../../components/ml/api/error_to_toaster'; @@ -18,6 +18,7 @@ interface Return { loading: boolean; loadingCreatePrePackagedRules: boolean; refetchPrePackagedRulesStatus: Func | null; + rulesCustomInstalled: number | null; rulesInstalled: number | null; rulesNotInstalled: number | null; rulesNotUpdated: number | null; @@ -47,13 +48,26 @@ export const usePrePackagedRules = ({ isAuthenticated, isSignalIndexExists, }: UsePrePackagedRuleProps): Return => { - const [rulesInstalled, setRulesInstalled] = useState(null); - const [rulesNotInstalled, setRulesNotInstalled] = useState(null); - const [rulesNotUpdated, setRulesNotUpdated] = useState(null); + const [rulesStatus, setRuleStatus] = useState< + Pick< + Return, + | 'createPrePackagedRules' + | 'refetchPrePackagedRulesStatus' + | 'rulesCustomInstalled' + | 'rulesInstalled' + | 'rulesNotInstalled' + | 'rulesNotUpdated' + > + >({ + createPrePackagedRules: null, + refetchPrePackagedRulesStatus: null, + rulesCustomInstalled: null, + rulesInstalled: null, + rulesNotInstalled: null, + rulesNotUpdated: null, + }); const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false); const [loading, setLoading] = useState(true); - const createPrePackagedRules = useRef(null); - const refetchPrePackagedRules = useRef(null); const [, dispatchToaster] = useStateToaster(); useEffect(() => { @@ -68,15 +82,25 @@ export const usePrePackagedRules = ({ }); if (isSubscribed) { - setRulesInstalled(prePackagedRuleStatusResponse.rules_installed); - setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed); - setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated); + setRuleStatus({ + createPrePackagedRules: createElasticRules, + refetchPrePackagedRulesStatus: fetchPrePackagedRules, + rulesCustomInstalled: prePackagedRuleStatusResponse.rules_custom_installed, + rulesInstalled: prePackagedRuleStatusResponse.rules_installed, + rulesNotInstalled: prePackagedRuleStatusResponse.rules_not_installed, + rulesNotUpdated: prePackagedRuleStatusResponse.rules_not_updated, + }); } } catch (error) { if (isSubscribed) { - setRulesInstalled(null); - setRulesNotInstalled(null); - setRulesNotUpdated(null); + setRuleStatus({ + createPrePackagedRules: null, + refetchPrePackagedRulesStatus: null, + rulesCustomInstalled: null, + rulesInstalled: null, + rulesNotInstalled: null, + rulesNotUpdated: null, + }); errorToToaster({ title: i18n.RULE_FETCH_FAILURE, error, dispatchToaster }); } } @@ -122,9 +146,14 @@ export const usePrePackagedRules = ({ iterationTryOfFetchingPrePackagedCount > 100) ) { setLoadingCreatePrePackagedRules(false); - setRulesInstalled(prePackagedRuleStatusResponse.rules_installed); - setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed); - setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated); + setRuleStatus({ + createPrePackagedRules: createElasticRules, + refetchPrePackagedRulesStatus: fetchPrePackagedRules, + rulesCustomInstalled: prePackagedRuleStatusResponse.rules_custom_installed, + rulesInstalled: prePackagedRuleStatusResponse.rules_installed, + rulesNotInstalled: prePackagedRuleStatusResponse.rules_not_installed, + rulesNotUpdated: prePackagedRuleStatusResponse.rules_not_updated, + }); displaySuccessToast(i18n.RULE_PREPACKAGED_SUCCESS, dispatchToaster); stopTimeOut(); resolve(true); @@ -146,8 +175,7 @@ export const usePrePackagedRules = ({ }; fetchPrePackagedRules(); - createPrePackagedRules.current = createElasticRules; - refetchPrePackagedRules.current = fetchPrePackagedRules; + return () => { isSubscribed = false; abortCtrl.abort(); @@ -157,10 +185,6 @@ export const usePrePackagedRules = ({ return { loading, loadingCreatePrePackagedRules, - refetchPrePackagedRulesStatus: refetchPrePackagedRules.current, - rulesInstalled, - rulesNotInstalled, - rulesNotUpdated, - createPrePackagedRules: createPrePackagedRules.current, + ...rulesStatus, }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx index 254e8cbdc9a88..af6e437255acd 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx @@ -36,7 +36,7 @@ export const useRules = (pagination: PaginationOptions, filterOptions: FilterOpt let isSubscribed = true; const abortCtrl = new AbortController(); - async function fetchData() { + async function fetchData(forceReload: boolean = false) { try { setLoading(true); const fetchRulesResult = await fetchRules({ @@ -59,7 +59,7 @@ export const useRules = (pagination: PaginationOptions, filterOptions: FilterOpt } fetchData(); - reFetchRules.current = fetchData; + reFetchRules.current = fetchData.bind(null, true); return () => { isSubscribed = false; abortCtrl.abort(); diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx index d225241875d4b..b93009c8ce2c2 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx @@ -24,10 +24,14 @@ interface Return { */ export const usePrivilegeUser = (): Return => { const [loading, setLoading] = useState(true); - const [isAuthenticated, setAuthenticated] = useState(null); - const [hasIndexManage, setHasIndexManage] = useState(null); - const [hasIndexWrite, setHasIndexWrite] = useState(null); - const [hasManageApiKey, setHasManageApiKey] = useState(null); + const [privilegeUser, setPrivilegeUser] = useState< + Pick + >({ + isAuthenticated: null, + hasIndexManage: null, + hasManageApiKey: null, + hasIndexWrite: null, + }); const [, dispatchToaster] = useStateToaster(); useEffect(() => { @@ -42,29 +46,31 @@ export const usePrivilegeUser = (): Return => { }); if (isSubscribed && privilege != null) { - setAuthenticated(privilege.is_authenticated); if (privilege.index != null && Object.keys(privilege.index).length > 0) { const indexName = Object.keys(privilege.index)[0]; - setHasIndexManage(privilege.index[indexName].manage); - setHasIndexWrite( - privilege.index[indexName].create || + setPrivilegeUser({ + isAuthenticated: privilege.is_authenticated, + hasIndexManage: privilege.index[indexName].manage, + hasIndexWrite: + privilege.index[indexName].create || privilege.index[indexName].create_doc || privilege.index[indexName].index || - privilege.index[indexName].write - ); - setHasManageApiKey( - privilege.cluster.manage_security || + privilege.index[indexName].write, + hasManageApiKey: + privilege.cluster.manage_security || privilege.cluster.manage_api_key || - privilege.cluster.manage_own_api_key - ); + privilege.cluster.manage_own_api_key, + }); } } } catch (error) { if (isSubscribed) { - setAuthenticated(false); - setHasIndexManage(false); - setHasIndexWrite(false); - setHasManageApiKey(false); + setPrivilegeUser({ + isAuthenticated: false, + hasIndexManage: false, + hasManageApiKey: false, + hasIndexWrite: false, + }); errorToToaster({ title: i18n.PRIVILEGE_FETCH_FAILURE, error, dispatchToaster }); } } @@ -80,5 +86,5 @@ export const usePrivilegeUser = (): Return => { }; }, []); - return { loading, isAuthenticated, hasIndexManage, hasManageApiKey, hasIndexWrite }; + return { loading, ...privilegeUser }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx index fa88a84fb1187..3dc6bac07be34 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx @@ -9,11 +9,16 @@ import React, { SetStateAction, useEffect, useState } from 'react'; import { fetchQuerySignals } from './api'; import { SignalSearchResponse } from './types'; -type Return = [ - boolean, - SignalSearchResponse | null, - React.Dispatch> -]; +type Func = () => void; + +interface Return { + loading: boolean; + data: SignalSearchResponse | null; + setQuery: React.Dispatch>; + response: string; + request: string; + refetch: Func | null; +} /** * Hook for using to get a Signals from the Detection Engine API @@ -21,9 +26,20 @@ type Return = [ * @param initialQuery query dsl object * */ -export const useQuerySignals = (initialQuery: object): Return => { +export const useQuerySignals = ( + initialQuery: object, + indexName?: string | null +): Return => { const [query, setQuery] = useState(initialQuery); - const [signals, setSignals] = useState | null>(null); + const [signals, setSignals] = useState< + Pick, 'data' | 'setQuery' | 'response' | 'request' | 'refetch'> + >({ + data: null, + response: '', + request: '', + setQuery, + refetch: null, + }); const [loading, setLoading] = useState(true); useEffect(() => { @@ -39,11 +55,23 @@ export const useQuerySignals = (initialQuery: object): Return(initialQuery: object): Return void; -type Return = [boolean, boolean | null, string | null, Func | null]; +interface Return { + loading: boolean; + signalIndexExists: boolean | null; + signalIndexName: string | null; + createDeSignalIndex: Func | null; +} /** * Hook for managing signal index @@ -23,9 +28,13 @@ type Return = [boolean, boolean | null, string | null, Func | null]; */ export const useSignalIndex = (): Return => { const [loading, setLoading] = useState(true); - const [signalIndexName, setSignalIndexName] = useState(null); - const [signalIndexExists, setSignalIndexExists] = useState(null); - const createDeSignalIndex = useRef(null); + const [signalIndex, setSignalIndex] = useState< + Pick + >({ + signalIndexExists: null, + signalIndexName: null, + createDeSignalIndex: null, + }); const [, dispatchToaster] = useStateToaster(); useEffect(() => { @@ -38,13 +47,19 @@ export const useSignalIndex = (): Return => { const signal = await getSignalIndex({ signal: abortCtrl.signal }); if (isSubscribed && signal != null) { - setSignalIndexName(signal.name); - setSignalIndexExists(true); + setSignalIndex({ + signalIndexExists: true, + signalIndexName: signal.name, + createDeSignalIndex: createIndex, + }); } } catch (error) { if (isSubscribed) { - setSignalIndexName(null); - setSignalIndexExists(false); + setSignalIndex({ + signalIndexExists: false, + signalIndexName: null, + createDeSignalIndex: createIndex, + }); if (error instanceof SignalIndexError && error.statusCode !== 404) { errorToToaster({ title: i18n.SIGNAL_GET_NAME_FAILURE, error, dispatchToaster }); } @@ -70,8 +85,11 @@ export const useSignalIndex = (): Return => { if (error instanceof PostSignalError && error.statusCode === 409) { fetchData(); } else { - setSignalIndexName(null); - setSignalIndexExists(false); + setSignalIndex({ + signalIndexExists: false, + signalIndexName: null, + createDeSignalIndex: createIndex, + }); errorToToaster({ title: i18n.SIGNAL_POST_FAILURE, error, dispatchToaster }); } } @@ -82,12 +100,11 @@ export const useSignalIndex = (): Return => { }; fetchData(); - createDeSignalIndex.current = createIndex; return () => { isSubscribed = false; abortCtrl.abort(); }; }, []); - return [loading, signalIndexExists, signalIndexName, createDeSignalIndex.current]; + return { loading, ...signalIndex }; }; diff --git a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx index e995d123b1b44..0336e4a9a977b 100644 --- a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx @@ -89,7 +89,8 @@ export const WithSource = React.memo(({ children, indexToAdd, s return [...configIndex, ...indexToAdd]; } return configIndex; - }, [configIndex, DEFAULT_INDEX_KEY, indexToAdd]); + }, [configIndex, indexToAdd]); + return ( query={sourceQuery} diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx index 03fe68ca1398d..f4eb088b6ad94 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getOr, isEmpty } from 'lodash/fp'; +import { getOr } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import React from 'react'; import { Query } from 'react-apollo'; @@ -84,12 +84,13 @@ class TimelineQueryComponent extends QueryTemplate< sortField, } = this.props; const defaultKibanaIndex = kibana.services.uiSettings.get(DEFAULT_INDEX_KEY); - const defaultIndex = isEmpty(indexPattern) - ? [ - ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []), - ...(['all', 'signal'].includes(eventType) ? indexToAdd : []), - ] - : indexPattern?.title.split(',') ?? []; + const defaultIndex = + indexPattern == null || (indexPattern != null && indexPattern.title === '') + ? [ + ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []), + ...(['all', 'signal'].includes(eventType) ? indexToAdd : []), + ] + : indexPattern?.title.split(',') ?? []; const variables: GetTimelineQuery.Variables = { fieldRequested: fields, filterQuery: createFilter(filterQuery), diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index 35599827ffe42..a9247403bf22c 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -4940,7 +4940,7 @@ "deprecationReason": null }, { - "name": "threats", + "name": "threat", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index 4b6825268403c..6a24ffcc13020 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -1015,7 +1015,7 @@ export interface RuleField { tags?: Maybe; - threats?: Maybe; + threat?: Maybe; type?: Maybe; diff --git a/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts b/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts new file mode 100644 index 0000000000000..856b7783a5367 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics'; + +import { SetupPlugins } from '../../plugin'; +export { telemetryMiddleware } from './middleware'; + +export { METRIC_TYPE }; + +type TrackFn = (type: UiStatsMetricType, event: string | string[], count?: number) => void; + +let _track: TrackFn; + +export const track: TrackFn = (type, event, count) => { + try { + _track(type, event, count); + } catch (error) { + // ignore failed tracking call + } +}; + +export const initTelemetry = (usageCollection: SetupPlugins['usageCollection'], appId: string) => { + try { + _track = usageCollection.reportUiStats.bind(null, appId); + } catch (error) { + // ignore failed setup here, as we'll just have an inert tracker + } +}; + +export enum TELEMETRY_EVENT { + // Detections + SIEM_RULE_ENABLED = 'siem_rule_enabled', + SIEM_RULE_DISABLED = 'siem_rule_disabled', + CUSTOM_RULE_ENABLED = 'custom_rule_enabled', + CUSTOM_RULE_DISABLED = 'custom_rule_disabled', + + // ML + SIEM_JOB_ENABLED = 'siem_job_enabled', + SIEM_JOB_DISABLED = 'siem_job_disabled', + CUSTOM_JOB_ENABLED = 'custom_job_enabled', + CUSTOM_JOB_DISABLED = 'custom_job_disabled', + JOB_ENABLE_FAILURE = 'job_enable_failure', + JOB_DISABLE_FAILURE = 'job_disable_failure', + + // Timeline + TIMELINE_OPENED = 'open_timeline', + TIMELINE_SAVED = 'timeline_saved', + TIMELINE_NAMED = 'timeline_named', + + // UI Interactions + TAB_CLICKED = 'tab_', +} diff --git a/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts b/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts new file mode 100644 index 0000000000000..59c6cb3566907 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Action, Dispatch, MiddlewareAPI } from 'redux'; + +import { track, METRIC_TYPE, TELEMETRY_EVENT } from './'; +import { timelineActions } from '../../store/actions'; + +export const telemetryMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => (action: Action) => { + if (timelineActions.endTimelineSaving.match(action)) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.TIMELINE_SAVED); + } else if (timelineActions.updateTitle.match(action)) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.TIMELINE_NAMED); + } else if (timelineActions.showTimeline.match(action)) { + if (action.payload.show) { + track(METRIC_TYPE.LOADED, TELEMETRY_EVENT.TIMELINE_OPENED); + } + } + + return next(action); +}; diff --git a/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts b/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts deleted file mode 100644 index 47277653c816f..0000000000000 --- a/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// @ts-ignore -import { - createUiStatsReporter, - METRIC_TYPE, -} from '../../../../../../../src/legacy/core_plugins/ui_metric/public'; -import { APP_ID } from '../../../common/constants'; - -export const trackUiAction = createUiStatsReporter(APP_ID); -export { METRIC_TYPE }; - -export enum TELEMETRY_EVENT { - // ML - SIEM_JOB_ENABLED = 'siem_job_enabled', - SIEM_JOB_DISABLED = 'siem_job_disabled', - CUSTOM_JOB_ENABLED = 'custom_job_enabled', - CUSTOM_JOB_DISABLED = 'custom_job_disabled', - JOB_ENABLE_FAILURE = 'job_enable_failure', - JOB_DISABLE_FAILURE = 'job_disable_failure', - - // Timeline - TIMELINE_OPENED = 'open_timeline', - - // UI Interactions - TAB_CLICKED = 'tab_', -} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx index 51d8e2630459c..e65adcf3a6920 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx @@ -20,8 +20,7 @@ import { DispatchUpdateTimeline } from '../../../../components/open_timeline/typ import { combineQueries } from '../../../../components/timeline/helpers'; import { TimelineNonEcsData } from '../../../../graphql/types'; import { useKibana } from '../../../../lib/kibana'; -import { inputsSelectors, State } from '../../../../store'; -import { InputsRange } from '../../../../store/inputs/model'; +import { inputsSelectors, State, inputsModel } from '../../../../store'; import { timelineActions, timelineSelectors } from '../../../../store/timeline'; import { timelineDefaults, TimelineModel } from '../../../../store/timeline/model'; import { useApolloClient } from '../../../../utils/apollo_context'; @@ -46,7 +45,7 @@ import { CreateTimelineProps, SetEventsDeletedProps, SetEventsLoadingProps, - UpdateSignalsStatus, + UpdateSignalsStatusCallback, UpdateSignalsStatusProps, } from './types'; import { dispatchUpdateTimeline } from '../../../../components/open_timeline/helpers'; @@ -97,7 +96,7 @@ const SignalsTableComponent: React.FC = ({ clearEventsDeleted, clearEventsLoading, clearSelected, - defaultFilters = [], + defaultFilters, from, globalFilters, globalQuery, @@ -118,7 +117,9 @@ const SignalsTableComponent: React.FC = ({ const [showClearSelectionAction, setShowClearSelectionAction] = useState(false); const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); - const [{ browserFields, indexPatterns }] = useFetchIndexPatterns([signalsIndex]); + const [{ browserFields, indexPatterns }] = useFetchIndexPatterns( + signalsIndex !== '' ? [signalsIndex] : [] + ); const kibana = useKibana(); const getGlobalQuery = useCallback(() => { @@ -207,8 +208,8 @@ const SignalsTableComponent: React.FC = ({ setShowClearSelectionAction(true); }, [setSelectAll, setShowClearSelectionAction]); - const updateSignalsStatusCallback: UpdateSignalsStatus = useCallback( - async ({ signalIds, status }: UpdateSignalsStatusProps) => { + const updateSignalsStatusCallback: UpdateSignalsStatusCallback = useCallback( + async (refetchQuery: inputsModel.Refetch, { signalIds, status }: UpdateSignalsStatusProps) => { await updateSignalStatusAction({ query: showClearSelectionAction ? getGlobalQuery()?.filterQuery : undefined, signalIds: Object.keys(selectedEventIds), @@ -216,6 +217,7 @@ const SignalsTableComponent: React.FC = ({ setEventsDeleted: setEventsDeletedCallback, setEventsLoading: setEventsLoadingCallback, }); + refetchQuery(); }, [ getGlobalQuery, @@ -228,7 +230,7 @@ const SignalsTableComponent: React.FC = ({ // Callback for creating the SignalUtilityBar which receives totalCount from EventsViewer component const utilityBarCallback = useCallback( - (totalCount: number) => { + (refetchQuery: inputsModel.Refetch, totalCount: number) => { return ( = ({ selectedEventIds={selectedEventIds} showClearSelection={showClearSelectionAction} totalCount={totalCount} - updateSignalsStatus={updateSignalsStatusCallback} + updateSignalsStatus={updateSignalsStatusCallback.bind(null, refetchQuery)} /> ); }, @@ -283,13 +285,16 @@ const SignalsTableComponent: React.FC = ({ ); const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]); - const defaultFiltersMemo = useMemo( - () => [ - ...defaultFilters, - ...(filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters), - ], - [defaultFilters, filterGroup] - ); + const defaultFiltersMemo = useMemo(() => { + if (isEmpty(defaultFilters)) { + return filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters; + } else if (defaultFilters != null && !isEmpty(defaultFilters)) { + return [ + ...defaultFilters, + ...(filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters), + ]; + } + }, [defaultFilters, filterGroup]); const timelineTypeContext = useMemo( () => ({ @@ -304,6 +309,11 @@ const SignalsTableComponent: React.FC = ({ [additionalActions, canUserCRUD, selectAll] ); + const headerFilterGroup = useMemo( + () => , + [onFilterGroupChangedCallback] + ); + if (loading || isEmpty(signalsIndex)) { return ( @@ -319,9 +329,7 @@ const SignalsTableComponent: React.FC = ({ pageFilters={defaultFiltersMemo} defaultModel={signalsDefaultModel} end={to} - headerFilterGroup={ - - } + headerFilterGroup={headerFilterGroup} id={SIGNALS_PAGE_TIMELINE_ID} start={from} timelineTypeContext={timelineTypeContext} @@ -338,9 +346,8 @@ const makeMapStateToProps = () => { getTimeline(state, SIGNALS_PAGE_TIMELINE_ID) ?? timelineDefaults; const { deletedEventIds, isSelectAllChecked, loadingEventIds, selectedEventIds } = timeline; - const globalInputs: InputsRange = getGlobalInputs(state); + const globalInputs: inputsModel.InputsRange = getGlobalInputs(state); const { query, filters } = globalInputs; - return { globalQuery: query, globalFilters: filters, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts index 2366a103492ec..b3e7ed75cfb99 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts @@ -8,6 +8,7 @@ import ApolloClient from 'apollo-client'; import { Ecs } from '../../../../graphql/types'; import { TimelineModel } from '../../../../store/timeline/model'; +import { inputsModel } from '../../../../store'; export interface SetEventsLoadingProps { eventIds: string[]; @@ -24,6 +25,10 @@ export interface UpdateSignalsStatusProps { status: 'open' | 'closed'; } +export type UpdateSignalsStatusCallback = ( + refetchQuery: inputsModel.Refetch, + { signalIds, status }: UpdateSignalsStatusProps +) => void; export type UpdateSignalsStatus = ({ signalIds, status }: UpdateSignalsStatusProps) => void; export interface UpdateSignalStatusActionProps { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts index d475fd155ea25..2c5a1ddd9a010 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts @@ -9,6 +9,7 @@ import { SignalsHistogramOption } from './types'; export const signalsHistogramOptions: SignalsHistogramOption[] = [ { text: 'signal.rule.risk_score', value: 'signal.rule.risk_score' }, { text: 'signal.rule.severity', value: 'signal.rule.severity' }, + { text: 'signal.rule.threat.tactic.name', value: 'signal.rule.threat.tactic.name' }, { text: 'destination.ip', value: 'destination.ip' }, { text: 'event.action', value: 'event.action' }, { text: 'event.category', value: 'event.category' }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/helpers.tsx similarity index 91% rename from x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/helpers.tsx rename to x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/helpers.tsx index ed503e9872f0a..71a19d4595f6a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/helpers.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HistogramData, SignalsAggregation, SignalsBucket, SignalsGroupBucket } from '../types'; -import { SignalSearchResponse } from '../../../../../containers/detection_engine/signals/types'; -import * as i18n from '../translations'; +import { HistogramData, SignalsAggregation, SignalsBucket, SignalsGroupBucket } from './types'; +import { SignalSearchResponse } from '../../../../containers/detection_engine/signals/types'; +import * as i18n from './translations'; export const formatSignalsData = ( signalsData: SignalSearchResponse<{}, SignalsAggregation> | null diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx index 64bc7ba24c689..2cdafe38a7434 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx @@ -4,34 +4,50 @@ * you may not use this file except in compliance with the Elastic License. */ import { Position } from '@elastic/charts'; -import { EuiButton, EuiPanel, EuiSelect } from '@elastic/eui'; +import { EuiButton, EuiSelect, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import React, { memo, useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { isEmpty } from 'lodash/fp'; import { HeaderSection } from '../../../../components/header_section'; import { SignalsHistogram } from './signals_histogram'; import * as i18n from './translations'; import { Query } from '../../../../../../../../../src/plugins/data/common/query'; -import { esFilters } from '../../../../../../../../../src/plugins/data/common/es_query'; -import { SignalsHistogramOption, SignalsTotal } from './types'; +import { esFilters, esQuery } from '../../../../../../../../../src/plugins/data/common/es_query'; +import { RegisterQuery, SignalsHistogramOption, SignalsAggregation, SignalsTotal } from './types'; import { signalsHistogramOptions } from './config'; import { getDetectionEngineUrl } from '../../../../components/link_to'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; -import { useUiSetting$ } from '../../../../lib/kibana'; +import { useKibana, useUiSetting$ } from '../../../../lib/kibana'; +import { InspectButtonContainer } from '../../../../components/inspect'; +import { useQuerySignals } from '../../../../containers/detection_engine/signals/use_query'; +import { MatrixLoader } from '../../../../components/matrix_histogram/matrix_loader'; + +import { formatSignalsData, getSignalsHistogramQuery } from './helpers'; + +const StyledEuiPanel = styled(EuiPanel)` + position: relative; +`; const defaultTotalSignalsObj: SignalsTotal = { value: 0, relation: 'eq', }; +export const DETECTIONS_HISTOGRAM_ID = 'detections-histogram'; + interface SignalsHistogramPanelProps { defaultStackByOption?: SignalsHistogramOption; + deleteQuery?: ({ id }: { id: string }) => void; filters?: esFilters.Filter[]; from: number; query?: Query; legendPosition?: Position; loadingInitial?: boolean; + signalIndexName: string | null; + setQuery: (params: RegisterQuery) => void; showLinkToSignals?: boolean; showTotalSignalsCount?: boolean; stackByOptions?: SignalsHistogramOption[]; @@ -43,11 +59,14 @@ interface SignalsHistogramPanelProps { export const SignalsHistogramPanel = memo( ({ defaultStackByOption = signalsHistogramOptions[0], + deleteQuery, filters, query, from, legendPosition = 'right', loadingInitial = false, + setQuery, + signalIndexName, showLinkToSignals = false, showTotalSignalsCount = false, stackByOptions, @@ -55,11 +74,24 @@ export const SignalsHistogramPanel = memo( title = i18n.HISTOGRAM_HEADER, updateDateRange, }) => { + const [isInitialLoading, setIsInitialLoading] = useState(loadingInitial || true); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const [totalSignalsObj, setTotalSignalsObj] = useState(defaultTotalSignalsObj); const [selectedStackByOption, setSelectedStackByOption] = useState( defaultStackByOption ); + const { + loading: isLoadingSignals, + data: signalsData, + setQuery: setSignalsQuery, + response, + request, + refetch, + } = useQuerySignals<{}, SignalsAggregation>( + getSignalsHistogramQuery(selectedStackByOption.value, from, to, []), + signalIndexName + ); + const kibana = useKibana(); const totalSignals = useMemo( () => @@ -77,34 +109,106 @@ export const SignalsHistogramPanel = memo( ); }, []); + const formattedSignalsData = useMemo(() => formatSignalsData(signalsData), [signalsData]); + + useEffect(() => { + if (!loadingInitial && isInitialLoading && !isLoadingSignals && signalsData) { + setIsInitialLoading(false); + } + }, [loadingInitial, isLoadingSignals, signalsData]); + + useEffect(() => { + return () => { + if (deleteQuery) { + deleteQuery({ id: DETECTIONS_HISTOGRAM_ID }); + } + }; + }, []); + + useEffect(() => { + if (refetch != null && setQuery != null) { + setQuery({ + id: DETECTIONS_HISTOGRAM_ID, + inspect: { + dsl: [request], + response: [response], + }, + loading: isLoadingSignals, + refetch, + }); + } + }, [setQuery, isLoadingSignals, signalsData, response, request, refetch]); + + useEffect(() => { + setTotalSignalsObj( + signalsData?.hits.total ?? { + value: 0, + relation: 'eq', + } + ); + }, [signalsData]); + + useEffect(() => { + const converted = esQuery.buildEsQuery( + undefined, + query != null ? [query] : [], + filters?.filter(f => f.meta.disabled === false) ?? [], + { + ...esQuery.getEsQueryConfig(kibana.services.uiSettings), + dateFormatTZ: undefined, + } + ); + + setSignalsQuery( + getSignalsHistogramQuery( + selectedStackByOption.value, + from, + to, + !isEmpty(converted) ? [converted] : [] + ) + ); + }, [selectedStackByOption.value, from, to, query, filters]); + return ( - - - {stackByOptions && ( - - )} - {showLinkToSignals && ( - {i18n.VIEW_SIGNALS} + + + {isInitialLoading ? ( + <> + + + + ) : ( + <> + + {stackByOptions && ( + + )} + {showLinkToSignals && ( + {i18n.VIEW_SIGNALS} + )} + + + + )} - - - - + + ); } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx new file mode 100644 index 0000000000000..9d2af1e78f285 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + Axis, + Chart, + getAxisId, + getSpecId, + HistogramBarSeries, + Position, + Settings, +} from '@elastic/charts'; +import React from 'react'; +import { EuiProgress } from '@elastic/eui'; +import { useTheme } from '../../../../components/charts/common'; +import { histogramDateTimeFormatter } from '../../../../components/utils'; +import { HistogramData } from './types'; + +interface HistogramSignalsProps { + from: number; + legendPosition?: Position; + loading: boolean; + to: number; + data: HistogramData[]; + updateDateRange: (min: number, max: number) => void; +} + +export const SignalsHistogram = React.memo( + ({ to, from, legendPosition = 'right', data, updateDateRange, loading }) => { + const theme = useTheme(); + + return ( + <> + {loading && ( + + )} + + + + + + + + + + + + ); + } +); +SignalsHistogram.displayName = 'SignalsHistogram'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx deleted file mode 100644 index d4db8cc7c37e8..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - Axis, - Chart, - getAxisId, - getSpecId, - HistogramBarSeries, - niceTimeFormatByDay, - Position, - Settings, - timeFormatter, -} from '@elastic/charts'; -import React, { useEffect, useMemo } from 'react'; -import { EuiLoadingContent } from '@elastic/eui'; -import { isEmpty } from 'lodash/fp'; -import { useQuerySignals } from '../../../../../containers/detection_engine/signals/use_query'; -import { Query } from '../../../../../../../../../../src/plugins/data/common/query'; -import { esFilters, esQuery } from '../../../../../../../../../../src/plugins/data/common/es_query'; -import { SignalsAggregation, SignalsTotal } from '../types'; -import { formatSignalsData, getSignalsHistogramQuery } from './helpers'; -import { useTheme } from '../../../../../components/charts/common'; -import { useKibana } from '../../../../../lib/kibana'; - -interface HistogramSignalsProps { - filters?: esFilters.Filter[]; - from: number; - legendPosition?: Position; - loadingInitial: boolean; - query?: Query; - setTotalSignalsCount: React.Dispatch; - stackByField: string; - to: number; - updateDateRange: (min: number, max: number) => void; -} - -export const SignalsHistogram = React.memo( - ({ - to, - from, - query, - filters, - legendPosition = 'right', - loadingInitial, - setTotalSignalsCount, - stackByField, - updateDateRange, - }) => { - const [isLoadingSignals, signalsData, setQuery] = useQuerySignals<{}, SignalsAggregation>( - getSignalsHistogramQuery(stackByField, from, to, []) - ); - const theme = useTheme(); - const kibana = useKibana(); - - const formattedSignalsData = useMemo(() => formatSignalsData(signalsData), [signalsData]); - - useEffect(() => { - setTotalSignalsCount( - signalsData?.hits.total ?? { - value: 0, - relation: 'eq', - } - ); - }, [signalsData]); - - useEffect(() => { - const converted = esQuery.buildEsQuery( - undefined, - query != null ? [query] : [], - filters?.filter(f => f.meta.disabled === false) ?? [], - { - ...esQuery.getEsQueryConfig(kibana.services.uiSettings), - dateFormatTZ: undefined, - } - ); - - setQuery( - getSignalsHistogramQuery(stackByField, from, to, !isEmpty(converted) ? [converted] : []) - ); - }, [stackByField, from, to, query, filters]); - - return ( - <> - {loadingInitial || isLoadingSignals ? ( - - ) : ( - - - - - - - - - - )} - - ); - } -); -SignalsHistogram.displayName = 'SignalsHistogram'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts index 4eb10852450ad..6ef4cecc4ec8b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { inputsModel } from '../../../../store'; + export interface SignalsHistogramOption { text: string; value: string; @@ -38,3 +40,10 @@ export interface SignalsTotal { value: number; relation: string; } + +export interface RegisterQuery { + id: string; + inspect: inputsModel.InspectQuery | null; + loading: boolean; + refetch: inputsModel.Refetch; +} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx index fc1110e382847..e7cdc3345c031 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx @@ -26,7 +26,7 @@ export const useSignalInfo = ({ ruleId = null }: SignalInfo): Return => { ); - const [loading, signals] = useQuerySignals(buildLastSignalsQuery(ruleId)); + const { loading, data: signals } = useQuerySignals(buildLastSignalsQuery(ruleId)); useEffect(() => { if (signals != null) { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx index bbaccb7882484..0f6a51e52cd2e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx @@ -154,12 +154,12 @@ export const useUserInfo = (): State => { hasIndexWrite: hasApiIndexWrite, hasManageApiKey: hasApiManageApiKey, } = usePrivilegeUser(); - const [ - indexNameLoading, - isApiSignalIndexExists, - apiSignalIndexName, - createSignalIndex, - ] = useSignalIndex(); + const { + loading: indexNameLoading, + signalIndexExists: isApiSignalIndexExists, + signalIndexName: apiSignalIndexName, + createDeSignalIndex: createSignalIndex, + } = useSignalIndex(); const uiCapabilities = useKibana().services.application.capabilities; const capabilitiesCanUserCRUD: boolean = @@ -172,46 +172,50 @@ export const useUserInfo = (): State => { }, [loading, privilegeLoading, indexNameLoading]); useEffect(() => { - if (hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) { + if (!loading && hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) { dispatch({ type: 'updateHasIndexManage', hasIndexManage: hasApiIndexManage }); } - }, [hasIndexManage, hasApiIndexManage]); + }, [loading, hasIndexManage, hasApiIndexManage]); useEffect(() => { - if (hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) { + if (!loading && hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) { dispatch({ type: 'updateHasIndexWrite', hasIndexWrite: hasApiIndexWrite }); } - }, [hasIndexWrite, hasApiIndexWrite]); + }, [loading, hasIndexWrite, hasApiIndexWrite]); useEffect(() => { - if (hasManageApiKey !== hasApiManageApiKey && hasApiManageApiKey != null) { + if (!loading && hasManageApiKey !== hasApiManageApiKey && hasApiManageApiKey != null) { dispatch({ type: 'updateHasManageApiKey', hasManageApiKey: hasApiManageApiKey }); } - }, [hasManageApiKey, hasApiManageApiKey]); + }, [loading, hasManageApiKey, hasApiManageApiKey]); useEffect(() => { - if (isSignalIndexExists !== isApiSignalIndexExists && isApiSignalIndexExists != null) { + if ( + !loading && + isSignalIndexExists !== isApiSignalIndexExists && + isApiSignalIndexExists != null + ) { dispatch({ type: 'updateIsSignalIndexExists', isSignalIndexExists: isApiSignalIndexExists }); } - }, [isSignalIndexExists, isApiSignalIndexExists]); + }, [loading, isSignalIndexExists, isApiSignalIndexExists]); useEffect(() => { - if (isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) { + if (!loading && isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) { dispatch({ type: 'updateIsAuthenticated', isAuthenticated: isApiAuthenticated }); } - }, [isAuthenticated, isApiAuthenticated]); + }, [loading, isAuthenticated, isApiAuthenticated]); useEffect(() => { - if (canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { + if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); } - }, [canUserCRUD, capabilitiesCanUserCRUD]); + }, [loading, canUserCRUD, capabilitiesCanUserCRUD]); useEffect(() => { - if (signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { + if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); } - }, [signalIndexName, apiSignalIndexName]); + }, [loading, signalIndexName, apiSignalIndexName]); useEffect(() => { if ( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx index 9e292fa69b2c4..e49cd2b36c7b8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -111,6 +111,10 @@ const DetectionEnginePageComponent: React.FC [detectionsTabs, tabName] ); + const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ + signalIndexName, + ]); + if (isUserAuthenticated != null && !isUserAuthenticated && !loading) { return ( @@ -131,7 +135,7 @@ const DetectionEnginePageComponent: React.FC return ( <> {hasIndexWrite != null && !hasIndexWrite && } - + {({ indicesExist, indexPattern }) => { return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( @@ -164,10 +168,13 @@ const DetectionEnginePageComponent: React.FC {tabName === DetectionEngineTab.signals && ( <> ({ tags: [], to: 'now', type: 'saved_query', - threats: [], + threat: [], version: 1, }); @@ -87,7 +87,7 @@ export const mockTableData: TableData[] = [ saved_id: "Garrett's IP", severity: 'low', tags: [], - threats: [], + threat: [], timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', timeline_title: 'Untitled timeline', to: 'now', @@ -136,7 +136,7 @@ export const mockTableData: TableData[] = [ saved_id: "Garrett's IP", severity: 'low', tags: [], - threats: [], + threat: [], timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', timeline_title: 'Untitled timeline', to: 'now', diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx index d6bf8643fff1c..6212c2067384d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx @@ -21,6 +21,7 @@ import { displayErrorToast, displaySuccessToast, } from '../../../../components/toasters'; +import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../../lib/telemetry'; import * as i18n from '../translations'; import { bucketRulesResponse } from './helpers'; @@ -38,12 +39,7 @@ export const duplicateRulesAction = async ( const ruleIds = rules.map(r => r.id); dispatch({ type: 'updateLoading', ids: ruleIds, isLoading: true }); const duplicatedRules = await duplicateRules({ rules }); - dispatch({ type: 'updateLoading', ids: ruleIds, isLoading: false }); - dispatch({ - type: 'updateRules', - rules: duplicatedRules, - appendRuleId: rules[rules.length - 1].id, - }); + dispatch({ type: 'refresh' }); displaySuccessToast( i18n.SUCCESSFULLY_DUPLICATED_RULES(duplicatedRules.length), dispatchToaster @@ -114,6 +110,19 @@ export const enableRulesAction = async ( dispatchToaster ); } + + if (rules.some(rule => rule.immutable)) { + track( + METRIC_TYPE.COUNT, + enabled ? TELEMETRY_EVENT.SIEM_RULE_ENABLED : TELEMETRY_EVENT.SIEM_RULE_DISABLED + ); + } + if (rules.some(rule => !rule.immutable)) { + track( + METRIC_TYPE.COUNT, + enabled ? TELEMETRY_EVENT.CUSTOM_RULE_ENABLED : TELEMETRY_EVENT.CUSTOM_RULE_DISABLED + ); + } } catch (e) { displayErrorToast(errorTitle, [e.message], dispatchToaster); dispatch({ type: 'updateLoading', ids, isLoading: false }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts index 3616d4dbaad24..9a523536694d9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts @@ -50,3 +50,16 @@ export const bucketRulesResponse = (response: Array) => }, { rules: [], errors: [] } ); + +export const showRulesTable = ({ + isInitialLoad, + rulesCustomInstalled, + rulesInstalled, +}: { + isInitialLoad: boolean; + rulesCustomInstalled: number | null; + rulesInstalled: number | null; +}) => + !isInitialLoad && + ((rulesCustomInstalled != null && rulesCustomInstalled > 0) || + (rulesInstalled != null && rulesInstalled > 0)); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index a4e7d7a3615cc..b304d77f2e276 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -6,7 +6,6 @@ import { EuiBasicTable, - EuiButton, EuiContextMenuPanel, EuiEmptyPrompt, EuiLoadingContent, @@ -40,9 +39,9 @@ import * as i18n from '../translations'; import { EuiBasicTableOnChange, TableData } from '../types'; import { getBatchItems } from './batch_actions'; import { getColumns } from './columns'; +import { showRulesTable } from './helpers'; import { allRulesReducer, State } from './reducer'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../components/link_to/redirect_to_detection_engine'; const initialState: State = { isLoading: true, @@ -69,6 +68,7 @@ interface AllRulesProps { loading: boolean; loadingCreatePrePackagedRules: boolean; refetchPrePackagedRulesStatus: () => void; + rulesCustomInstalled: number | null; rulesInstalled: number | null; rulesNotInstalled: number | null; rulesNotUpdated: number | null; @@ -91,6 +91,7 @@ export const AllRules = React.memo( loading, loadingCreatePrePackagedRules, refetchPrePackagedRulesStatus, + rulesCustomInstalled, rulesInstalled, rulesNotInstalled, rulesNotUpdated, @@ -109,6 +110,7 @@ export const AllRules = React.memo( dispatch, ] = useReducer(allRulesReducer, initialState); const history = useHistory(); + const [oldRefreshToggle, setOldRefreshToggle] = useState(refreshToggle); const [isInitialLoad, setIsInitialLoad] = useState(true); const [isGlobalLoading, setIsGlobalLoad] = useState(false); const [, dispatchToaster] = useStateToaster(); @@ -131,20 +133,16 @@ export const AllRules = React.memo( const tableOnChangeCallback = useCallback( ({ page, sort }: EuiBasicTableOnChange) => { - dispatch({ - type: 'updatePagination', - pagination: { ...pagination, page: page.index + 1, perPage: page.size }, - }); dispatch({ type: 'updateFilterOptions', filterOptions: { - ...filterOptions, sortField: 'enabled', // Only enabled is supported for sorting currently sortOrder: sort?.direction ?? 'desc', }, + pagination: { page: page.index + 1, perPage: page.size }, }); }, - [dispatch, filterOptions, pagination] + [dispatch] ); const columns = useMemo(() => { @@ -176,11 +174,18 @@ export const AllRules = React.memo( }, [importCompleteToggle]); useEffect(() => { - if (reFetchRulesData != null) { + if (!isInitialLoad && reFetchRulesData != null && oldRefreshToggle !== refreshToggle) { + setOldRefreshToggle(refreshToggle); reFetchRulesData(); + refetchPrePackagedRulesStatus(); } - refetchPrePackagedRulesStatus(); - }, [refreshToggle, reFetchRulesData, refetchPrePackagedRulesStatus]); + }, [ + isInitialLoad, + refreshToggle, + oldRefreshToggle, + reFetchRulesData, + refetchPrePackagedRulesStatus, + ]); useEffect(() => { if (reFetchRulesData != null) { @@ -220,16 +225,18 @@ export const AllRules = React.memo( dispatch({ type: 'updateFilterOptions', filterOptions: { - ...filterOptions, ...newFilterOptions, }, - }); - dispatch({ - type: 'updatePagination', - pagination: { ...pagination, page: 1 }, + pagination: { page: 1 }, }); }, []); + const emptyPrompt = useMemo(() => { + return ( + {i18n.NO_RULES}} titleSize="xs" body={i18n.NO_RULES_BODY} /> + ); + }, []); + return ( <> ( <> - {rulesInstalled != null && rulesInstalled > 0 && ( + {((rulesCustomInstalled && rulesCustomInstalled > 0) || + (rulesInstalled != null && rulesInstalled > 0)) && ( - + )} - {isInitialLoad && isEmpty(tableData) && ( + {isInitialLoad && ( )} - {isGlobalLoading && !isEmpty(tableData) && ( + {isGlobalLoading && !isEmpty(tableData) && !isInitialLoad && ( )} - {isEmpty(tableData) && prePackagedRuleStatus === 'ruleNotInstalled' && ( - - )} - {!isEmpty(tableData) && ( + {rulesCustomInstalled != null && + rulesCustomInstalled === 0 && + prePackagedRuleStatus === 'ruleNotInstalled' && ( + + )} + {showRulesTable({ isInitialLoad, rulesCustomInstalled, rulesInstalled }) && ( <> @@ -304,24 +318,7 @@ export const AllRules = React.memo( isSelectable={!hasNoPermissions ?? false} itemId="id" items={tableData} - noItemsMessage={ - {i18n.NO_RULES}} - titleSize="xs" - body={i18n.NO_RULES_BODY} - actions={ - - {i18n.ADD_NEW_RULE} - - } - /> - } + noItemsMessage={emptyPrompt} onChange={tableOnChangeCallback} pagination={{ pageIndex: pagination.page - 1, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts index 74ce8f2847faa..3634a16cbf6ac 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts @@ -31,9 +31,13 @@ export type Action = | { type: 'setExportPayload'; exportPayload?: Rule[] } | { type: 'setSelected'; selectedItems: TableData[] } | { type: 'updateLoading'; ids: string[]; isLoading: boolean } - | { type: 'updateRules'; rules: Rule[]; appendRuleId?: string; pagination?: PaginationOptions } - | { type: 'updatePagination'; pagination: PaginationOptions } - | { type: 'updateFilterOptions'; filterOptions: FilterOptions } + | { type: 'updateRules'; rules: Rule[]; pagination?: PaginationOptions } + | { type: 'updatePagination'; pagination: Partial } + | { + type: 'updateFilterOptions'; + filterOptions: Partial; + pagination: Partial; + } | { type: 'failure' }; export const allRulesReducer = (state: State, action: Action): State => { @@ -56,18 +60,10 @@ export const allRulesReducer = (state: State, action: Action): State => { } const ruleIds = state.rules.map(r => r.rule_id); - const appendIdx = - action.appendRuleId != null ? state.rules.findIndex(r => r.id === action.appendRuleId) : -1; const updatedRules = action.rules.reverse().reduce((rules, updatedRule) => { let newRules = rules; if (ruleIds.includes(updatedRule.rule_id)) { newRules = newRules.map(r => (updatedRule.rule_id === r.rule_id ? updatedRule : r)); - } else if (appendIdx !== -1) { - newRules = [ - ...newRules.slice(0, appendIdx + 1), - updatedRule, - ...newRules.slice(appendIdx + 1, newRules.length), - ]; } else { newRules = [...newRules, updatedRule]; } @@ -90,25 +86,28 @@ export const allRulesReducer = (state: State, action: Action): State => { rules: updatedRules, tableData: formatRules(updatedRules), selectedItems: updatedSelectedItems, - pagination: { - ...state.pagination, - total: - action.appendRuleId != null - ? state.pagination.total + action.rules.length - : state.pagination.total, - }, }; } case 'updatePagination': { return { ...state, - pagination: action.pagination, + pagination: { + ...state.pagination, + ...action.pagination, + }, }; } case 'updateFilterOptions': { return { ...state, - filterOptions: action.filterOptions, + filterOptions: { + ...state.filterOptions, + ...action.filterOptions, + }, + pagination: { + ...state.pagination, + ...action.pagination, + }, }; } case 'deleteRules': { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx index daf519f5af695..c54a2e8d49844 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx @@ -21,6 +21,8 @@ import { TagsFilterPopover } from './tags_filter_popover'; interface RulesTableFiltersProps { onFilterChanged: (filterOptions: Partial) => void; + rulesCustomInstalled: number | null; + rulesInstalled: number | null; } /** @@ -29,7 +31,11 @@ interface RulesTableFiltersProps { * * @param onFilterChanged change listener to be notified on filter changes */ -const RulesTableFiltersComponent = ({ onFilterChanged }: RulesTableFiltersProps) => { +const RulesTableFiltersComponent = ({ + onFilterChanged, + rulesCustomInstalled, + rulesInstalled, +}: RulesTableFiltersProps) => { const [filter, setFilter] = useState(''); const [selectedTags, setSelectedTags] = useState([]); const [showCustomRules, setShowCustomRules] = useState(false); @@ -84,13 +90,17 @@ const RulesTableFiltersComponent = ({ onFilterChanged }: RulesTableFiltersProps) withNext > {i18n.ELASTIC_RULES} + {rulesInstalled != null ? ` (${rulesInstalled})` : ''} - {i18n.CUSTOM_RULES} + <> + {i18n.CUSTOM_RULES} + {rulesCustomInstalled != null ? ` (${rulesCustomInstalled})` : ''} + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index 011c008c5b2d2..e1cbc6ee92393 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -24,7 +24,7 @@ import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_ import { FilterLabel } from './filter_label'; import * as i18n from './translations'; -import { BuildQueryBarDescription, BuildThreatsDescription, ListItems } from './types'; +import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types'; import { SeverityBadge } from '../severity_badge'; import ListTreeIcon from './assets/list_tree_icon.svg'; @@ -94,7 +94,7 @@ export const buildQueryBarDescription = ({ return items; }; -const ThreatsEuiFlexGroup = styled(EuiFlexGroup)` +const ThreatEuiFlexGroup = styled(EuiFlexGroup)` .euiFlexItem { margin-bottom: 0px; } @@ -114,25 +114,22 @@ const ReferenceLinkItem = styled(EuiButtonEmpty)` } `; -export const buildThreatsDescription = ({ - label, - threats, -}: BuildThreatsDescription): ListItems[] => { - if (threats.length > 0) { +export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => { + if (threat.length > 0) { return [ { title: label, description: ( - - {threats.map((threat, index) => { - const tactic = tacticsOptions.find(t => t.id === threat.tactic.id); + + {threat.map((singleThreat, index) => { + const tactic = tacticsOptions.find(t => t.id === singleThreat.tactic.id); return ( - - + + {tactic != null ? tactic.text : ''} - {threat.techniques.map(technique => { + {singleThreat.technique.map(technique => { const myTechnique = techniquesOptions.find(t => t.id === technique.id); return ( @@ -153,7 +150,7 @@ export const buildThreatsDescription = ({ ); })} - + ), }, ]; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx index 8cf1601e2c4b6..f1d2609cde8fe 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx @@ -24,7 +24,7 @@ import { buildQueryBarDescription, buildSeverityDescription, buildStringArrayDescription, - buildThreatsDescription, + buildThreatDescription, buildUnorderedListArrayDescription, buildUrlsDescription, } from './helpers'; @@ -116,11 +116,11 @@ const getDescriptionItem = ( savedId, indexPatterns, }); - } else if (field === 'threats') { - const threats: IMitreEnterpriseAttack[] = get(field, value).filter( - (threat: IMitreEnterpriseAttack) => threat.tactic.name !== 'none' + } else if (field === 'threat') { + const threat: IMitreEnterpriseAttack[] = get(field, value).filter( + (singleThreat: IMitreEnterpriseAttack) => singleThreat.tactic.name !== 'none' ); - return buildThreatsDescription({ label, threats }); + return buildThreatDescription({ label, threat }); } else if (field === 'description') { return [ { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts index d32fbcd725d12..c120d4a4106d0 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts @@ -27,7 +27,7 @@ export interface BuildQueryBarDescription { indexPatterns?: IIndexPattern; } -export interface BuildThreatsDescription { +export interface BuildThreatDescription { label: string; - threats: IMitreEnterpriseAttack[]; + threat: IMitreEnterpriseAttack[]; } diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts index 1202fe54ad194..7a28a16214df6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts @@ -9,9 +9,9 @@ import { IMitreAttack } from '../../types'; export const isMitreAttackInvalid = ( tacticName: string | null | undefined, - techniques: IMitreAttack[] | null | undefined + technique: IMitreAttack[] | null | undefined ) => { - if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(techniques))) { + if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(technique))) { return true; } return false; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx index f9a22c37cfdf0..d85be053065fc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx @@ -21,7 +21,7 @@ import styled from 'styled-components'; import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_techniques'; import * as Rulei18n from '../../translations'; import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports'; -import { threatsDefault } from '../step_about_rule/default_value'; +import { threatDefault } from '../step_about_rule/default_value'; import { IMitreEnterpriseAttack } from '../../types'; import { MyAddItemButton } from '../add_item_form'; import { isMitreAttackInvalid } from './helpers'; @@ -49,7 +49,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI const values = field.value as string[]; const newValues = [...values.slice(0, index), ...values.slice(index + 1)]; if (isEmpty(newValues)) { - field.setValue(threatsDefault); + field.setValue(threatDefault); } else { field.setValue(newValues); } @@ -62,10 +62,10 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI if (!isEmpty(values[values.length - 1])) { field.setValue([ ...values, - { tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] }, + { tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] }, ]); } else { - field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] }]); + field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] }]); } }, [field]); @@ -82,7 +82,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI { ...values[index], tactic: { id, reference, name }, - techniques: [], + technique: [], }, ...values.slice(index + 1), ]); @@ -96,7 +96,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI ...values.slice(0, index), { ...values[index], - techniques: selectedOptions, + technique: selectedOptions, }, ...values.slice(index + 1), ]); @@ -133,9 +133,9 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI ); const getSelectTechniques = (item: IMitreEnterpriseAttack, index: number, disabled: boolean) => { - const invalid = isMitreAttackInvalid(item.tactic.name, item.techniques); + const invalid = isMitreAttackInvalid(item.tactic.name, item.technique); const options = techniquesOptions.filter(t => t.tactics.includes(kebabCase(item.tactic.name))); - const selectedOptions = item.techniques.map(technic => ({ + const selectedOptions = item.technique.map(technic => ({ ...technic, label: `${technic.name} (${technic.id})`, // API doesn't allow for label field })); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts index 557e91691b6c7..bbc12800b7df5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts @@ -13,7 +13,7 @@ export const TACTIC = i18n.translate('xpack.siem.detectionEngine.mitreAttack.tac export const TECHNIQUE = i18n.translate( 'xpack.siem.detectionEngine.mitreAttack.techniquesDescription', { - defaultMessage: 'technique', + defaultMessage: 'techniques', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts index e03cc252ad729..d128696ec7253 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; export const STATUS = i18n.translate('xpack.siem.detectionEngine.ruleStatus.statusDescription', { - defaultMessage: 'Status', + defaultMessage: 'Last response', }); export const STATUS_AT = i18n.translate( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts index 92aca1cecf9f3..70bfc2bcddd62 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts @@ -7,11 +7,11 @@ import { AboutStepRule } from '../../types'; import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/search_super_select/translations'; -export const threatsDefault = [ +export const threatDefault = [ { framework: 'MITRE ATT&CK', tactic: { id: 'none', name: 'none', reference: 'none' }, - techniques: [], + technique: [], }, ]; @@ -28,5 +28,5 @@ export const stepAboutDefaultValue: AboutStepRule = { id: null, title: DEFAULT_TIMELINE_TITLE, }, - threats: threatsDefault, + threat: threatDefault, }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx index 8370af397bfec..4ae88b3014d19 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx @@ -227,12 +227,12 @@ const StepAboutRuleComponent: FC = ({ }} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx index 15b793a502840..22033dcf6b0f7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx @@ -139,7 +139,7 @@ export const schema: FormSchema = { ), labelAppend: OptionalFieldLabel, }, - threats: { + threat: { label: i18n.translate( 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldMitreThreatLabel', { @@ -155,7 +155,7 @@ export const schema: FormSchema = { const [{ value, path }] = args; let hasError = false; (value as IMitreEnterpriseAttack[]).forEach(v => { - if (isMitreAttackInvalid(v.tactic.name, v.techniques)) { + if (isMitreAttackInvalid(v.tactic.name, v.technique)) { hasError = true; } }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts index ce91e15cdcf0d..de6678b42df6f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts @@ -72,15 +72,7 @@ const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRul }; const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => { - const { - falsePositives, - references, - riskScore, - threats, - timeline, - isNew, - ...rest - } = aboutStepData; + const { falsePositives, references, riskScore, threat, timeline, isNew, ...rest } = aboutStepData; return { false_positives: falsePositives.filter(item => !isEmpty(item)), references: references.filter(item => !isEmpty(item)), @@ -91,12 +83,12 @@ const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => timeline_title: timeline.title, } : {}), - threats: threats - .filter(threat => threat.tactic.name !== 'none') - .map(threat => ({ - ...threat, + threat: threat + .filter(singleThreat => singleThreat.tactic.name !== 'none') + .map(singleThreat => ({ + ...singleThreat, framework: 'MITRE ATT&CK', - techniques: threat.techniques.map(technique => { + technique: singleThreat.technique.map(technique => { const { id, name, reference } = technique; return { id, name, reference }; }), diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index 3406d5bcd6950..1914f967813a1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -119,22 +119,17 @@ const RuleDetailsPageComponent: FC = ({ // This is used to re-trigger api rule status when user de/activate rule const [ruleEnabled, setRuleEnabled] = useState(null); const [ruleDetailTab, setRuleDetailTab] = useState(RuleDetailTabs.signals); - const { aboutRuleData, defineRuleData, scheduleRuleData } = getStepsData({ - rule, - detailsView: true, - }); + const { aboutRuleData, defineRuleData, scheduleRuleData } = + rule != null + ? getStepsData({ + rule, + detailsView: true, + }) + : { aboutRuleData: null, defineRuleData: null, scheduleRuleData: null }; const [lastSignals] = useSignalInfo({ ruleId }); const userHasNoPermissions = canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false; - if ( - isSignalIndexExists != null && - isAuthenticated != null && - (!isSignalIndexExists || !isAuthenticated) - ) { - return ; - } - const title = isLoading === true || rule === null ? : rule.name; const subTitle = useMemo( () => @@ -217,6 +212,10 @@ const RuleDetailsPageComponent: FC = ({ [rule, ruleDetailTab] ); + const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ + signalIndexName, + ]); + const updateDateRangeCallback = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); @@ -233,15 +232,23 @@ const RuleDetailsPageComponent: FC = ({ [ruleEnabled, setRuleEnabled] ); + if ( + isSignalIndexExists != null && + isAuthenticated != null && + (!isSignalIndexExists || !isAuthenticated) + ) { + return ; + } + return ( <> {hasIndexWrite != null && !hasIndexWrite && } {userHasNoPermissions && } - + {({ indicesExist, indexPattern }) => { return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - {({ to, from }) => ( + {({ to, from, deleteQuery, setQuery }) => ( @@ -348,9 +355,12 @@ const RuleDetailsPageComponent: FC = ({ { const defineRuleData: DefineStepRule | null = @@ -41,9 +43,9 @@ export const getStepsData = ({ rule != null ? { isNew: false, - ...pick(['description', 'name', 'references', 'severity', 'tags', 'threats'], rule), + ...pick(['description', 'name', 'references', 'severity', 'tags', 'threat'], rule), ...(detailsView ? { name: '' } : {}), - threats: rule.threats as IMitreEnterpriseAttack[], + threat: rule.threat as IMitreEnterpriseAttack[], falsePositives: rule.false_positives, riskScore: rule.risk_score, timeline: { @@ -52,15 +54,25 @@ export const getStepsData = ({ }, } : null; + + const from = dateMath.parse(rule.from) ?? moment(); + const interval = dateMath.parse(`now-${rule.interval}`) ?? moment(); + + const fromDuration = moment.duration(interval.diff(from)); + let fromHumanize = `${Math.floor(fromDuration.asHours())}h`; + + if (fromDuration.asSeconds() < 60) { + fromHumanize = `${Math.floor(fromDuration.asSeconds())}s`; + } else if (fromDuration.asMinutes() < 60) { + fromHumanize = `${Math.floor(fromDuration.asMinutes())}m`; + } + const scheduleRuleData: ScheduleStepRule | null = rule != null ? { isNew: false, ...pick(['enabled', 'interval'], rule), - from: - rule?.meta?.from != null - ? rule.meta.from.replace('now-', '') - : rule.from.replace('now-', ''), + from: fromHumanize, } : null; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx index 75b1ce71efbb6..1c0ed34e92793 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx @@ -5,14 +5,14 @@ */ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import React, { useCallback, useRef, useState } from 'react'; import { Redirect } from 'react-router-dom'; import { usePrePackagedRules } from '../../../containers/detection_engine/rules'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../components/link_to/redirect_to_detection_engine'; -import { FormattedRelativePreferenceDate } from '../../../components/formatted_date'; -import { getEmptyTagValue } from '../../../components/empty_value'; +import { + getDetectionEngineUrl, + getCreateRuleUrl, +} from '../../../components/link_to/redirect_to_detection_engine'; import { DetectionEngineHeaderPage } from '../components/detection_engine_header_page'; import { WrapperPage } from '../../../components/wrapper_page'; import { SpyRoute } from '../../../utils/route/spy_routes'; @@ -44,6 +44,7 @@ const RulesPageComponent: React.FC = () => { loading: prePackagedRuleLoading, loadingCreatePrePackagedRules, refetchPrePackagedRulesStatus, + rulesCustomInstalled, rulesInstalled, rulesNotInstalled, rulesNotUpdated, @@ -62,7 +63,6 @@ const RulesPageComponent: React.FC = () => { const userHasNoPermissions = canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false; - const lastCompletedRun = undefined; const handleCreatePrePackagedRules = useCallback(async () => { if (createPrePackagedRules != null) { @@ -88,7 +88,7 @@ const RulesPageComponent: React.FC = () => { isAuthenticated != null && (!isSignalIndexExists || !isAuthenticated) ) { - return ; + return ; } return ( @@ -102,22 +102,9 @@ const RulesPageComponent: React.FC = () => { , - }} - /> - ) : ( - getEmptyTagValue() - ) - } title={i18n.PAGE_TITLE} > @@ -159,7 +146,7 @@ const RulesPageComponent: React.FC = () => { @@ -182,6 +169,7 @@ const RulesPageComponent: React.FC = () => { hasNoPermissions={userHasNoPermissions} importCompleteToggle={importCompleteToggle} refetchPrePackagedRulesStatus={handleRefetchPrePackagedRulesStatus} + rulesCustomInstalled={rulesCustomInstalled} rulesInstalled={rulesInstalled} rulesNotInstalled={rulesNotInstalled} rulesNotUpdated={rulesNotUpdated} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts index 2b50e32a367ec..effaa90d685df 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts @@ -76,7 +76,7 @@ export interface AboutStepRule extends StepRuleData { falsePositives: string[]; tags: string[]; timeline: FieldValueTimeline; - threats: IMitreEnterpriseAttack[]; + threat: IMitreEnterpriseAttack[]; } export interface DefineStepRule extends StepRuleData { @@ -109,7 +109,7 @@ export interface AboutStepRuleJson { tags: string[]; timeline_id?: string; timeline_title?: string; - threats: IMitreEnterpriseAttack[]; + threat: IMitreEnterpriseAttack[]; } export interface ScheduleStepRuleJson { @@ -134,5 +134,5 @@ export interface IMitreAttack { export interface IMitreEnterpriseAttack { framework: string; tactic: IMitreAttack; - techniques: IMitreAttack[]; + technique: IMitreAttack[]; } diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index b5bfdbde306ca..5cfed4121ba77 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -19,7 +19,7 @@ import { MlNetworkConditionalContainer } from '../../components/ml/conditional_l import { StatefulTimeline } from '../../components/timeline'; import { AutoSaveWarningMsg } from '../../components/timeline/auto_save_warning'; import { UseUrlState } from '../../components/url_state'; -import { WithSource } from '../../containers/source'; +import { WithSource, indicesExistOrDataTemporarilyUnavailable } from '../../containers/source'; import { SpyRoute } from '../../utils/route/spy_routes'; import { NotFoundPage } from '../404'; import { DetectionEngineContainer } from '../detection_engine'; @@ -63,28 +63,32 @@ export const HomePage: React.FC = () => (
- {({ browserFields, indexPattern }) => ( + {({ browserFields, indexPattern, indicesExist }) => ( - - - - + {indicesExistOrDataTemporarilyUnavailable(indicesExist) && ( + <> + + + + + + )} diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx index d3c9df8ca0980..9ce7b8b0f71dc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx @@ -103,6 +103,7 @@ const OverviewComponent: React.FC = ({ void; filters?: esFilters.Filter[]; from: number; indexPattern: IIndexPattern; @@ -32,26 +33,40 @@ interface Props { } export const SignalsByCategory = React.memo( - ({ filters = NO_FILTERS, from, query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, to }) => { + ({ + deleteQuery, + filters = NO_FILTERS, + from, + query = DEFAULT_QUERY, + setAbsoluteRangeDatePicker, + setQuery, + to, + }) => { const updateDateRangeCallback = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max }); }, [setAbsoluteRangeDatePicker] ); + const defaultStackByOption = { + text: `${i18n.SIGNALS_BY_CATEGORY}`, + value: 'signal.rule.threat', + }; + + const { signalIndexName } = useSignalIndex(); return ( { ) {} public setup(core: CoreSetup, plugins: SetupPlugins) { + initTelemetry(plugins.usageCollection, this.id); + core.application.register({ id: this.id, title: this.name, diff --git a/x-pack/legacy/plugins/siem/public/store/store.ts b/x-pack/legacy/plugins/siem/public/store/store.ts index b56132967d1e3..d3559e7a7adde 100644 --- a/x-pack/legacy/plugins/siem/public/store/store.ts +++ b/x-pack/legacy/plugins/siem/public/store/store.ts @@ -10,6 +10,7 @@ import { createEpicMiddleware } from 'redux-observable'; import { Observable } from 'rxjs'; import { AppApolloClient } from '../lib/lib'; +import { telemetryMiddleware } from '../lib/telemetry'; import { appSelectors } from './app'; import { timelineSelectors } from './timeline'; import { inputsSelectors } from './inputs'; @@ -42,7 +43,11 @@ export const createStore = ( } ); - store = createReduxStore(reducer, state, composeEnhancers(applyMiddleware(epicMiddleware))); + store = createReduxStore( + reducer, + state, + composeEnhancers(applyMiddleware(epicMiddleware, telemetryMiddleware)) + ); epicMiddleware.run(createRootEpic()); diff --git a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts index 9f57155d4d189..730e6b884a182 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts @@ -399,7 +399,7 @@ export const ecsSchema = gql` references: ToStringArray severity: ToStringArray tags: ToStringArray - threats: ToAny + threat: ToAny type: ToStringArray size: ToStringArray to: ToStringArray diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index bc7486777b990..303262ece5c7f 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -1017,7 +1017,7 @@ export interface RuleField { tags?: Maybe; - threats?: Maybe; + threat?: Maybe; type?: Maybe; @@ -4994,7 +4994,7 @@ export namespace RuleFieldResolvers { tags?: TagsResolver, TypeParent, TContext>; - threats?: ThreatsResolver, TypeParent, TContext>; + threat?: ThreatResolver, TypeParent, TContext>; type?: TypeResolver, TypeParent, TContext>; @@ -5112,7 +5112,7 @@ export namespace RuleFieldResolvers { Parent = RuleField, TContext = SiemContext > = Resolver; - export type ThreatsResolver< + export type ThreatResolver< R = Maybe, Parent = RuleField, TContext = SiemContext diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 4a48301ee4b9c..d950d89eb22a6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -34,11 +34,11 @@ export const mockPrepackagedRule = (): PrepackagedRules => ({ severity: 'high', query: 'user.name: root or user.name: admin', language: 'kuery', - threats: [ + threat: [ { framework: 'fake', tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' }, - techniques: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }], + technique: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }], }, ], enabled: true, @@ -69,11 +69,11 @@ export const typicalPayload = (): Partial => ({ severity: 'high', query: 'user.name: root or user.name: admin', language: 'kuery', - threats: [ + threat: [ { framework: 'fake', tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' }, - techniques: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }], + technique: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }], }, ], }); @@ -298,7 +298,7 @@ export const getResult = (): RuleAlertType => ({ severity: 'high', to: 'now', type: 'query', - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -306,7 +306,7 @@ export const getResult = (): RuleAlertType => ({ name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json index 4f3ba768b17b0..4986c100f1b0b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json @@ -87,8 +87,38 @@ "tags": { "type": "keyword" }, - "threats": { - "type": "object" + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } }, "type": { "type": "keyword" diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 0c98507bc6fa8..68375043070f8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -70,7 +70,7 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou name, severity, tags, - threats, + threat, to, type, updated_at: updatedAt, @@ -128,7 +128,7 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou tags, to, type, - threats, + threat, updatedAt, references, version, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index 4480186d9a7a6..060659d0e1897 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -56,7 +56,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = name, severity, tags, - threats, + threat, to, type, updated_at: updatedAt, @@ -115,7 +115,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = tags, to, type, - threats, + threat, updatedAt, references, version: 1, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts index 67680a8f86eec..de7f0fe26cc74 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts @@ -74,26 +74,28 @@ describe('get_prepackaged_rule_status_route', () => { }); describe('payload', () => { - test('0 rules installed, 1 rules not installed, and 1 rule not updated', async () => { + test('0 rules installed, 0 custom rules, 1 rules not installed, and 1 rule not updated', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); expect(JSON.parse(payload)).toEqual({ + rules_custom_installed: 0, rules_installed: 0, rules_not_installed: 1, rules_not_updated: 0, }); }); - test('1 rule installed, 0 rules not installed, and 1 rule to not updated', async () => { + test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => { alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); expect(JSON.parse(payload)).toEqual({ + rules_custom_installed: 1, rules_installed: 1, rules_not_installed: 0, rules_not_updated: 1, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 0208a209c5eae..ab6ee8e97a70f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -13,6 +13,7 @@ import { transformError } from '../utils'; import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; +import { findRules } from '../../rules/find_rules'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { @@ -36,10 +37,19 @@ export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { try { const rulesFromFileSystem = getPrepackagedRules(); + const customRules = await findRules({ + alertsClient, + perPage: 1, + page: 1, + sortField: 'enabled', + sortOrder: 'desc', + filter: 'alert.attributes.tags:"__internal_immutable:false"', + }); const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules); const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules); return { + rules_custom_installed: customRules.total, rules_installed: prepackagedRules.length, rules_not_installed: rulesToInstall.length, rules_not_updated: rulesToUpdate.length, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 0dfdee2d71375..88a31c36a87fc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -105,7 +105,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = name, severity, tags, - threats, + threat, to, type, references, @@ -154,7 +154,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = tags, to, type, - threats, + threat, updatedAt: new Date().toISOString(), references, version, @@ -189,7 +189,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = tags, to, type, - threats, + threat, references, version, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index cf98043529bc0..8c7558d6d4fb5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -70,7 +70,7 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou tags, to, type, - threats, + threat, references, version, } = payloadRule; @@ -103,7 +103,7 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou tags, to, type, - threats, + threat, references, version, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index cbb66317186a1..f51cea0753f1a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -54,7 +54,7 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = tags, to, type, - threats, + threat, references, version, } = request.payload; @@ -98,7 +98,7 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = tags, to, type, - threats, + threat, references, version, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index c1b4c7de73f68..ec11a8fb2da39 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -52,7 +52,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -60,7 +60,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -115,7 +115,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -123,7 +123,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -180,7 +180,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -188,7 +188,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -245,7 +245,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -253,7 +253,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -308,7 +308,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -316,7 +316,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -374,7 +374,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -382,7 +382,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -440,7 +440,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -448,7 +448,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -506,7 +506,7 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: ['tag 1', 'tag 2'], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -514,7 +514,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -623,7 +623,7 @@ describe('utils', () => { tags: [], to: 'now', type: 'query', - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -631,7 +631,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -696,7 +696,7 @@ describe('utils', () => { tags: [], to: 'now', type: 'query', - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -704,7 +704,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -858,7 +858,7 @@ describe('utils', () => { tags: [], to: 'now', type: 'query', - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -866,7 +866,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -979,7 +979,7 @@ describe('utils', () => { saved_id: 'some-id', severity: 'high', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -987,7 +987,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -1039,7 +1039,7 @@ describe('utils', () => { saved_id: 'some-id', severity: 'high', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -1047,7 +1047,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', @@ -1088,7 +1088,7 @@ describe('utils', () => { saved_id: 'some-id', severity: 'high', tags: [], - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -1096,7 +1096,7 @@ describe('utils', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index ae79b571b2b62..663ddf3a835a6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -110,7 +110,7 @@ export const transformAlertToRule = ( tags: transformTags(alert.tags), to: alert.params.to, type: alert.params.type, - threats: alert.params.threats, + threat: alert.params.threat, version: alert.params.version, status: ruleStatus?.attributes.status, status_date: ruleStatus?.attributes.statusDate, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts index 2a04c15b8cd9f..b536cfac05df3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts @@ -224,7 +224,7 @@ describe('add prepackaged rules schema', () => { ).toBeFalsy(); }); - test('You can send in an empty array to threats', () => { + test('You can send in an empty array to threat', () => { expect( addPrepackagedRulesSchema.validate>({ rule_id: 'rule-1', @@ -241,12 +241,12 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [], + threat: [], version: 1, }).error ).toBeFalsy(); }); - test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version, threats] does validate', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version, s] does validate', () => { expect( addPrepackagedRulesSchema.validate>({ rule_id: 'rule-1', @@ -259,7 +259,7 @@ describe('add prepackaged rules schema', () => { severity: 'low', interval: '5m', type: 'query', - threats: [ + threat: [ { framework: 'someFramework', tactic: { @@ -267,7 +267,7 @@ describe('add prepackaged rules schema', () => { name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -342,28 +342,7 @@ describe('add prepackaged rules schema', () => { ).toEqual(true); }); - test('immutable cannot be false', () => { - expect( - addPrepackagedRulesSchema.validate>({ - rule_id: 'rule-1', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - index: ['index-1'], - immutable: false, - name: 'some-name', - severity: 'low', - interval: '5m', - type: 'query', - query: 'some-query', - language: 'kuery', - version: 1, - }).error.message - ).toEqual('child "immutable" fails because ["immutable" must be one of [true]]'); - }); - - test('immutable can be true', () => { + test('immutable cannot be set in a pre-packaged rule', () => { expect( addPrepackagedRulesSchema.validate>({ rule_id: 'rule-1', @@ -380,8 +359,8 @@ describe('add prepackaged rules schema', () => { query: 'some-query', language: 'kuery', version: 1, - }).error - ).toBeFalsy(); + }).error.message + ).toEqual('child "immutable" fails because ["immutable" is not allowed]'); }); test('defaults enabled to false', () => { @@ -765,11 +744,11 @@ describe('add prepackaged rules schema', () => { ); }); - test('You cannot send in an array of threats that are missing "framework"', () => { + test('You cannot send in an array of threat that are missing "framework"', () => { expect( addPrepackagedRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -786,14 +765,14 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -805,15 +784,15 @@ describe('add prepackaged rules schema', () => { version: 1, }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "tactic"', () => { + test('You cannot send in an array of threat that are missing "tactic"', () => { expect( addPrepackagedRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -830,10 +809,10 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -845,15 +824,15 @@ describe('add prepackaged rules schema', () => { version: 1, }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "techniques"', () => { + test('You cannot send in an array of threat that are missing "technique"', () => { expect( addPrepackagedRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -870,7 +849,7 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', tactic: { @@ -883,7 +862,7 @@ describe('add prepackaged rules schema', () => { version: 1, }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]' ); }); @@ -937,54 +916,6 @@ describe('add prepackaged rules schema', () => { ); }); - test('You can optionally set the immutable to be true', () => { - expect( - addPrepackagedRulesSchema.validate>({ - rule_id: 'rule-1', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - immutable: true, - index: ['index-1'], - name: 'some-name', - severity: 'low', - interval: '5m', - type: 'query', - references: ['index-1'], - query: 'some query', - language: 'kuery', - max_signals: 1, - version: 1, - }).error - ).toBeFalsy(); - }); - - test('You cannot set the immutable to be a number', () => { - expect( - addPrepackagedRulesSchema.validate< - Partial> & { immutable: number } - >({ - rule_id: 'rule-1', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - immutable: 5, - index: ['index-1'], - name: 'some-name', - severity: 'low', - interval: '5m', - type: 'query', - references: ['index-1'], - query: 'some query', - language: 'kuery', - max_signals: 1, - version: 1, - }).error.message - ).toEqual('child "immutable" fails because ["immutable" must be a boolean]'); - }); - test('You cannot set the risk_score to 101', () => { expect( addPrepackagedRulesSchema.validate>({ @@ -993,7 +924,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1016,7 +946,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1039,7 +968,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1062,7 +990,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1085,7 +1012,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1113,7 +1039,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1137,7 +1062,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1183,7 +1107,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1207,7 +1130,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1232,7 +1154,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1257,7 +1178,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1282,7 +1202,6 @@ describe('add prepackaged rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1322,6 +1241,7 @@ describe('add prepackaged rules schema', () => { rule_id: 'rule-1', risk_score: 50, description: 'some description', + index: ['auditbeat-*'], name: 'some-name', severity: 'low', type: 'query', @@ -1340,6 +1260,7 @@ describe('add prepackaged rules schema', () => { rule_id: 'rule-1', risk_score: 50, description: 'some description', + index: ['auditbeat-*'], name: 'some-name', severity: 'junk', type: 'query', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts index d254f83243491..b62c480492c84 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts @@ -30,7 +30,7 @@ import { tags, to, type, - threats, + threat, references, version, } from './schemas'; @@ -42,9 +42,10 @@ import { DEFAULT_MAX_SIGNALS } from '../../../../../common/constants'; * Big differences between this schema and the createRulesSchema * - rule_id is required here * - output_index is not allowed (and instead the space index must be used) - * - immutable defaults to true instead of to false and if it is there can only be true + * - immutable is forbidden but defaults to true instead of to false and it can only ever be true * - enabled defaults to false instead of true * - version is a required field that must exist + * - index is a required field that must exist */ export const addPrepackagedRulesSchema = Joi.object({ description: description.required(), @@ -53,8 +54,11 @@ export const addPrepackagedRulesSchema = Joi.object({ filters, from: from.default('now-6m'), rule_id: rule_id.required(), - immutable: immutable.default(true).valid(true), - index, + immutable: immutable + .forbidden() + .default(true) + .valid(true), + index: index.required(), interval: interval.default('5m'), query: query.allow('').default(''), language: language.default('kuery'), @@ -73,7 +77,7 @@ export const addPrepackagedRulesSchema = Joi.object({ tags: tags.default([]), to: to.default('now'), type: type.required(), - threats: threats.default([]), + threat: threat.default([]), references: references.default([]), version: version.required(), }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index f765f01300c58..d9605a265d28b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -239,7 +239,7 @@ describe('create rules schema', () => { ).toBeFalsy(); }); - test('You can send in an empty array to threats', () => { + test('You can send in an empty array to threat', () => { expect( createRulesSchema.validate>({ rule_id: 'rule-1', @@ -257,12 +257,12 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [], + threat: [], }).error ).toBeFalsy(); }); - test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => { expect( createRulesSchema.validate>({ rule_id: 'rule-1', @@ -276,7 +276,7 @@ describe('create rules schema', () => { severity: 'low', interval: '5m', type: 'query', - threats: [ + threat: [ { framework: 'someFramework', tactic: { @@ -284,7 +284,7 @@ describe('create rules schema', () => { name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -678,11 +678,11 @@ describe('create rules schema', () => { ); }); - test('You cannot send in an array of threats that are missing "framework"', () => { + test('You cannot send in an array of threat that are missing "framework"', () => { expect( createRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -700,14 +700,14 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -718,15 +718,15 @@ describe('create rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "tactic"', () => { + test('You cannot send in an array of threat that are missing "tactic"', () => { expect( createRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -744,10 +744,10 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -758,15 +758,15 @@ describe('create rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "techniques"', () => { + test('You cannot send in an array of threat that are missing "technique"', () => { expect( createRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -784,7 +784,7 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', tactic: { @@ -796,7 +796,7 @@ describe('create rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]' ); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts index 06dbb0cbb48f3..eb79e06c8efa6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts @@ -30,7 +30,7 @@ import { tags, to, type, - threats, + threat, references, version, } from './schemas'; @@ -65,7 +65,7 @@ export const createRulesSchema = Joi.object({ tags: tags.default([]), to: to.default('now'), type: type.required(), - threats: threats.default([]), + threat: threat.default([]), references: references.default([]), version: version.default(1), }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts index b19a91d18c3ff..c72e1b7ef2a63 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts @@ -244,7 +244,7 @@ describe('import rules schema', () => { ).toBeFalsy(); }); - test('You can send in an empty array to threats', () => { + test('You can send in an empty array to threat', () => { expect( importRulesSchema.validate>({ rule_id: 'rule-1', @@ -262,12 +262,12 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [], + threat: [], }).error ).toBeFalsy(); }); - test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => { expect( importRulesSchema.validate>({ rule_id: 'rule-1', @@ -281,7 +281,7 @@ describe('import rules schema', () => { severity: 'low', interval: '5m', type: 'query', - threats: [ + threat: [ { framework: 'someFramework', tactic: { @@ -289,7 +289,7 @@ describe('import rules schema', () => { name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -685,11 +685,11 @@ describe('import rules schema', () => { ); }); - test('You cannot send in an array of threats that are missing "framework"', () => { + test('You cannot send in an array of threat that are missing "framework"', () => { expect( importRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -707,14 +707,14 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -725,15 +725,15 @@ describe('import rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "tactic"', () => { + test('You cannot send in an array of threat that are missing "tactic"', () => { expect( importRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -751,10 +751,10 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -765,15 +765,15 @@ describe('import rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' ); }); - test('You cannot send in an array of threats that are missing "techniques"', () => { + test('You cannot send in an array of threat that are missing "technique"', () => { expect( importRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ rule_id: 'rule-1', @@ -791,7 +791,7 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', tactic: { @@ -803,7 +803,7 @@ describe('import rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]' ); }); @@ -857,7 +857,7 @@ describe('import rules schema', () => { ); }); - test('You can optionally set the immutable to be true', () => { + test('You can optionally set the immutable to be false', () => { expect( importRulesSchema.validate>({ rule_id: 'rule-1', @@ -866,7 +866,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -880,6 +880,29 @@ describe('import rules schema', () => { ).toBeFalsy(); }); + test('You cannnot set immutable to be true', () => { + expect( + importRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: true, + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + }).error.message + ).toEqual('child "immutable" fails because ["immutable" must be one of [false]]'); + }); + test('You cannot set the immutable to be a number', () => { expect( importRulesSchema.validate< @@ -914,7 +937,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -937,7 +960,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -960,7 +983,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -983,7 +1006,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1006,7 +1029,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1032,7 +1055,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', @@ -1056,7 +1079,7 @@ describe('import rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, + immutable: false, index: ['index-1'], name: 'some-name', severity: 'low', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts index 8516585a2c055..1254694645b9c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts @@ -36,7 +36,7 @@ import { tags, to, type, - threats, + threat, references, version, } from './schemas'; @@ -61,7 +61,7 @@ export const importRulesSchema = Joi.object({ filters, from: from.default('now-6m'), rule_id: rule_id.required(), - immutable: immutable.default(false), + immutable: immutable.default(false).valid(false), index, interval: interval.default('5m'), query: query.allow('').default(''), @@ -82,7 +82,7 @@ export const importRulesSchema = Joi.object({ tags: tags.default([]), to: to.default('now'), type: type.required(), - threats: threats.default([]), + threat: threat.default([]), references: references.default([]), version: version.default(1), created_at, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts index a027fcb96b599..9b311b1b58ea7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts @@ -87,11 +87,11 @@ export const threat_technique = Joi.object({ reference: threat_technique_reference.required(), }); export const threat_techniques = Joi.array().items(threat_technique.required()); -export const threats = Joi.array().items( +export const threat = Joi.array().items( Joi.object({ framework: threat_framework.required(), tactic: threat_tactic.required(), - techniques: threat_techniques.required(), + technique: threat_techniques.required(), }) ); export const created_at = Joi.string() diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index 44b3b5b927be2..0dc9f3df3da1c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -670,7 +670,7 @@ describe('update rules schema', () => { ).toEqual('child "filters" fails because ["filters" must be an array]'); }); - test('threats is not defaulted to empty array on update', () => { + test('threat is not defaulted to empty array on update', () => { expect( updateRulesSchema.validate>({ id: 'rule-1', @@ -686,11 +686,11 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).value.threats + }).value.threat ).toBe(undefined); }); - test('threats is not defaulted to undefined on update with empty array', () => { + test('threat is not defaulted to undefined on update with empty array', () => { expect( updateRulesSchema.validate>({ id: 'rule-1', @@ -706,12 +706,12 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [], - }).value.threats + threat: [], + }).value.threat ).toMatchObject([]); }); - test('threats is valid when updated with all sub-objects', () => { + test('threat is valid when updated with all sub-objects', () => { const expected: ThreatParams[] = [ { framework: 'fake', @@ -720,7 +720,7 @@ describe('update rules schema', () => { name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -744,7 +744,7 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', tactic: { @@ -752,7 +752,7 @@ describe('update rules schema', () => { name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -761,15 +761,15 @@ describe('update rules schema', () => { ], }, ], - }).value.threats + }).value.threat ).toMatchObject(expected); }); - test('threats is invalid when updated with missing property framework', () => { + test('threat is invalid when updated with missing property framework', () => { expect( updateRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ id: 'rule-1', @@ -785,14 +785,14 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef', }, - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -803,15 +803,15 @@ describe('update rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]' ); }); - test('threats is invalid when updated with missing tactic sub-object', () => { + test('threat is invalid when updated with missing tactic sub-object', () => { expect( updateRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ id: 'rule-1', @@ -827,10 +827,10 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', - techniques: [ + technique: [ { id: 'techniqueId', name: 'techniqueName', @@ -841,15 +841,15 @@ describe('update rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' ); }); - test('threats is invalid when updated with missing techniques', () => { + test('threat is invalid when updated with missing technique', () => { expect( updateRulesSchema.validate< - Partial> & { - threats: Array>>; + Partial> & { + threat: Array>>; } >({ id: 'rule-1', @@ -865,7 +865,7 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - threats: [ + threat: [ { framework: 'fake', tactic: { @@ -877,7 +877,7 @@ describe('update rules schema', () => { ], }).error.message ).toEqual( - 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]' ); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts index d363bfca98466..3aa8e007a8cbd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts @@ -30,7 +30,7 @@ import { tags, to, type, - threats, + threat, references, id, version, @@ -61,7 +61,7 @@ export const updateRulesSchema = Joi.object({ tags, to, type, - threats, + threat, references, version, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index 1d3801d80de11..d65f5f84c6d64 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -32,7 +32,7 @@ export const createRules = ({ name, severity, tags, - threats, + threat, to, type, references, @@ -63,7 +63,7 @@ export const createRules = ({ maxSignals, riskScore, severity, - threats, + threat, to, type, updatedAt: new Date().toISOString(), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts index 48b7195c3b0bc..d4b7c252e3e38 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts @@ -65,7 +65,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -88,7 +88,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -130,7 +130,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -153,7 +153,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -194,7 +194,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -217,7 +217,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }, @@ -258,7 +258,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }); @@ -282,7 +282,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }); @@ -322,7 +322,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }); @@ -348,7 +348,7 @@ describe('create_rules_stream_from_ndjson', () => { language: 'kuery', max_signals: 100, tags: [], - threats: [], + threat: [], references: [], version: 1, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index eb9756af8fde1..ff48b9f5f7c33 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -23,7 +23,7 @@ describe('getExportAll', () => { const exports = await getExportAll(unsafeCast); expect(exports).toEqual({ rulesNdjson: - '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threats":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"techniques":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', + '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"technique":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', exportDetails: '{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n', }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index a861d80a66fd5..05e455efb3f22 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -25,7 +25,7 @@ describe('get_export_by_object_ids', () => { const exports = await getExportByObjectIds(unsafeCast, objects); expect(exports).toEqual({ rulesNdjson: - '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threats":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"techniques":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', + '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"technique":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', exportDetails: '{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n', }); }); @@ -98,7 +98,7 @@ describe('get_export_by_object_ids', () => { tags: [], to: 'now', type: 'query', - threats: [ + threat: [ { framework: 'MITRE ATT&CK', tactic: { @@ -106,7 +106,7 @@ describe('get_export_by_object_ids', () => { name: 'impact', reference: 'https://attack.mitre.org/tactics/TA0040/', }, - techniques: [ + technique: [ { id: 'T1499', name: 'endpoint denial of service', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index 555063854dc60..7e8ed62baf1cf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -40,7 +40,7 @@ export const installPrepackagedRules = ( tags, to, type, - threats, + threat, references, version, } = rule; @@ -72,7 +72,7 @@ export const installPrepackagedRules = ( tags, to, type, - threats, + threat, references, version, createdAt: new Date().toISOString(), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json index 932f198604557..da0613e1f6fa7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json @@ -1,17 +1,25 @@ { - "description": "403 Response to a POST", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "A POST request to web application returned a 403 response which indicates the web application declined to process the request because the action requested was disallowed.", + "false_positives": [ + "Security scans and tests may result in these errors. Misconfigured or buggy applications may produce large numbers of these errors. If the source is unexpected, or the user is unauthorized, or the request is unusual, these may be suspicious or malicious activity." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "403 Response to a POST", + "max_signals": 33, + "name": "Web Application Suspicious Activity: POST Request Declined", "query": "http.response.status_code:403 and http.request.method:post", + "references": ["https://en.wikipedia.org/wiki/HTTP_403"], "risk_score": 50, "rule_id": "a87a4e42-1d82-44bd-b0bf-d9b7f91fb89e", "severity": "low", - "to": "now", + "tags": ["Elastic", "apm"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json index d4c9a40ddb45f..b0edfb25e9392 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json @@ -1,17 +1,25 @@ { - "description": "405 Response (Method Not Allowed)", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "A request to web application returned a 405 response which indicates the web application declined to process the request because the HTTP method was not allowed for the resource.", + "false_positives": [ + "Security scans and tests may result in these errors. Misconfigured or buggy applications may produce large numbers of these errors. If the source is unexpected, or the user is unauthorized, or the request is unusual, these may be suspicious or malicious activity." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "405 Response (Method Not Allowed)", + "max_signals": 33, + "name": "Web Application Suspicious Activity: Unauthorized Method", "query": "http.response.status_code:405", + "references": ["https://en.wikipedia.org/wiki/HTTP_405"], "risk_score": 50, "rule_id": "75ee75d8-c180-481c-ba88-ee50129a6aef", "severity": "low", - "to": "now", + "tags": ["Elastic", "apm"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/500_response_on_admin_page.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/500_response_on_admin_page.json index 6231200379732..3b4bcbe670921 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/500_response_on_admin_page.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/500_response_on_admin_page.json @@ -1,17 +1,20 @@ { "description": "500 Response on Admin page", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "500 Response on Admin page", "query": "url.path:\"/admin/\" and http.response.status_code:500", "risk_score": 50, "rule_id": "054f669c-b065-492e-acd9-15e44fc42380", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json index e5280d19f8e4a..6843f622bee8f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json @@ -1,20 +1,21 @@ { "description": "Adversaries can add the 'hidden' attribute to files to hide them from the user in an attempt to evade detection", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Adding the Hidden File Attribute with via attrib.exe", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"attrib.exe\" and process.args:\"+h\"", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"attrib.exe\" and process.args:\"+h\"", "risk_score": 25, "rule_id": "4630d948-40d4-4cef-ac69-4002e29bc3db", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1158", "name": "Hidden Files and Directories", @@ -37,7 +38,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1158", "name": "Hidden Files and Directories", @@ -46,7 +47,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json index 0fac9b17160e2..fcc105f2447e8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adobe_hijack_persistence.json @@ -1,20 +1,21 @@ { "description": "Detects writing executable files that will be automatically launched by Adobe on launch.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Adobe Hijack Persistence", "query": "file.path:(\"C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF\\RdrCEF.exe\" or \"C:\\Program Files\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF\\RdrCEF.exe\") and event.action:\"File created (rule: FileCreate)\" and not process.name:msiexeec.exe", "risk_score": 25, "rule_id": "2bf78aa2-9c56-48de-b139-f169bf99cf86", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1044", "name": "File System Permissions Weakness", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_powershell.json index 0506d03348913..feaa8451754a5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_powershell.json @@ -1,20 +1,21 @@ { "description": "An adversary can leverage a computer's peripheral devices or applications to capture audio recordings for the purpose of listening into sensitive conversations to gather information.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Audio Capture via PowerShell", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"powershell.exe\" and process.args:\"WindowsAudioDevice-Powershell-Cmdlet\"", "risk_score": 25, "rule_id": "b27b9f47-0a20-4807-8377-7f899b4fbada", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Collection", "reference": "https://attack.mitre.org/tactics/TA0009/" }, - "techniques": [ + "technique": [ { "id": "T1123", "name": "Audio Capture", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_soundrecorder.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_soundrecorder.json index 392eeb3980c9f..0365616e86faf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_soundrecorder.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_audio_capture_via_soundrecorder.json @@ -1,20 +1,21 @@ { "description": "An adversary can leverage a computer's peripheral devices or applications to capture audio recordings for the purpose of listening into sensitive conversations to gather information.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Audio Capture via SoundRecorder", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"SoundRecorder.exe\" and process.args:\"/FILE\"", "risk_score": 25, "rule_id": "f8e06892-ed10-4452-892e-2c5a38d552f1", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Collection", "reference": "https://attack.mitre.org/tactics/TA0009/" }, - "techniques": [ + "technique": [ { "id": "T1123", "name": "Audio Capture", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_event_viewer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_event_viewer.json index ecbc9a2dd46c4..e3d57d2b05503 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_event_viewer.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_event_viewer.json @@ -1,20 +1,21 @@ { "description": "Identifies User Account Control (UAC) bypass via eventvwr. Attackers bypass UAC to stealthily execute code with elevated permissions.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Bypass UAC via Event Viewer", "query": "process.parent.name:eventvwr.exe and event.action:\"Process Create (rule: ProcessCreate)\" and not process.executable:(\"C:\\Windows\\System32\\mmc.exe\" or \"C:\\Windows\\SysWOW64\\mmc.exe\")", "risk_score": 25, "rule_id": "59547add-a400-4baa-aa0c-66c72efdb77f", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Privilege Escalation", "reference": "https://attack.mitre.org/tactics/TA0004/" }, - "techniques": [ + "technique": [ { "id": "T1088", "name": "Bypass User Account Control", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_cmstp.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_cmstp.json index 2518fda68ee0f..0d9346a7e1f88 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_cmstp.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_cmstp.json @@ -1,20 +1,21 @@ { "description": "Identifies User Account Control (UAC) bypass via cmstp. Attackers bypass UAC to stealthily execute code with elevated permissions.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Bypass UAC via CMSTP", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"cmstp.exe\" and process.parent.args:(\"/s\" and \"/au\")", "risk_score": 25, "rule_id": "2f7403da-1a4c-46bb-8ecc-c1a596e10cd0", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Privilege Escalation", "reference": "https://attack.mitre.org/tactics/TA0004/" }, - "techniques": [ + "technique": [ { "id": "T1088", "name": "Bypass User Account Control", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_sdclt.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_sdclt.json index c419dc080ec3c..3e99f1be6bf2e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_sdclt.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_bypass_uac_via_sdclt.json @@ -1,20 +1,21 @@ { "description": "Identifies User Account Control (UAC) bypass via cmstp. Attackers bypass UAC to stealthily execute code with elevated permissions.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Bypass UAC via SDCLT", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"sdclt.exe\" and process.args:\"/kickoffelev\" and not process.executable:(\"C:\\Windows\\System32\\sdclt.exe\" or \"C:\\Windows\\System32\\control.exe\" or \"C:\\Windows\\SysWOW64\\sdclt.exe\" or \"C:\\Windows\\SysWOW64\\control.exe\")", "risk_score": 25, "rule_id": "f68d83a1-24cb-4b8d-825b-e8af400b9670", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Privilege Escalation", "reference": "https://attack.mitre.org/tactics/TA0004/" }, - "techniques": [ + "technique": [ { "id": "T1088", "name": "Bypass User Account Control", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json index bcf9b02a0210f..9d8d3bab1ace7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json @@ -1,20 +1,21 @@ { "description": "Identifies attempts to clear Windows event log stores. This is often done by attackers in an attempt evade detection or destroy forensic evidence on a system.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Clearing Windows Event Logs", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and (process.name:\"wevtutil.exe\" and process.args:\"cl\") or (process.name:\"powershell.exe\" and process.args:\"Clear-EventLog\")", "risk_score": 25, "rule_id": "d331bbe2-6db4-4941-80a5-8270db72eb61", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1070", "name": "Indicator Removal on Host", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json index 5a9ba60597534..e69de058960d4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json @@ -1,20 +1,21 @@ { "description": "Identifies use of the fsutil command to delete the volume USNJRNL. This technique is used by attackers to eliminate evidence of files created during post-exploitation activities.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Delete Volume USN Journal with fsutil", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"fsutil.exe\" and process.args:(\"usn\" and \"deletejournal\")", "risk_score": 25, "rule_id": "f675872f-6d85-40a3-b502-c0d2ef101e92", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1107", "name": "File Deletion", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json index 240678d45238c..cbf51ffb7c20b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json @@ -1,20 +1,21 @@ { "description": "Identifies use of the wbadmin command to delete the backup catalog. Ransomware and other malware may do this to prevent system recovery.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Deleting Backup Catalogs with wbadmin", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wbadmin.exe\" and process.args:(\"delete\" and \"catalog\")", "risk_score": 25, "rule_id": "581add16-df76-42bb-af8e-c979bfb39a59", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1107", "name": "File Deletion", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json index 9e5ccc73dc05e..5e8321c6777aa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json @@ -1,20 +1,21 @@ { "description": "Identifies unexpected processes making network connections over port 445. Windows File Sharing is typically implemented over Server Message Block (SMB), which communicates between hosts using port 445. When legitimate, these network connections are established by the kernel. Processes making 445/tcp connections may be port scanners, exploits, or suspicious user-level processes moving laterally.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Direct Outbound SMB Connection", "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and destination.port:445 and not process.pid:4 and not destination.ip:(\"127.0.0.1\" or \"::1\")", "risk_score": 50, "rule_id": "c82c7d8f-fb9e-4874-a4bd-fd9e3f9becf1", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Lateral Movement", "reference": "https://attack.mitre.org/tactics/TA0008/" }, - "techniques": [ + "technique": [ { "id": "T1210", "name": "Exploitation of Remote Services", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json index 40a8298561dbd..c9510913a151f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json @@ -1,20 +1,21 @@ { "description": "Identifies use of the netsh command to disable or weaken the local firewall. Attackers will use this command line tool to disable the firewall during troubleshooting or to enable network mobility.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Disable Windows Firewall Rules with Netsh", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"netsh.exe\" and process.args:(\"firewall\" and \"set\" and \"disable\") or process.args:(\"advfirewall\" and \"state\" and \"off\")", "risk_score": 50, "rule_id": "4b438734-3793-4fda-bd42-ceeada0be8f9", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1089", "name": "Disabling Security Tools", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_dll_search_order_hijack.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_dll_search_order_hijack.json index 0ee8674e3304b..214ddfaf0feec 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_dll_search_order_hijack.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_dll_search_order_hijack.json @@ -1,20 +1,21 @@ { "description": "Detects writing DLL files to known locations associated with Windows files vulnerable to DLL search order hijacking.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "DLL Search Order Hijack", "query": " event.action:\"File created (rule: FileCreate)\" and not winlog.user.identifier:(\"S-1-5-18\" or \"S-1-5-19\" or \"S-1-5-20\") and file.path:(\"C\\Windows\\ehome\\cryptbase.dll\" or \"C\\Windows\\System32\\Sysprep\\cryptbase.dll\" or \"C\\Windows\\System32\\Sysprep\\cryptsp.dll\" or \"C\\Windows\\System32\\Sysprep\\rpcrtremote.dll\" or \"C\\Windows\\System32\\Sysprep\\uxtheme.dll\" or \"C\\Windows\\System32\\Sysprep\\dwmapi.dll\" or \"C\\Windows\\System32\\Sysprep\\shcore.dll\" or \"C\\Windows\\System32\\Sysprep\\oleacc.dll\" or \"C\\Windows\\System32\\ntwdblib.dll\") ", "risk_score": 50, "rule_id": "73fbc44c-c3cd-48a8-a473-f4eb2065c716", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Privilege Escalation", "reference": "https://attack.mitre.org/tactics/TA0004/" }, - "techniques": [ + "technique": [ { "id": "T1088", "name": "Bypass User Account Control", @@ -37,7 +38,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1088", "name": "Bypass User Account Control", @@ -46,7 +47,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json index 3e912e076adec..e531a2d05a97e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json @@ -1,20 +1,21 @@ { "description": "Identifies the use of certutil.exe to encode or decode data. CertUtil is a native Windows component which is part of Certificate Services. CertUtil is often abused by attackers to encode or decode base64 data for stealthier command and control or exfiltration.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Encoding or Decoding Files via CertUtil", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"certutil.exe\" and process.args:(\"-encode\" or \"/encode\" or \"-decode\" or \"/decode\")", "risk_score": 50, "rule_id": "fd70c98a-c410-42dc-a2e3-761c71848acf", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1140", "name": "Deobfuscate/Decode Files or Information", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json index 304fea1cfbb76..426d32b9b1e48 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json @@ -1,20 +1,21 @@ { "description": "A scheduled task can be used by an adversary to establish persistence, move laterally, and/or escalate privileges.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Local Scheduled Task Commands", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:schtasks.exe and process.args:(\"/create\" or \"-create\" or \"/S\" or \"-s\" or \"/run\" or \"-run\" or \"/change\" or \"-change\")", "risk_score": 25, "rule_id": "afcce5ad-65de-4ed2-8516-5e093d3ac99a", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1053", "name": "Scheduled Task", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json index 7454b0fd452c6..71f94ecf91788 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json @@ -1,20 +1,21 @@ { "description": "Identifies use of sc.exe to create, modify, or start services on remote hosts. This could be indicative of adversary lateral movement but will be noisy if commonly done by admins.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Local Service Commands", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:sc.exe and process.args:(\"create\" or \"config\" or \"failure\" or \"start\")", "risk_score": 25, "rule_id": "e8571d5f-bea1-46c2-9f56-998de2d3ed95", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1021", "name": "Remote Services", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_modification_of_boot_configuration.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_modification_of_boot_configuration.json index d4ac29a78c77d..162dfe717df55 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_modification_of_boot_configuration.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_modification_of_boot_configuration.json @@ -1,20 +1,21 @@ { "description": "Identifies use of the bcdedit command to delete boot configuration data. This tactic is sometimes used as by malware or an attacker as a destructive technique.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Modification of Boot Configuration", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"bcdedit.exe\" and process.args:\"set\" and process.args:( (\"bootstatuspolicy\" and \"ignoreallfailures\") or (\"recoveryenabled\" and \"no\") ) ", "risk_score": 75, "rule_id": "b9ab2f7f-f719-4417-9599-e0252fffe2d8", "severity": "high", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1107", "name": "File Deletion", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json index 61049bba92cce..296f6f0862374 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json @@ -1,20 +1,21 @@ { "description": "Identifies MsBuild.exe making outbound network connections. This may indicate adversarial activity as MsBuild is often leveraged by adversaries to execute code and evade detection.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "MsBuild Making Network Connections", "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:msbuild.exe and not destination.ip:(\"127.0.0.1\" or \"::1\")", "risk_score": 50, "rule_id": "0e79980b-4250-4a50-a509-69294c14e84b", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1127", "name": "Trusted Developer Utilities", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json index f2ed8449b9aaf..18c9e286c99ef 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json @@ -1,23 +1,22 @@ { "description": "Identifies Mshta.exe making outbound network connections. This may indicate adversarial activity as Mshta is often leveraged by adversaries to execute malicious scripts and evade detection.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Mshta Making Network Connections", "query": "event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:\"mshta.exe\" and not process.name:\"mshta.exe\"", - "references": [ - "https://www.fireeye.com/blog/threat-research/2017/05/cyber-espionage-apt32.html" - ], + "references": ["https://www.fireeye.com/blog/threat-research/2017/05/cyber-espionage-apt32.html"], "risk_score": 50, "rule_id": "a4ec1382-4557-452b-89ba-e413b22ed4b8", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -25,7 +24,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1170", "name": "Mshta", @@ -34,7 +33,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msxsl_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msxsl_making_network_connections.json index c86b7515173dc..b21b17cd89abf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msxsl_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msxsl_making_network_connections.json @@ -1,20 +1,21 @@ { "description": "Identifies MsXsl.exe making outbound network connections. This may indicate adversarial activity as MsXsl is often leveraged by adversaries to execute malicious scripts and evade detection.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "MsXsl Making Network Connections", "query": "process.name:msxsl.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "d7351b03-135d-43ba-8b36-cc9b07854525", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1220", "name": "XSL Script Processing", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json index e35843bc9b413..3e04dd4be292b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json @@ -1,20 +1,20 @@ { "description": "Identifies use of the SysInternals tool PsExec to execute commands on a remote host. This is an indication of lateral movement and may detect adversaries.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "PsExec Lateral Movement Command", "query": "process.name:psexec.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" ", "risk_score": 50, "rule_id": "55d551c6-333b-4665-ab7e-5d14a59715ce", "severity": "low", - "tags": [ - "EIA" - ], - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json index 9d3b0361c9d29..ac66af50ecd1d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_office_child_process.json @@ -1,20 +1,21 @@ { "description": "Identifies suspicious child processes of frequently targeted Microsoft Office applications (Word, PowerPoint, Excel). These child processes are often launched during exploitation of Office applications or from documents with malicious macros.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Suspicious MS Office Child Process", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(\"winword.exe\" or \"excel.exe\" or \"powerpnt.exe\" or \"eqnedt32.exe\" or \"fltldr.exe\" or \"mspub.exe\" or \"msaccess.exe\") and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", "risk_score": 25, "rule_id": "a624863f-a70d-417f-a7d2-7a404638d47f", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1193", "name": "Spearphishing Attachment", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json index f445cb187c428..928144f0ecf0c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_ms_outlook_child_process.json @@ -1,20 +1,21 @@ { "description": "Identifies suspicious child processes of Microsoft Outlook. These child processes are often associated with spear phishing activity.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Suspicious MS Outlook Child Process", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"outlook.exe\" and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"outlook.exe\" and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", "risk_score": 25, "rule_id": "32f4675e-6c49-4ace-80f9-97c9259dca2e", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1193", "name": "Spearphishing Attachment", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_pdf_reader_child_process.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_pdf_reader_child_process.json index 0b44ebd922c02..160da5b899042 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_pdf_reader_child_process.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_suspicious_pdf_reader_child_process.json @@ -1,20 +1,21 @@ { "description": "Identifies suspicious child processes of PDF reader applications. These child processes are often launched via exploitation of PDF applications or social engineering.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "EQL - Suspicious PDF Reader Child Process", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(\"acrord32.exe\" or \"rdrcef.exe\" or \"foxitphantomPDF.exe\" or \"foxitreader.exe\") and process.name:(\"arp.exe\" or \"dsquery.exe\" or \"dsget.exe\" or \"gpresult.exe\" or \"hostname.exe\" or \"ipconfig.exe\" or \"nbtstat.exe\" or \"net.exe\" or \"net1.exe\" or \"netsh.exe\" or \"netstat.exe\" or \"nltest.exe\" or \"ping.exe\" or \"qprocess.exe\" or \"quser.exe\" or \"qwinsta.exe\" or \"reg.exe\" or \"sc.exe\" or \"systeminfo.exe\" or \"tasklist.exe\" or \"tracert.exe\" or \"whoami.exe\" or \"bginfo.exe\" or \"cdb.exe\" or \"cmstp.exe\" or \"csi.exe\" or \"dnx.exe\" or \"fsi.exe\" or \"ieexec.exe\" or \"iexpress.exe\" or \"installutil.exe\" or \"Microsoft.Workflow.Compiler.exe\" or \"msbuild.exe\" or \"mshta.exe\" or \"msxsl.exe\" or \"odbcconf.exe\" or \"rcsi.exe\" or \"regsvr32.exe\" or \"xwizard.exe\" or \"atbroker.exe\" or \"forfiles.exe\" or \"schtasks.exe\" or \"regasm.exe\" or \"regsvcs.exe\" or \"cmd.exe\" or \"cscript.exe\" or \"powershell.exe\" or \"pwsh.exe\" or \"wmic.exe\" or \"wscript.exe\" or \"bitsadmin.exe\" or \"certutil.exe\" or \"ftp.exe\") ", "risk_score": 75, "rule_id": "afcac7b1-d092-43ff-a136-aa7accbda38f", "severity": "high", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1193", "name": "Spearphishing Attachment", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json index 687f5c0db2dab..268e8110c508d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json @@ -1,20 +1,21 @@ { "description": "Windows services typically run as SYSTEM and can be used as a privilege escalation opportunity. Malware or penetration testers may run a shell as a service to gain SYSTEM permissions.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "System Shells via Services", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:\"services.exe\" and process.name:(\"cmd.exe\" or \"powershell.exe\")", "risk_score": 50, "rule_id": "0022d47d-39c7-4f69-a232-4fe9dc7a3acd", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1050", "name": "New Service", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json index 4893f80e8b56c..7332cc7710347 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json @@ -1,20 +1,21 @@ { "description": "Identifies unusual instances of Rundll32.exe making outbound network connections. This may indicate adversarial activity and may identify malicious DLLs.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Unusual Network Connection via RunDLL32", "query": "process.name:rundll32.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 25, "rule_id": "52aaab7b-b51c-441a-89ce-4387b3aea886", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1085", "name": "Rundll32", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json index 29e3c998ebe02..d13d23a9354f7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json @@ -1,20 +1,21 @@ { "description": "Identifies Windows programs run from unexpected parent processes. This could indicate masquerading or other strange activity on a system.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Unusual Parent-Child Relationship ", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.executable:* and ( (process.name:\"smss.exe\" and not process.parent.name:(\"System\" or \"smss.exe\")) or (process.name:\"csrss.exe\" and not process.parent.name:(\"smss.exe\" or \"svchost.exe\")) or (process.name:\"wininit.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"winlogon.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"lsass.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"LogonUI.exe\" and not process.parent.name:(\"winlogon.exe\" or \"wininit.exe\")) or (process.name:\"services.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"svchost.exe\" and not process.parent.name:(\"services.exe\" or \"MsMpEng.exe\")) or (process.name:\"spoolsv.exe\" and not process.parent.name:\"services.exe\") or (process.name:\"taskhost.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"taskhostw.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"userinit.exe\" and not process.parent.name:(\"dwm.exe\" or \"winlogon.exe\")) )", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.executable:* and ( (process.name:\"smss.exe\" and not process.parent.name:(\"System\" or \"smss.exe\")) or (process.name:\"csrss.exe\" and not process.parent.name:(\"smss.exe\" or \"svchost.exe\")) or (process.name:\"wininit.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"winlogon.exe\" and not process.parent.name:\"smss.exe\") or (process.name:\"lsass.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"LogonUI.exe\" and not process.parent.name:(\"winlogon.exe\" or \"wininit.exe\")) or (process.name:\"services.exe\" and not process.parent.name:\"wininit.exe\") or (process.name:\"svchost.exe\" and not process.parent.name:(\"services.exe\" or \"MsMpEng.exe\")) or (process.name:\"spoolsv.exe\" and not process.parent.name:\"services.exe\") or (process.name:\"taskhost.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"taskhostw.exe\" and not process.parent.name:(\"services.exe\" or \"svchost.exe\")) or (process.name:\"userinit.exe\" and not process.parent.name:(\"dwm.exe\" or \"winlogon.exe\")) )", "risk_score": 50, "rule_id": "35df0dd8-092d-4a83-88c1-5151a804f31b", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Privilege Escalation", "reference": "https://attack.mitre.org/tactics/TA0004/" }, - "techniques": [ + "technique": [ { "id": "T1093", "name": "Process Hollowing", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json index ce34e4a352c88..138ecbb820513 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json @@ -1,20 +1,21 @@ { "description": "Identifies network activity from unexpected system applications. This may indicate adversarial activity as these applications are often leveraged by adversaries to execute code and evade detection.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Unusual Process Network Connection", "query": " event.action:\"Network connection detected (rule: NetworkConnect)\" and process.name:(bginfo.exe or cdb.exe or cmstp.exe or csi.exe or dnx.exe or fsi.exe or ieexec.exe or iexpress.exe or Microsoft.Workflow.Compiler.exe or odbcconf.exe or rcsi.exe or xwizard.exe)", "risk_score": 25, "rule_id": "610949a1-312f-4e04-bb55-3a79b8c95267", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1127", "name": "Trusted Developer Utilities", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json index 5b94babaf8add..9f3ecdb7a7f57 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json @@ -1,20 +1,21 @@ { "description": "Identifies attempts to create new local users. This is sometimes done by attackers to increase access to a system or domain.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "User Account Creation", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(\"net.exe\" or \"net1.exe\") and not process.parent.name:\"net.exe\" and process.args:(\"user\" and (\"/add\" or \"/ad\")) ", "risk_score": 50, "rule_id": "1aa9181a-492b-4c01-8b16-fa0735786b2b", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1136", "name": "Create Account", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_added_to_administrator_group.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_added_to_administrator_group.json index f0b770985c716..1a0e0f8dcb2ad 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_added_to_administrator_group.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_added_to_administrator_group.json @@ -1,20 +1,21 @@ { "description": "Identifies attempts to add a user to an administrative group with the \"net.exe\" command. This is sometimes done by attackers to increase access of a compromised account or create new account.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "User Added to Administrator Group", "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(\"net.exe\" or \"net1.exe\") and not process.parent.name:\"net.exe\" and process.args:(\"group\" and \"admin\" and \"/add\") ", "risk_score": 50, "rule_id": "4426de6f-6103-44aa-a77e-49d672836c27", "severity": "medium", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/tactics/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1098", "name": "Account Manipulation", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json index 8f23d398a48a7..794fec38b380e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json @@ -1,20 +1,21 @@ { "description": "Identifies use of vssadmin.exe for shadow copy deletion on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Volume Shadow Copy Deletion via VssAdmin", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"vssadmin.exe\" and process.args:(\"delete\" and \"shadows\") ", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"vssadmin.exe\" and process.args:(\"delete\" and \"shadows\") ", "risk_score": 75, "rule_id": "b5ea4bfe-a1b2-421f-9d47-22a75a6f2921", "severity": "high", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1490", "name": "Inhibit System Recovery", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json index fc18b2c0f5d70..a3e94b08275be 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json @@ -1,20 +1,21 @@ { "description": "Identifies use of wmic for shadow copy deletion on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Volume Shadow Copy Deletion via WMIC", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wmic.exe\" and process.args:(\"shadowcopy\" and \"delete\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wmic.exe\" and process.args:(\"shadowcopy\" and \"delete\")", "risk_score": 75, "rule_id": "dc9c1f74-dac3-48e3-b47f-eb79db358f57", "severity": "high", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "techniques": [ + "technique": [ { "id": "T1107", "name": "File Deletion", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json index ff3d660704eeb..868d84ef9ebce 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json @@ -1,20 +1,21 @@ { "description": "Identifies a PowerShell process launched by either CScript or WScript. Observing Windows scripting processes executing a PowerShell script, may be indicative of malicious activity.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Script Executing PowerShell", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.parent.name:(\"wscript.exe\" or \"cscript.exe\") and process.name:\"powershell.exe\"", "risk_score": 50, "rule_id": "f545ff26-3c94-4fd0-bd33-3c7f95a3a0fc", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/" }, - "techniques": [ + "technique": [ { "id": "T1193", "name": "Spearphishing Attachment", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_wmic_command_lateral_movement.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_wmic_command_lateral_movement.json index 5a9bda9e8ddfa..5c2804507cbd2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_wmic_command_lateral_movement.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_wmic_command_lateral_movement.json @@ -1,20 +1,21 @@ { "description": "Identifies use of wmic.exe to run commands on remote hosts. This could be indicative of adversary lateral movement but will be noisy if commonly done by admins.", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "WMIC Command Lateral Movement", - "query": " event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wmic.exe\" and process.args:(\"/node\" or \"-node\") and process.args:(\"call\" or \"set\")", + "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:\"wmic.exe\" and process.args:(\"/node\" or \"-node\") and process.args:(\"call\" or \"set\")", "risk_score": 25, "rule_id": "9616587f-6396-42d0-bd31-ef8dbd806210", "severity": "low", - "tags": [ - "EIA" - ], - "threats": [ + "tags": ["Elastic"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -22,7 +23,7 @@ "name": "Lateral Movement", "reference": "https://attack.mitre.org/tactics/TA0008/" }, - "techniques": [ + "technique": [ { "id": "T1047", "name": "Windows Management Instrumentation", @@ -31,7 +32,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json index b42e4130b688c..d0a07ce2d0365 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Hping Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "Hping ran on a Linux host. Hping is FOSS command-line packet analyzer and has the ability to construct network packets for a wide variety of network security testing applications including scanning and firewall auditing.", + "false_positives": [ + "Normal use of hping is uncommon apart from security testing and research. Use by non-security engineers is very uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Hping Activity", + "max_signals": 33, + "name": "Hping Process Activity", "query": "process.name: hping and event.action:executed", - "risk_score": 50, + "references": ["https://en.wikipedia.org/wiki/Hping"], + "risk_score": 75, "rule_id": "90169566-2260-4824-b8e4-8615c3b4ed52", - "severity": "low", - "to": "now", + "severity": "high", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json index 1eb66c39571d7..1a116735e98f3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Iodine Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "Iodine is a tool for tunneling internet protocol version 4 (IPV4) trafic over the DNS protocol in order to circumvent firewalls, network security groups or network access lists while evading detection.", + "false_positives": [ + "Normal use of Iodine is uncommon apart from security testing and research. Use by non-security engineers is very uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Iodine Activity", + "max_signals": 33, + "name": "Potential DNS Tunneling via Iodine", "query": "process.name: (iodine or iodined) and event.action:executed", - "risk_score": 50, + "references": ["https://code.kryo.se/iodine/"], + "risk_score": 75, "rule_id": "041d4d41-9589-43e2-ba13-5680af75ebc2", - "severity": "low", - "to": "now", + "severity": "high", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json index d6887f7928dd8..1529862571381 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json @@ -1,16 +1,9 @@ { "description": "Identifies loadable kernel module errors, often indicative of potential persistence attempts.", - "enabled": false, "false_positives": [ - "Security tools and device drivers may load legitimate kernel modules." + "Security tools and device drivers may run these programs in order to load legitimate kernel modules. Use of these programs by ordinary users is uncommon." ], - "from": "now-6m", - "immutable": true, - "index": [ - "auditbeat-*" - ], - "interval": "5m", - "language": "kuery", + "index": ["auditbeat-*"], "max_signals": 33, "name": "Persistence via Kernel Module Modification", "query": "process.name: (insmod or kmod or modprobe or rmod) and event.action:executed", @@ -20,11 +13,8 @@ "risk_score": 25, "rule_id": "81cc58f5-8062-49a2-ba84-5cc4b4d31c40", "severity": "low", - "tags": [ - "EIA", - "auditbeat" - ], - "threats": [ + "tags": ["Elastic", "auditbeat"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -32,7 +22,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/techniques/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1215", "name": "Kernel Modules and Extensions", @@ -41,7 +31,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json index 174e246fa70d9..187fc6379ef25 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ldso_process_activity.json @@ -1,17 +1,24 @@ { - "description": "Linux ld.so process activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "ld.so runs in a privlieged context and can be used to escape restrictive environments by spawning a shell in order to elevate privlieges or move laterally.", + "false_positives": [ + "ld.so is a dual-use tool that can be used for benign or malicious activity. Some normal use of this command may originate from developers or administrators. Use of ld.so by non-engineers or ordinary users is uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux ld.so process activity", + "max_signals": 33, + "name": "Ld.so Process Activity", "query": "process.name:ld.so and event.action:executed", - "risk_score": 50, + "risk_score": 25, "rule_id": "3f31a31c-f7cf-4268-a0df-ec1a98099e7f", "severity": "low", - "to": "now", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity.json index 77953240c2185..8061ff72e130b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity.json @@ -1,17 +1,20 @@ { "description": "Linux lzop activity - possible @JulianRunnels", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Linux lzop activity", "query": "process.name:lzop and event.action:executed", "risk_score": 50, "rule_id": "d7359214-54a4-4572-9e51-ebf79cda9b04", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json index 0894011520741..1fe4802c6cf79 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Mknod Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "The Linux mknod program is sometimes used in the command paylod of remote command injection (RCI) and other exploits to export a command shell when the traditional version of netcat is not available to the payload.", + "false_positives": [ + "Mknod is a Linux system program. Some normal use of this program, at varying levels of frequency, may originate from scripts, automation tools and frameworks. Usage by web servers is more likely to be suspicious." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Mknod Activity", + "max_signals": 33, + "name": "Mknod Process Activity", "query": "process.name: mknod and event.action:executed", - "risk_score": 50, + "references": ["https://pen-testing.sans.org/blog/2013/05/06/netcat-without-e-no-problem"], + "risk_score": 25, "rule_id": "61c31c14-507f-4627-8c31-072556b89a9c", "severity": "low", - "to": "now", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json index d324a4f64cbba..6d57d0cbab375 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json @@ -1,16 +1,29 @@ { - "description": "Linux: Netcat Network Connection", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "A netcat process is engaging in network activity on a Linux host. Netcat is often used as a persistence mechanism by exporting a reverse shell or by serving a shell on a listening port. Netcat is also sometimes used for data exfiltation. ", + "false_positives": [ + "Netcat is a dual-use tool that can be used for benign or malicious activity. Netcat is included in some Linux distributions so its presence is not necessarily suspicious. Some normal use of this program, while uncommon, may originate from scripts, automation tools and frameworks." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Netcat Network Activity", + "max_signals": 33, + "name": "Netcat Network Activity", "query": "process.name: (nc or ncat or netcat or netcat.openbsd or netcat.traditional) and event.action: (connected-to or bound-socket or socket_opened)", + "references": [ + "http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet", + "https://www.sans.org/security-resources/sec560/netcat_cheat_sheet_v1.pdf", + "https://en.wikipedia.org/wiki/Netcat" + ], "risk_score": 50, "rule_id": "adb961e0-cb74-42a0-af9e-29fc41f88f5f", - "severity": "low", - "to": "now", + "severity": "medium", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json index d04f6610f450d..f10c940f8bb93 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_network_anomalous_process_using_https_ports.json @@ -1,17 +1,20 @@ { "description": "Linux Network - Anomalous Process Using HTTP/S Ports", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Linux Network - Anomalous Process Using HTTP/S Ports", "query": "(destination.port:443 or destination.port:80) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not process.name:curl and not process.name:http and not process.name:https and not process.name:nginx and not process.name:packetbeat and not process.name:python2 and not process.name:snapd and not process.name:wget", "risk_score": 50, "rule_id": "be40c674-1799-4a00-934d-0b2d54495913", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json index cb89fdc6ebbff..b2284eea3f309 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Nmap Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "Nmap ran on a Linux host. Nmap is a FOSS tool for network scanning and security testing. It can map and discover networks and identify listneing services and operating systems. It is sometimes used to gather information in support of exploitation, execution or lateral movement.", + "false_positives": [ + "Security testing tools and frameworks may run nmap in the course of security auditing. Some normal use of this command may originate from security engineers and network or server administrators. Use of nmap by ordinary users is uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Nmap Activity", + "max_signals": 33, + "name": "Nmap Process Activity", "query": "process.name: nmap", - "risk_score": 50, + "references": ["https://en.wikipedia.org/wiki/Nmap"], + "risk_score": 25, "rule_id": "c87fca17-b3a9-4e83-b545-f30746c53920", "severity": "low", - "to": "now", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json index b5508c388059c..4d37f32fb3ca0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Nping Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "Nping ran on a Linux host. Nping is part of the Nmap tool suite and has the ability to construct raw packets for a wide variety of security testing applications including denial of service testing.", + "false_positives": [ + "Some normal use of this command may originate from security engineers and network or server administrators but this is usually not routine or unannounced. Use of nping by non-engineers or ordinary users is uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Nping Activity", + "max_signals": 33, + "name": "Nping Process Activity", "query": "process.name: nping and event.action:executed", + "references": ["https://en.wikipedia.org/wiki/Nmap"], "risk_score": 50, "rule_id": "0d69150b-96f8-467c-a86d-a67a3378ce77", - "severity": "low", - "to": "now", + "severity": "medium", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json index 945c8acfe00e4..d38cead306cd4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json @@ -1,27 +1,24 @@ { "description": "Identifies processes running in a temporary folder. This is sometimes done by adversaries to hide malware.", - "enabled": false, "false_positives": [ - "Build systems like Jenkins may start processes in the /tmp directory." + "Build systems like Jenkins may start processes in the /tmp directory. These can be exempted by name or by username." ], - "from": "now-6m", - "immutable": true, "index": [ - "auditbeat-*" + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" ], - "interval": "5m", "language": "kuery", "max_signals": 33, "name": "Unusual Process Execution - Temp", "query": "process.working_directory: /tmp and event.action:executed", - "risk_score": 25, + "risk_score": 50, "rule_id": "df959768-b0c9-4d45-988c-5606a2be8e5a", - "severity": "low", - "tags": [ - "EIA", - "auditbeat" - ], - "to": "now", + "severity": "medium", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json index 47ae28cf8ea4c..6f99312c04a00 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ptrace_activity.json @@ -1,16 +1,20 @@ { "description": "Linux: Ptrace Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Linux: Ptrace Activity", "query": "process.name: ptrace and event.action:executed", "risk_score": 50, "rule_id": "1bff9259-e160-4920-bf72-4c96b6dbb7af", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json index d4924cab7048f..148468e959899 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_rawshark_activity.json @@ -1,16 +1,20 @@ { "description": "Linux: Rawshark Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Linux: Rawshark Activity", "query": "process.name: rawshark and event.action:executed", "risk_score": 50, "rule_id": "30eb2b9d-b53b-4ba5-bfab-7119a8b84029", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json index e8c5942ec5100..1711f45e770ed 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json @@ -1,31 +1,19 @@ { "description": "Identifies suspicious commands executed via a web server, which may suggest a vulnerability and remote shell access.", - "enabled": false, "false_positives": [ "Network monitoring or management products may have a web server component that runs shell commands as part of normal behavior." ], - "filters": [], - "from": "now-6m", - "immutable": true, - "index": [ - "auditbeat-*" - ], - "interval": "5m", + "index": ["auditbeat-*"], "language": "kuery", "max_signals": 33, - "name": "Linux: Shell Activity By Web Server", + "name": "Potential Shell via Web Server", "query": "process.name: bash and (user.name: apache or www) and event.action:executed", - "references": [ - "https://pentestlab.blog/tag/web-shell/" - ], + "references": ["https://pentestlab.blog/tag/web-shell/"], "risk_score": 50, "rule_id": "231876e7-4d1f-4d63-a47c-47dd1acdc1cb", "severity": "low", - "tags": [ - "EIA", - "auditbeat" - ], - "threats": [ + "tags": ["Elastic", "linux"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -33,7 +21,7 @@ "name": "Persistence", "reference": "https://attack.mitre.org/techniques/TA0003/" }, - "techniques": [ + "technique": [ { "id": "T1100", "name": "Web Shell", @@ -42,7 +30,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json index 2ea860e061958..364a2bee65c23 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json @@ -1,16 +1,27 @@ { - "description": "Linux: socat activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "A Socat process is running on a Linux host. Socat is often used as a persistence mechanism by exporting a reverse shell or by serving a shell on a listening port. Socat is also sometimes used for lateral movement. ", + "false_positives": [ + "Socat is a dual-use tool that can be used for benign or malicious activity. Some normal use of this program, at varying levels of frequency, may originate from scripts, automation tools and frameworks. Usage by web servers is more likely to be suspicious." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Whoami Commmand", + "max_signals": 33, + "name": "Socat Process Activity", "query": "process.name:socat and not process.args:\"-V\" and event.action:executed", + "references": [ + "https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/#method-2-using-socat" + ], "risk_score": 50, "rule_id": "cd4d5754-07e1-41d4-b9a5-ef4ea6a0a126", - "severity": "low", - "to": "now", + "severity": "medium", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ssh_forwarding.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ssh_forwarding.json index 38562320921b4..3447689f08d62 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ssh_forwarding.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_ssh_forwarding.json @@ -1,19 +1,42 @@ { - "description": "Detect ssh processes with the `-R` flag which can be used to forward a port on a local system to the local system so that someone on the remote system can connect to the local system. This is often used by attackers to create encrypted tunnels through firewalls for pivoting and persistence.", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "An SSH processes ran with the `-R` flag which can be used to forward a port to a remote destination for purposes of pivoting and persistence. This technique often used to create encrypted tunnels and circumvent firewalls, security groups or network access lists.", + "false_positives": [ + "Some normal use of this command may originate from usage by engineers as an alternative or ad-hoc remote access solution. Use of this command by non-administrative users is uncommon." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: SSH Port Forwarding", + "max_signals": 33, + "name": "Potential Lateral Movement via SSH Port Forwarding", "query": "process.name:ssh and process.args:\"-R\" and event.action:executed", - "references": [ - " - https://www.ssh.com/ssh/tunneling,https://www.ssh.com/ssh/tunneling/example,https://attack.mitre.org/techniques/T1184/" - ], + "references": ["https://www.ssh.com/ssh/tunneling", "https://www.ssh.com/ssh/tunneling/example"], "risk_score": 50, "rule_id": "45d256ab-e665-445b-8306-2f83a8db59f8", - "severity": "low", - "to": "now", + "severity": "medium", + "tags": ["Elastic", "linux"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0008", + "name": "Lateral Movement", + "reference": "https://attack.mitre.org/tactics/TA0008/" + }, + "technique": [ + { + "id": "T1184", + "name": "SSH Hijacking", + "reference": "https://attack.mitre.org/techniques/T1184/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json index dc0eae38d20c6..b0c2b4ecd07c2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json @@ -1,16 +1,25 @@ { - "description": "Linux: Strace Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "Strace runs in a privlieged context and can be used to escape restrictive environments by instantiating a shell in order to elevate privlieges or move laterally.", + "false_positives": [ + "Strace is a dual-use tool that can be used for benign or malicious activity. Some normal use of this command may originate from developers or SREs engaged in debugging or system call tracing." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Strace Activity", + "max_signals": 33, + "name": "Strace Process Activity", "query": "process.name: strace and event.action:executed", - "risk_score": 50, + "references": ["https://en.wikipedia.org/wiki/Strace"], + "risk_score": 25, "rule_id": "d6450d4e-81c6-46a3-bd94-079886318ed5", "severity": "low", - "to": "now", + "tags": ["Elastic", "linux"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json index f7b543fef75f5..594aee0eca708 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json @@ -1,16 +1,56 @@ { - "description": "Linux: Tcpdump Activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "The Tcpdump program ran on a Linux host. Tcpdump is a network monitoring or packet 'sniffing' tool that can be used to capture insecure credentials or data in motion. Sniffing can also be used to discover details of network services as a prelude to lateral movement or defense evasion.", + "false_positives": [ + "Some normal use of this command may originate from server or network administrators engaged in network troubleshooting." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Linux: Tcpdump Activity", + "max_signals": 33, + "name": "Network Sniffing via Tcpdump", "query": "process.name: tcpdump and event.action:executed", - "risk_score": 50, + "risk_score": 25, "rule_id": "7a137d76-ce3d-48e2-947d-2747796a78c0", "severity": "low", - "to": "now", + "tags": ["Elastic", "linux"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0006", + "name": "Credential Access", + "reference": "https://attack.mitre.org/tactics/TA0006/" + }, + "technique": [ + { + "id": "T1040", + "name": "Network Sniffing", + "reference": "https://attack.mitre.org/techniques/T1040/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0007", + "name": "Discovery", + "reference": "https://attack.mitre.org/tactics/TA0007/" + }, + "technique": [ + { + "id": "T1040", + "name": "Network Sniffing", + "reference": "https://attack.mitre.org/techniques/T1040/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json index 876a3fef7aa09..311e2b5779602 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_web_download.json @@ -1,16 +1,20 @@ { "description": "Linux: Web Download", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Linux: Web Download", "query": "process.name: (curl or wget) and event.action:executed", "risk_score": 50, "rule_id": "e8ec93a6-49d2-4467-8c12-81c435fcc519", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json index c57e21334b4f7..a370a44d4eb46 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json @@ -1,27 +1,15 @@ { "description": "The 'whoami' command was executed on a Linux host. This is often used by tools and persistence mechanisms to test for privlieged access.", - "enabled": false, - "false_positives": [ - "Security testing tools and frameworks may run this command. Some normal use of this command may originate from automation tools and frameworks." - ], - "from": "now-6m", - "immutable": true, - "index": [ - "auditbeat-*" - ], - "interval": "5m", + "index": ["auditbeat-*"], "language": "kuery", "max_signals": 33, - "name": "Linux: User Discovery Via The Whoami Commmand", + "name": "User Discovery via Whoami", "query": "process.name: whoami and event.action:executed", - "risk_score": 50, + "risk_score": 25, "rule_id": "120559c6-5e24-49f4-9e30-8ffe697df6b9", "severity": "low", - "tags": [ - "EIA", - "auditbeat" - ], - "threats": [ + "tags": ["Elastic", "linux"], + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -29,7 +17,7 @@ "name": "Discovery", "reference": "https://attack.mitre.org/tactics/TA0007/" }, - "techniques": [ + "technique": [ { "id": "T1033", "name": "System Owner/User Discovery", @@ -38,7 +26,6 @@ ] } ], - "to": "now", "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json index 1a3c3c003b532..5c1d64e294159 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_dns_directly_to_the_internet.json @@ -1,17 +1,43 @@ { - "description": "Network - DNS Directly to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Network - DNS Directly to the Internet\t", - "query": "destination.port:53 and not destination.ip: 169.254.169.254/32 and not destination.ip:127.0.0.53/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "description": "This signal detects DNS network traffic logs that indicate an internal network\nclient reaching out to infrastructure on the Internet directly to answer name\nqueries. This activity could be a default or misconfiguration. This impacts\nyour organization's ability to provide enterprise monitoring and logging of DNS\nand opens your network to a variety of abuses or malicious communications.\n", + "false_positives": [ + "You should apply a filter to this rule to exclude your enterprise nameservers that are expected to reach out to the Internet" + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], + "name": "DNS Activity to the Internet", + "query": "destination.port:53 and (\n network.direction: outbound or (\n source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip:( 169.254.169.254/32 or 127.0.0.53/32 or 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.251 or ff02\\:\\:fb or 255.255.255.255 )\n )\n)\n", + "references": [ + "https://www.us-cert.gov/ncas/alerts/TA15-240A", + "https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-81-2.pdf" + ], "risk_score": 50, "rule_id": "6ea71ff0-9e95-475b-9506-2580d1ce6154", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json index 99a126f0613ec..62064db7e1443 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ftp_file_transfer_protocol_activity_to_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - FTP (File Transfer Protocol) Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may indicate the use of FTP network connections.\nThe File Transfer Protocol (FTP) has been around in its current form since the\n1980's. It can be an efficient and normal procedure on your network to send and\nreceive files. Because it is common and efficient, adversaries will also often\nuse this protocol to exfiltrate data from your network or download new tools.\nAdditionally, FTP is a plaintext protocol which may expose your username and\npassword, if intercepted.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - FTP (File Transfer Protocol) Activity to the Internet\t", - "query": "(destination.port:20 or destination.port:21) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "FTP (File Transfer Protocol) Activity to the Internet", + "query": "network.transport: tcp and destination.port: (20 or 21) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "87ec6396-9ac4-4706-bcf0-2ebb22002f43", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json index 79814eb552d5b..4590fdf39d143 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_irc_internet_relay_chat_protocol_activity_to_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - IRC (Internet Relay Chat) Protocol Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that use common ports for IRC to the Internet. IRC\nis a common protocol that can be used chat and file transfer. This protocol\nalso makes a good candidate for remote control of malware and data transfer in\nand out of a network.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - IRC (Internet Relay Chat) Protocol Activity to the Internet\t", - "query": "(destination.port:20 or destination.port:21) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "IRC (Internet Relay Chat) Protocol Activity to the Internet", + "query": "network.transport: tcp and destination.port:(6667 or 6697) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "c6474c34-4953-447a-903e-9fcb7b6661aa", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json index d370773e3879f..e74bed3463993 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_nat_traversal_port_activity.json @@ -1,17 +1,37 @@ { - "description": "Network - NAT Traversal Port Activity\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that could be describing IPSEC NAT Traversal traffic.\nIPSEC is a VPN technology that allows one system to talk to another using\nencrypted tunnels. NAT Traversal enables these tunnels to communicate over\nthe Internet where one of the sides is behind a NAT router gateway. This may\nbe common on your network, but this technique is also used by threat actors\nto avoid detection.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - NAT Traversal Port Activity\t", - "query": "destination.port:4500", - "risk_score": 50, + "name": "IPSEC NAT Traversal Port Activity", + "query": "network.transport: udp and destination.port: 4500", + "risk_score": 25, "rule_id": "a9cb3641-ff4b-4cdc-a063-b4b8d02a67c7", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json index cfdb5e6584ee3..e05e83ff0a1ee 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_26_activity.json @@ -1,17 +1,56 @@ { - "description": "Network - Port 26 Activity\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may indicate use of SMTP on TCP port 26. This\nport is commonly used by several popular mail transfer agents to deconflict\nwith the default SMTP port 25. This port has also been used by a malware family\ncalled BadPatch for command and control of Windows systems.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - Port 26 Activity\t", - "query": "destination.port:26", - "risk_score": 50, + "name": "SMTP on Port 26/TCP", + "query": "network.transport: tcp and destination.port: 26", + "references": [ + "https://unit42.paloaltonetworks.com/unit42-badpatch/", + "https://isc.sans.edu/forums/diary/Next+up+whats+up+with+TCP+port+26/25564/" + ], + "risk_score": 25, "rule_id": "d7e62693-aab9-4f66-a21a-3d79ecdd603d", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json index 218109b73221d..73a634a3a9f42 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity.json @@ -1,17 +1,20 @@ { "description": "Network - Port 8000 Activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Network - Port 8000 Activity", "query": "destination.port:8000", "risk_score": 50, "rule_id": "9c5f8092-e3f7-4eda-b9d3-56eed28fb157", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json index 5eeda8e094bb9..e193ab83d89fd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_port_8000_activity_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - Port 8000 Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "TCP Port 8000 is commonly used for development environments of web server\nsoftware. It generally should not be exposed directly to the Internet. If you are\nrunning software like this on the Internet, you should consider placing it behind\na reverse proxy.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - Port 8000 Activity to the Internet\t", - "query": "destination.port:8000 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "TCP Port 8000 Activity to the Internet", + "query": "network.transport: tcp and destination.port: 8000 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "08d5d7e2-740f-44d8-aeda-e41f4263efaf", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json index 7b83966e18e70..7b527dbc09a44 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_pptp_point_to_point_tunneling_protocol_activity.json @@ -1,17 +1,20 @@ { - "description": "Network - PPTP (Point to Point Tunneling Protocol) Activity\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may indicate use of a PPTP VPN connection. Some threat actors use these types of connections to tunnel their traffic while avoiding detection.", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - PPTP (Point to Point Tunneling Protocol) Activity\t", - "query": "destination.port:1723", - "risk_score": 50, + "name": "PPTP (Point to Point Tunneling Protocol) Activity", + "query": "network.transport: tcp and destination.port: 1723", + "risk_score": 25, "rule_id": "d2053495-8fe7-4168-b3df-dad844046be3", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json index 3a55db4050459..50f521ea91e2b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_proxy_port_activity_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - Proxy Port Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may describe network events of proxy use to the\nInternet. It includes popular HTTP proxy ports and SOCKS proxy ports. Typically\nenvironments will use an internal IP address for a proxy server. It can also\nbe used to circumvent network controls and detection mechanisms.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - Proxy Port Activity to the Internet\t", - "query": "(destination.port:8080 or destination.port:3128) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "Proxy Port Activity to the Internet", + "query": "network.transport: tcp and destination.port: (3128 or 8080 or 1080) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "ad0e5e75-dd89-4875-8d0a-dfdc1828b5f3", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json index e5c1e33470fa4..edd4aa456974d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_from_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - RDP (Remote Desktop Protocol) from the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of RDP traffic\nfrom the Internet. RDP is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - RDP (Remote Desktop Protocol) from the Internet\t", - "query": "(destination.port:8080 or destination.port:3128) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "RDP (Remote Desktop Protocol) from the Internet", + "query": "network.transport: tcp and destination.port: 3389 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n and destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "8c1bdde8-4204-45c0-9e0c-c85ca3902488", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json index 92316f2bb05da..c9f3f95ad1e07 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rdp_remote_desktop_protocol_to_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - RDP (Remote Desktop Protocol) to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of RDP traffic\nto the Internet. RDP is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - RDP (Remote Desktop Protocol) to the Internet\t", - "query": "destination.port:3389 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "RDP (Remote Desktop Protocol) to the Internet", + "query": "network.transport: tcp and destination.port: 3389 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "e56993d2-759c-4120-984c-9ec9bb940fd5", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json index 69d6d18ced8b9..9f5a60f1743d7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_from_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - RPC (Remote Procedure Call) from the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of RPC traffic\nfrom the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - RPC (Remote Procedure Call) from the Internet\t", - "query": "destination.port:3389 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "RPC (Remote Procedure Call) from the Internet", + "query": "network.transport: tcp and destination.port: 135 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "143cb236-0956-4f42-a706-814bcaa0cf5a", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json index 1f9a71bab9244..b860158ef93d3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_rpc_remote_procedure_call_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - RPC (Remote Procedure Call) to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of RPC traffic\nto the Internet. RPC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - RPC (Remote Procedure Call) to the Internet\t", - "query": "destination.port:135 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "RPC (Remote Procedure Call) to the Internet", + "query": "network.transport: tcp and destination.port: 135 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "32923416-763a-4531-bb35-f33b9232ecdb", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json index 627a89609cc21..fa1f1aba66e83 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smb_windows_file_sharing_activity_to_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - SMB (Windows File Sharing) Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of Windows\nfile sharing (also called SMB or CIFS) traffic to the Internet. SMB is commonly\nused within networks to share files, printers, and other system resources amongst\ntrusted systems. It should almost never be directly exposed to the Internet, as\nit is frequently targetted and exploited by threat actors as an initial access\nor backdoor vector or for data exfiltration.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - SMB (Windows File Sharing) Activity to the Internet\t", - "query": "(destination.port:139 or destination.port:445) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "SMB (Windows File Sharing) Activity to the Internet", + "query": "network.transport: tcp and destination.port: (139 or 445) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "c82b2bd8-d701-420c-ba43-f11a155b681a", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json index ff5a61cbe00e6..85c8b3f05166c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_smtp_to_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - SMTP to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may describe SMTP traffic from internal\nhosts to a host across the Internet. In an enterprise network, there is typically\na dedicate host that is internal that could perform this function. It is also\nfrequently abused by threat actors for command and control or data exfiltration.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - SMTP to the Internet\t", - "query": "destination.port:25 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "SMTP to the Internet", + "query": "network.transport: tcp and destination.port: (25 or 465 or 587) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "67a9beba-830d-4035-bfe8-40b7e28f8ac4", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0010", + "name": "Exfiltration", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Exfiltration Over Alternative Protocol", + "reference": "https://attack.mitre.org/techniques/T1048/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json index eeeb93e12938f..e0998029081d3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_sql_server_port_activity_to_the_internet.json @@ -1,17 +1,20 @@ { - "description": "Network - SQL Server Port Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects events that may describe database traffic\n(MS SQL, Oracle, MySQL, and Postgresql) across the Internet. Databases\nshould almost never be directly exposed to the Internet, as they are\nfrequently targeted by threat actors to gain initial access to network resources.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - SQL Server Port Activity to the Internet\t", - "query": "destination.port:1433 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", + "name": "SQL Traffic to the Internet", + "query": "network.transport: tcp and destination.port: (1433 or 1521 or 3336 or 5432) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", "risk_score": 50, "rule_id": "139c7458-566a-410c-a5cd-f80238d6a5cd", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json index 11f24626fa0c1..2428909491584 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_from_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - SSH (Secure Shell) from the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of SSH traffic\nfrom the Internet. SSH is commonly used by system administrators to remotely\ncontrol a system using the command line shell. If it is exposed to the Internet,\nit should be done with strong security controls as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - SSH (Secure Shell) from the Internet\t", - "query": "destination.port:22 and not source.ip:10.0.0.0/8 and not source.ip:172.16.0.0/12 and not source.ip:192.168.0.0/16", - "risk_score": 50, + "name": "SSH (Secure Shell) from the Internet", + "query": "network.transport: tcp and destination.port:22 and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "ea0784f0-a4d7-4fea-ae86-4baaf27a6f17", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0001", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0001/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1190/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json index ded8c005c4462..cf77f9363f525 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_ssh_secure_shell_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - SSH (Secure Shell) to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of SSH traffic\nfrom the Internet. SSH is commonly used by system administrators to remotely\ncontrol a system using the command line shell. If it is exposed to the Internet,\nit should be done with strong security controls as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - SSH (Secure Shell) to the Internet\t", - "query": "destination.port:22 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "SSH (Secure Shell) to the Internet", + "query": "network.transport: tcp and destination.port:22 and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "6f1500bc-62d7-4eb9-8601-7485e87da2f4", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json index a48f311163c2d..a9a364b1b14bd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_telnet_port_activity.json @@ -1,17 +1,52 @@ { - "description": "Network - Telnet Port Activity\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of Telnet traffic.\nTelnet is commonly used by system administrators to remotely control older or embeded\nsystems using the command line shell. It should almost never be directly exposed to\nthe Internet, as it is frequently targetted and exploited by threat actors as an\ninitial access or backdoor vector. As a plaintext protocol, it may also expose\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - Telnet Port Activity\t", - "query": "destination.port:23", + "name": "Telnet Port Activity", + "query": "network.transport: tcp and destination.port: 23", "risk_score": 50, "rule_id": "34fde489-94b0-4500-a76f-b8a157cf9269", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json index 713cc7da72e57..811a81c0e6754 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_tor_activity_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - Tor Activity to the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of Tor traffic\nto the Internet. Tor is a network protocol that sends traffic through a\nseries of encrypted tunnels used to conceal a user's location and usage.\nTor may be used by threat actors as an alternate communication pathway to\nconceal the actor's indentity and avoid detection.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - Tor Activity to the Internet\t", - "query": "(destination.port:9001 or destination.port:9030) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "Tor Activity to the Internet", + "query": "network.transport: tcp and destination.port: (9001 or 9030) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "7d2c38d7-ede7-4bdf-b140-445906e6c540", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json index 4f1dba808600e..d46ee76ba72b2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_from_the_internet.json @@ -1,17 +1,52 @@ { - "description": "Network - VNC (Virtual Network Computing) From the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of VNC traffic\nfrom the Internet. VNC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - VNC (Virtual Network Computing) From the Internet\t", - "query": "destination.port:5800 and not source.ip:10.0.0.0/8 and not source.ip:172.16.0.0/12 and not source.ip:192.168.0.0/16", - "risk_score": 50, + "name": "VNC (Virtual Network Computing) from the Internet", + "query": "network.transport: tcp and (destination.port >= 5800 and destination.port <= 5810) and (\n network.direction: inbound or (\n not source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "5700cb81-df44-46aa-a5d7-337798f53eb8", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0001", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1190", + "name": "Exploit Public-Facing Application", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json index fd04ae3ae7dee..d820cedc335ab 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/network_vnc_virtual_network_computing_to_the_internet.json @@ -1,17 +1,37 @@ { - "description": "Network - VNC (Virtual Network Computing) To the Internet\t", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This signal detects network events that may indicate the use of VNC traffic\nfrom the Internet. VNC is commonly used by system administrators to remotely\ncontrol a system for maintenance or to use shared resources. It should almost\nnever be directly exposed to the Internet, as it is frequently targetted and\nexploited by threat actors as an initial access or backdoor vector.\n", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "Network - VNC (Virtual Network Computing) To the Internet\t", - "query": "destination.port:5800 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, + "name": "VNC (Virtual Network Computing) to the Internet", + "query": "network.transport: tcp and (destination.port >= 5800 and destination.port <= 5810) and (\n network.direction: outbound or (\n source.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and\n not destination.ip: (10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)\n )\n)\n", + "risk_score": 25, "rule_id": "3ad49c61-7adc-42c1-b788-732eda2f5abf", "severity": "low", - "to": "now", + "tags": ["Elastic", "network"], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1043", + "name": "Commonly Used Port", + "reference": "https://attack.mitre.org/techniques/T1043/" + } + ] + } + ], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json index d563944171b7a..9d787d3ab738f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json @@ -1,35 +1,43 @@ { - "description": "Null user agent", - "enabled": false, + "description": "A request to a web application server contained no identifying user agent string.", + "false_positives": [ + "Some normal applications and scripts may contain no user agent. Most legitmate web requests from the Internet contain a user agent string. Requests from web browsers almost always contain a user agent string. If the source is unexpected, or the user is unauthorized, or the request is unusual, these may be suspicious or malicious activity." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "filters": [ { - "meta": { - "alias": null, - "negate": true, - "disabled": false, - "type": "exists", - "key": "user_agent.original", - "value": "exists", - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index" + "$state": { + "store": "appState" }, "exists": { "field": "user_agent.original" }, - "$state": { - "store": "appState" + "meta": { + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "key": "user_agent.original", + "negate": true, + "type": "exists", + "value": "exists" } } ], - "from": "now-6m", - "immutable": true, - "interval": "5m", "language": "kuery", - "name": "Null user agent", + "max_signals": 33, + "name": "Web Application Suspicious Activity: No User Agent", "query": "url.path: *", + "references": ["https://en.wikipedia.org/wiki/User_agent"], "risk_score": 50, "rule_id": "43303fd4-4839-4e48-b2b2-803ab060758d", "severity": "low", - "to": "now", + "tags": ["Elastic", "apm"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json index 48cf20bcbacf7..c92b801995837 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json @@ -1,17 +1,24 @@ { - "description": "SQLmap user agent", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "description": "This is an example of how to detect an unwanted web client user agent. This search matches the user agent for sqlmap 1.3.11 which is a popular FOSS tool for testing web applications for SQL injection vulnerabilities. ", + "false_positives": [ + "This signal does not indicate that a SQL injection attack occured, only that the sqlmap tool was used. Security scans and tests may result in these errors. If the source is not an authorized security tester, this is generally suspicious or malicious activity." + ], + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", - "name": "SQLmap user agent", + "name": "Web Application Suspicious Activity: sqlmap User Agent", "query": "user_agent.original:\"sqlmap/1.3.11#stable (http://sqlmap.org)\"", + "references": ["http://sqlmap.org/"], "risk_score": 50, "rule_id": "d49cc73f-7a16-4def-89ce-9fc7127d7820", "severity": "low", - "to": "now", + "tags": ["Elastic", "apm"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json index 301954fc58745..91abe1368b011 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Background Intelligent Transfer Service (BITS) Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Background Intelligent Transfer Service (BITS) Connecting to the Internet", "query": "process.name:bitsadmin.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "7edadee3-98ae-472c-b1c4-8c0a2c4877cc", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json index 22429df353679..f3e62405d6e18 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_burp_ce_activity.json @@ -1,17 +1,20 @@ { "description": "Windows Burp CE activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Burp CE activity", "query": "process.name:BurpSuiteCommunity.exe", "risk_score": 50, "rule_id": "0f09845b-2ec8-4770-8155-7df3d4e402cc", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json index 6cf9a375586ba..451a1ad4942de 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Certutil Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Certutil Connecting to the Internet", "query": "process.name:certutil.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "1a2cf526-6784-4c51-a2b9-f0adcc05d85c", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json index c404bf7a05c85..6a2a9213a94a9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Command Prompt Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Command Prompt Connecting to the Internet", "query": "process.name:cmd.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "89f9a4b0-9f8f-4ee0-8823-c4751a6d6696", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_internet_explorer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_internet_explorer.json index 1bcad8ae016ef..92edd71a665dd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_internet_explorer.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_internet_explorer.json @@ -1,16 +1,20 @@ { "description": "Command shell started by Internet Explorer", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Command shell started by Internet Explorer", "query": "process.parent.name:iexplore.exe and process.name:cmd.exe", "risk_score": 50, "rule_id": "7a6e1e81-deae-4cf6-b807-9a768fff3c06", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json index faa9694f658ff..663b2485fab93 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json @@ -1,16 +1,20 @@ { "description": "Command shell started by Powershell", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Command shell started by Powershell", "query": "process.parent.name:powershell.exe and process.name:cmd.exe", "risk_score": 50, "rule_id": "0f616aee-8161-4120-857e-742366f5eeb3", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json index aa371fea3f01d..73ab27a131e3d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json @@ -1,16 +1,20 @@ { "description": "Command shell started by Svchost", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Command shell started by Svchost", "query": "process.parent.name:svchost.exe and process.name:cmd.exe", "risk_score": 50, "rule_id": "fd7a6052-58fa-4397-93c3-4795249ccfa2", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json index dec76deb3e888..9516b80412582 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_commands.json @@ -1,17 +1,20 @@ { "description": "Windows Credential Dumping Commands", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Credential Dumping Commands", "query": "event.code: 1 and process.args:*Invoke-Mimikatz-DumpCreds* or process.args:*gsecdump* or process.args:*wce* or (process.args:*procdump* and process.args:*lsass*) or (process.args:*ntdsutil* and process.args:*ntds*ifm*create*)", "risk_score": 50, "rule_id": "66885745-ea38-432c-9edb-599b943948d4", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json index de3fc49fefa37..06a9de8f20720 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_imageload.json @@ -1,17 +1,20 @@ { "description": "Windows Credential Dumping via ImageLoad", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Credential Dumping via ImageLoad", "query": "event.code:7 and not process.name:Sysmon.exe and not process.name:Sysmon64.exe and not process.name:svchost.exe and not process.name:logonui.exe and (file.path:*samlib.dll* or file.path:*WinSCard.dll* or file.path:*cryptdll.dll* or file.path:*hid.dll* or file.path:*vaultcli.dll*)", "risk_score": 50, "rule_id": "f872647c-d070-4b1c-afcc-055f081d9205", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json index 016f49e22a8f8..a19646d2f83cf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_via_registry_save.json @@ -1,17 +1,20 @@ { "description": "Windows Credential Dumping via Registry Save", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Credential Dumping via Registry Save", "query": "event.code: 1 and process.name:reg.exe and process.args:*save* and (process.args:*sam* or process.args:*system*)", "risk_score": 50, "rule_id": "9f6fb56f-4bbd-404e-b955-49dfba7c0e68", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json index cf1334eda6778..9be27cbec023f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_data_compression_using_powershell.json @@ -1,17 +1,20 @@ { "description": "Windows Data Compression Using Powershell", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Data Compression Using Powershell", "query": "event.code: 1 and process.name:powershell.exe and (process.args:*Recurse* and process.args:*Compress-Archive*)", "risk_score": 50, "rule_id": "bc913943-e1f9-4bf5-a593-caca7c2eb0c3", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json index f718e5effe8ae..a4126a9b45ec9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_decoding_using_certutil.json @@ -1,17 +1,20 @@ { "description": "Windows Defense Evasion - Decoding Using Certutil", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Defense Evasion - Decoding Using Certutil", "query": "event.code:1 and process.name:attrib.exe and (process.args:*+h* or process.args:*+s*)", "risk_score": 50, "rule_id": "d9642bf2-87d0-45c2-8781-2bd2017cdbb8", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json index 844a3fc2ac9ec..edba96cbcc37b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_or_persistence_via_hidden_files.json @@ -1,17 +1,20 @@ { "description": "Windows Defense Evasion or Persistence via Hidden Files", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Defense Evasion or Persistence via Hidden Files", "query": "event.code:1 and process.name:attrib.exe and (process.args:\"+h\" or process.args:\"+s\")", "risk_score": 50, "rule_id": "340a0063-baba-447b-8396-26a5cc1eb684", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json index b98b0e3f8d0aa..56c2a3ecd7eaf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json @@ -1,17 +1,20 @@ { "description": "Windows Defense evasion via Filter Manager", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Defense evasion via Filter Manager", "query": "event.code:1 and process.name:fltmc.exe", "risk_score": 50, "rule_id": "06dceabf-adca-48af-ac79-ffdf4c3b1e9a", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json index 2d37fedd30480..2f25c7282a87d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_windows_event_log_tools.json @@ -1,17 +1,20 @@ { "description": "Windows Defense Evasion via Windows Event Log Tools", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Defense Evasion via Windows Event Log Tools", "query": "event.code:1 and process.name:wevtutil.exe", "risk_score": 50, "rule_id": "07979a67-ab4d-460f-9ff3-bf1352de6762", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json index 027556b7f2456..079d33bf0f676 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via Compiled HTML File", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via Compiled HTML File", "query": "event.code:1 and process.name:hh.exe", "risk_score": 50, "rule_id": "e3343ab9-4245-4715-b344-e11c56b0a47f", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json index 30d2f4e3c8936..9c8a4f4b47dce 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_connection_manager.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via Connection Manager", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via Connection Manager", "query": "event.code:1 and process.parent.name:pcalua.exe or (process.name:bash.exe or process.name:forfiles.exe or process.name:pcalua.exe)", "risk_score": 50, "rule_id": "f2728299-167a-489c-913c-2e0955ac3c40", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json index aa67d2aebe64b..d986ccbb865f8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_microsoft_html_application_hta.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via Microsoft HTML Application (HTA)", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via Microsoft HTML Application (HTA)", "query": "event.code:1 and (process.parent.args:*mshta* or process.args:*mshta*)", "risk_score": 50, "rule_id": "b007cc82-c522-48d1-b7a7-53f63c50c494", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json index 20e0eba610e95..26e99cbb59e48 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via .NET COM Assemblies", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via .NET COM Assemblies", "query": "event.code:1 and (process.name:regasm.exe or process.name:regsvcs.exe)", "risk_score": 50, "rule_id": "5c12412f-602c-4120-8c4f-69d723dbba04", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json index 9371ec67fcec5..06d4a075c4e6b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_regsvr32.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via Regsvr32", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via Regsvr32", "query": "event.code: 1 and scrobj.dll and (process.name:certutil.exe or process.name:regsvr32.exe or process.name:rundll32.exe)", "risk_score": 50, "rule_id": "b7333d08-be4b-4cb4-b81e-924ae37b3143", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json index 3153c0d38d2fd..bc3ebf38181a0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json @@ -1,17 +1,20 @@ { "description": "Windows Execution via Trusted Developer Utilities", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Execution via Trusted Developer Utilities", "query": "event.code:1 and (process.name:MSBuild.exe or process.name:msxsl.exe)", "risk_score": 50, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae1", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json index 4e39ab96840df..cec9fe4a4aebe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: HTML Help executable Program Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: HTML Help executable Program Connecting to the Internet", "query": "process.name:hh.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "b29ee2be-bf99-446c-ab1a-2dc0183394b8", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json index d00f671c7b606..3e80b58377af6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_image_load_from_a_temp_directory.json @@ -1,6 +1,13 @@ { "description": "Windows image load from a temp directory", - "enabled": false, + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "filters": [ { "$state": { @@ -28,16 +35,13 @@ } } ], - "from": "now-6m", - "immutable": true, - "interval": "5m", "language": "kuery", "name": "Windows image load from a temp directory", "query": "file.path:Temp", "risk_score": 50, "rule_id": "f23e4cc7-6825-4a28-b27a-e67437a9a806", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json index cbde84a5fc858..a7f22358a11d9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_indirect_command_execution.json @@ -1,17 +1,20 @@ { "description": "Windows Indirect Command Execution", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Indirect Command Execution", "query": "event.code:1 and process.parent.name:pcalua.exe or (process.name:bash.exe or process.name:forfiles.exe or process.name:pcalua.exe)", "risk_score": 50, "rule_id": "ff969842-c573-4e69-8e12-02fb303290f2", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json index e60c57ebc489a..8aae9dc83a1cd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_iodine_activity.json @@ -1,17 +1,20 @@ { "description": "Windows Iodine activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Iodine activity", "query": "event.code: 1 and process.name:iodine.exe or process.name:iodined.exe", "risk_score": 50, "rule_id": "fcbbf0b2-99c5-4c7f-8411-dc9ee392e43f", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json index 378b23825dc82..da525a8573264 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_management_instrumentation_wmi_execution.json @@ -1,17 +1,20 @@ { "description": "Windows Management Instrumentation (WMI) Execution", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Management Instrumentation (WMI) Execution", "query": "event.code:1 and (process.parent.args:*wmiprvse.exe* or process.name:wmic.exe or process.args:*wmic* )", "risk_score": 50, "rule_id": "cec5eb81-6e01-40e5-a1bf-bf175cce4eb4", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json index 8b5dffeec67af..2f7a8dbee7c80 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_microsoft_html_application_hta_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Microsoft HTML Application (HTA) Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Microsoft HTML Application (HTA) Connecting to the Internet", "query": "process.name:mshta.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "b084514b-e8ba-4bc4-bc2b-50fe145a4215", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json index 1016d2c7af5f2..64641bb539cb9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_mimikatz_activity.json @@ -1,16 +1,20 @@ { "description": "Windows Mimikatz activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Mimikatz activity", "query": "event.code: 1 and process.name:mimikatz.exe", "risk_score": 50, "rule_id": "5346463d-062f-419d-88ff-7a5e97875210", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json index e6d606384d454..bb08cd4023e6a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Misc LOLBin Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Misc LOLBin Connecting to the Internet", "query": "(process.name:expand.exe or process.name:extrac.exe or process.name:ieexec.exe or process.name:makecab.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "63e65ec3-43b1-45b0-8f2d-45b34291dc44", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json index f8689bb314857..fce37db4fae3d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_activity_by_the_system_account.json @@ -1,16 +1,20 @@ { "description": "Windows net command activity by the SYSTEM account", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows net command activity by the SYSTEM account", "query": "process.name: (net.exe or net1.exe) and user.name:SYSTEM", "risk_score": 50, "rule_id": "c3f5dc81-a8b4-4144-95a7-d0a818d7355d", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json index 6b895f30fd5c4..555bb4afb0c10 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_user_command_activity.json @@ -1,16 +1,20 @@ { "description": "Windows net user command activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows net user command activity", "query": "process.name:net.exe and process.args:user and event.code:1", "risk_score": 50, "rule_id": "b039a69d-7fba-4c84-8029-57ac12548a15", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json index 8b105514ec798..288bc6dd2375b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_activity.json @@ -1,16 +1,20 @@ { "description": "Windows Netcat activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Netcat activity", "query": "process.name:ncat.exe and event.code:1", "risk_score": 50, "rule_id": "e2437364-0c89-4e65-a34b-782cfbb7690b", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json index c16c91d9637e5..a533cd36ffdcf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_netcat_network_activity.json @@ -1,16 +1,20 @@ { "description": "Windows Netcat network activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Netcat network activity", "query": "process.name:ncat.exe and event.action:\"Network connection detected (rule: NetworkConnect)\"", "risk_score": 50, "rule_id": "ebdc4b6f-7fdb-4c21-bbd6-59e1ed11024a", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json index a22b12d242414..173e5191d9e65 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_network_anomalous_windows_process_using_https_ports.json @@ -1,17 +1,20 @@ { "description": "Windows Network - Anomalous Windows Process Using HTTP/S Ports", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Network - Anomalous Windows Process Using HTTP/S Ports", "query": "(destination.port:443 or destination.port:80) and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not process.name:chrome.exe and not process.name:explorer.exe and not process.name:filebeat.exe and not process.name:firefox.exe and not process.name:iexplore.exe and not process.name:jusched.exe and not process.name:MpCmdRun.exe and not process.name:MpSigStub.exe and not process.name:msfeedssync.exe and not process.name:packetbeat.exe and not process.name:powershell.exe and not process.name:procexp64.exe and not process.name:svchost.exe and not process.name:taskhostw.exe and not process.name:winlogbeat.exe", "risk_score": 50, "rule_id": "b486fa9e-e6c7-44a1-b07d-7d5f07f21ce1", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json index a0c3b5be64d1c..dc231e5edce1e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_activity.json @@ -1,16 +1,20 @@ { "description": "Windows nmap activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows nmap activity", "query": "process.name:nmap.exe and event.code:1", "risk_score": 50, "rule_id": "5a4b2a98-31a6-4852-b224-d63aeb9e172d", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json index 0195367b6f712..ccd49169e6497 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_nmap_scan_activity.json @@ -1,16 +1,20 @@ { "description": "Windows nmap scan activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows nmap scan activity", "query": "process.name:nmap.exe and event.action:\"Network connection detected (rule: NetworkConnect)\"", "risk_score": 50, "rule_id": "54413985-a3da-4f45-b238-75afb65a1bae", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json index 421cadfa8a63d..f7a331ca01474 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_payload_obfuscation_via_certutil.json @@ -1,17 +1,20 @@ { "description": "Windows Payload Obfuscation via Certutil", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Payload Obfuscation via Certutil", "query": "event.code:1 and process.name:certutil.exe and (process.args:*encode* or process.args:*ToBase64String*)", "risk_score": 50, "rule_id": "ce7c270c-c69b-47dd-8c21-60a35e92f372", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json index 47de4ba9ff6e7..379cab0f07438 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_or_priv_escalation_via_hooking.json @@ -1,17 +1,20 @@ { "description": "Windows Persistence or Priv Escalation via Hooking", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Persistence or Priv Escalation via Hooking", "query": "event.code:1 and process.name:mavinject.exe and processs.args:*INJECTRUNNING*", "risk_score": 50, "rule_id": "015f070d-cf70-437c-99d1-472e31d36b03", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json index c6e558a3be260..ca5daf772a22e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json @@ -1,17 +1,20 @@ { "description": "Windows Persistence via Application Shimming", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Persistence via Application Shimming", "query": "event.code:1 and process.name:sdbinst.exe", "risk_score": 50, "rule_id": "fd4a992d-6130-4802-9ff8-829b89ae801f", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json index b6d97628f98ec..4c6515f33fad0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_bits_jobs.json @@ -1,17 +1,20 @@ { "description": "Windows Persistence via BITS Jobs", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Persistence via BITS Jobs", "query": "event.code:1 and (process.name:bitsadmin.exe or process.args:*Start-BitsTransfer*)", "risk_score": 50, "rule_id": "7904fb20-172c-43fb-83e4-bfe27e3c702c", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json index 782ce7a6eec92..01b56a1ecd1e0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_modification_of_existing_service.json @@ -1,17 +1,20 @@ { "description": "Windows Persistence via Modification of Existing Service", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Persistence via Modification of Existing Service", "query": "event.code:1 and process.args:*sc*config*binpath* and (process.name:cmd.exe or process.name:powershell.exe or process.name:sc.exe)", "risk_score": 50, "rule_id": "3bb04809-84ab-4487-bd99-ccc58675bd40", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json index 19e6ac51158eb..50b31aa7033eb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_netshell_helper_dll.json @@ -1,17 +1,20 @@ { "description": "Windows Persistence via Netshell Helper DLL", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Persistence via Netshell Helper DLL", "query": "event.code:1 and process.name:netsh.exe and process.args:*helper*", "risk_score": 50, "rule_id": "d7c2561d-2758-46ad-b5a9-247efb9eea21", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json index 50e3d6e0f3874..5198f85b999ac 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_powershell_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Powershell Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Powershell Connecting to the Internet", "query": "process.name:powershell.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:169.254.169.254/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "a8cfa646-e4d8-48b5-884e-6204ba77fc8d", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json index 96faa2a88e3a6..f24460373f55d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json @@ -1,17 +1,20 @@ { "description": "Windows Priv Escalation via Accessibility Features", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Priv Escalation via Accessibility Features", "query": "event.code:1 and process.parent.name:winlogon.exe and (process.name:atbroker.exe or process.name:displayswitch.exe or process.name:magnify.exe or process.name:narrator.exe or process.name:osk.exe or process.name:sethc.exe or process.name:utilman.exe)", "risk_score": 50, "rule_id": "7405ddf1-6c8e-41ce-818f-48bea6bcaed8", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json index 572a9ede23e2a..fd2bfcf216bf3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json @@ -1,17 +1,20 @@ { "description": "Windows Process Discovery via Tasklist Command", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Process Discovery via Tasklist Command", "query": "event.code:1 and process.name:tasklist.exe", "risk_score": 50, "rule_id": "cc16f774-59f9-462d-8b98-d27ccd4519ec", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_execution_via_wmi.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_execution_via_wmi.json index 9e29c82e48872..1e14de81b7cb2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_execution_via_wmi.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_execution_via_wmi.json @@ -1,17 +1,20 @@ { "description": "Process Execution via WMI", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Process Execution via WMI", "query": "process.name:scrcons.exe", "risk_score": 50, "rule_id": "7e6cd4b9-6346-4683-b3e6-6a3e66f3208f", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_acrobat_reader_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_acrobat_reader_possible_payload.json index e96c223765cbd..973a7df57f712 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_acrobat_reader_possible_payload.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_acrobat_reader_possible_payload.json @@ -1,16 +1,20 @@ { "description": "Process started by Acrobat reader - possible payload", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Process started by Acrobat reader - possible payload", "query": "process.parent.name:AcroRd32.exe and event.code:1", "risk_score": 50, "rule_id": "b6422896-b6e3-45c3-9d9e-4eccb2a25270", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_ms_office_program_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_ms_office_program_possible_payload.json index c2e185cd0c7eb..cb7b234c21f8c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_ms_office_program_possible_payload.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_ms_office_program_possible_payload.json @@ -1,16 +1,20 @@ { "description": "Process started by MS Office program - possible payload", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Process started by MS Office program - possible payload", "query": "process.parent.name:EXCEL.EXE or process.parent.name:MSPUB.EXE or process.parent.name:OUTLOOK.EXE or process.parent.name:POWERPNT.EXE or process.parent.name:VISIO.EXE or process.parent.name:WINWORD.EXE and event.code:1", "risk_score": 50, "rule_id": "838dcec6-ce9a-4cdd-9ca8-f6512cf6d559", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json index 6902807cb51d1..c684be0732064 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_started_by_the_java_runtime.json @@ -1,16 +1,20 @@ { "description": "Windows process started by the Java runtime", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows process started by the Java runtime", "query": "process.parent.name:javaw.exe and event.code:1", "risk_score": 50, "rule_id": "159168a1-b1d0-4e5c-ad72-c1e9ae2edec2", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_psexec_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_psexec_activity.json index 280f061ed7785..e4c91b6f89cd4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_psexec_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_psexec_activity.json @@ -1,17 +1,20 @@ { "description": "PSexec activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "PSexec activity", "query": "process.name:PsExec.exe or process.name:PsExec64.exe", "risk_score": 50, "rule_id": "3e61ab8b-0f39-4d2e-ab64-332f0d0b3ad7", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json index 563553a24a3e7..a106eda988e94 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Register Server Program Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Register Server Program Connecting to the Internet", "query": "(process.name:regsvr32.exe or process.name:regsvr64.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:169.254.169.254/32 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "fb02b8d3-71ee-4af1-bacd-215d23f17efa", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json index d9bc00cfbd336..49642d271d4ea 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_local.json @@ -1,17 +1,20 @@ { "description": "Windows Registry Query, Local", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Registry Query, Local", "query": "event.code: 1 and process.name:reg.exe and process.args:*query* and process.args:*reg*", "risk_score": 50, "rule_id": "b9074c74-6d23-4b07-927e-cc18b318a088", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json index ddf8ff569e35f..884deb7645a67 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_registry_query_network.json @@ -1,17 +1,20 @@ { "description": "Windows Registry Query, Network", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Registry Query, Network", "query": "event.code: 1 and process.name:reg.exe and process.args:*query* and process.args:*reg*", "risk_score": 50, "rule_id": "f5412e37-981e-4d37-a1b2-eddaf797445a", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json index 0e67b777ac6dc..08d96ad741502 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_remote_management_execution.json @@ -1,17 +1,20 @@ { "description": "Windows Remote Management Execution", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Remote Management Execution", "query": "(process.name:wsmprovhost.exe or process.name:winrm.cmd) and (process.args:*Enable-PSRemoting -Force* or process.args:*Invoke-Command -computer_name* or process.args:*wmic*node*process call create*)", "risk_score": 50, "rule_id": "ced66221-3e07-40ee-8588-5f107e7d50d8", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json index 58fd2df8f15ef..56f5b71ceb510 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_scheduled_task_activity.json @@ -1,17 +1,20 @@ { "description": "Windows Scheduled Task Activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Scheduled Task Activity", "query": "event.code:1 and (process.name:schtasks.exe or process.name:taskeng.exe) or (event.code:1 and process.name:svchost.exe and not process.parent.executable: \"C:\\Windows\\System32\\services.exe\" )", "risk_score": 50, "rule_id": "a1abd54d-3021-4f21-b2d1-0c6bc5c4051f", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json index 41559425538ab..a700ac0a48bc2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_script_interpreter_connecting_to_the_internet.json @@ -1,17 +1,20 @@ { "description": "Windows: Script Interpreter Connecting to the Internet", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows: Script Interpreter Connecting to the Internet", "query": "(process.name:cscript.exe or process.name:wscript.exe) and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", "risk_score": 50, "rule_id": "2cc4597c-b0c9-4481-b1a6-e6c05cfc9f02", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json index 64185c784e028..1dc62c7b5db42 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution.json @@ -1,17 +1,20 @@ { "description": "Windows Signed Binary Proxy Execution", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Signed Binary Proxy Execution", "query": "event.code:1 and http and (process.name:certutil.exe or process.name:msiexec.exe)", "risk_score": 50, "rule_id": "7edb573f-1f9b-4161-8c19-c7c383bb17f2", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json index b1146f07612f6..717d99ee7901c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_signed_binary_proxy_execution_download.json @@ -1,17 +1,20 @@ { "description": "Windows Signed Binary Proxy Execution Download", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Signed Binary Proxy Execution Download", "query": " event.code:3 and http and (process.name:certutil.exe or process.name:replace.exe)", "risk_score": 50, "rule_id": "68ecc190-cce2-4021-b976-c7c846ac0a00", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_process_started_by_a_script.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_process_started_by_a_script.json index c5a7db434ac38..82733cbb6b21c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_process_started_by_a_script.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_suspicious_process_started_by_a_script.json @@ -1,16 +1,20 @@ { "description": "Suspicious process started by a script", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Suspicious process started by a script", "query": "(process.parent.name:cmd.exe or process.parent.name:cscript.exe or process.parent.name:mshta.exe or process.parent.name:powershell.exe or process.parent.name:rundll32.exe or process.parent.name:wscript.exe or process.parent.name:wmiprvse.exe) and (process.name:bitsadmin.exe or process.name:certutil.exe or mshta.exe or process.name:nslookup.exe or process.name:schtasks.exe) and event.code:1", "risk_score": 50, "rule_id": "89db767d-99f9-479f-8052-9205fd3090c4", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json index b13a20518893c..768cd65c5e4f5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json @@ -1,16 +1,20 @@ { "description": "Windows whoami command activity", - "enabled": false, - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows whoami command activity", "query": "process.name:whoami.exe and event.code:1", "risk_score": 50, "rule_id": "ef862985-3f13-4262-a686-5f357bbb9bc2", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_windump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_windump_activity.json index 8fc548b694b02..4f33e95cfe2e9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_windump_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_windump_activity.json @@ -1,17 +1,20 @@ { "description": "WinDump activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "WinDump activity", "query": "process.name:WinDump.exe", "risk_score": 50, "rule_id": "a342cfcb-8420-46a4-8d85-53edc631e0d6", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json index 30ee18fe53557..72db4aed03c88 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_wireshark_activity.json @@ -1,17 +1,20 @@ { "description": "Windows Wireshark activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", + "index": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "packetbeat-*", + "winlogbeat-*" + ], "language": "kuery", "name": "Windows Wireshark activity", "query": "process.name:wireshark.exe", "risk_score": 50, "rule_id": "9af965ed-d501-4541-97f6-5f8d2a39737b", "severity": "low", - "to": "now", + "tags": ["Elastic"], "type": "query", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts index e8fa0b562bd24..e8e883701c6a9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert } from '../../../../../alerting/server/types'; +import { SanitizedAlert } from '../../../../../alerting/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; import { ReadRuleParams, isAlertType } from './types'; @@ -21,7 +21,7 @@ export const readRules = async ({ alertsClient, id, ruleId, -}: ReadRuleParams): Promise => { +}: ReadRuleParams): Promise => { if (id != null) { try { const rule = await alertsClient.get({ id }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts index 0120a90df58ae..a169e5107c316 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -38,7 +38,7 @@ export const updatePrepackagedRules = async ( tags, to, type, - threats, + threat, references, version, } = rule; @@ -70,7 +70,7 @@ export const updatePrepackagedRules = async ( tags, to, type, - threats, + threat, references, version, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts index c079c637d88b7..0d426fb03bd37 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts @@ -5,6 +5,7 @@ */ import { calculateInterval, calculateName, calculateVersion } from './update_rules'; +import { UpdateRuleParams } from './types'; describe('update_rules', () => { describe('#calculateInterval', () => { @@ -25,20 +26,26 @@ describe('update_rules', () => { }); describe('#calculateVersion', () => { - test('given preVersion and nextVersion numbers being null it will return a 1', () => { - expect(calculateVersion(null, null)).toEqual(1); + test('returning the same version number if given an immutable but no updated version number', () => { + expect(calculateVersion(true, 1, { description: 'some description change' })).toEqual(1); }); - test('given preVersion and nextVersion numbers being undefined it will return a 1', () => { - expect(calculateVersion(undefined, undefined)).toEqual(1); + test('returning an updated version number if given an immutable and an updated version number', () => { + expect(calculateVersion(true, 2, { description: 'some description change' })).toEqual(2); }); - test('given prevVersion as null and nextVersion being defined, nextVersion will be returned', () => { - expect(calculateVersion(undefined, 5)).toEqual(5); + test('returning an updated version number if not given an immutable but but an updated description', () => { + expect(calculateVersion(false, 1, { description: 'some description change' })).toEqual(2); }); - test('given prevVersion as being defined but nextVersion is not, prevVersion will be incremented by 1', () => { - expect(calculateVersion(5, undefined)).toEqual(6); + test('returning the same version number but a undefined description', () => { + expect(calculateVersion(false, 1, { description: undefined })).toEqual(1); + }); + + test('returning an updated version number if not given an immutable but an updated falsy value', () => { + expect( + calculateVersion(false, 1, ({ description: false } as unknown) as UpdateRuleParams) + ).toEqual(2); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index f6932fc37d85a..e37e899c7de51 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { defaults } from 'lodash/fp'; -import { AlertAction, IntervalSchedule } from '../../../../../alerting/server/types'; +import { defaults, pickBy, isEmpty } from 'lodash/fp'; import { readRules } from './read_rules'; import { UpdateRuleParams, IRuleSavedAttributesSavedObjectAttributes } from './types'; import { addTags } from './add_tags'; @@ -25,23 +24,37 @@ export const calculateInterval = ( }; export const calculateVersion = ( - prevVersion: number | null | undefined, - nextVersion: number | null | undefined -) => { - if (nextVersion == null) { - if (prevVersion != null) { - return prevVersion + 1; + immutable: boolean, + currentVersion: number, + updateProperties: Partial> +): number => { + // early return if we are pre-packaged/immutable rule to be safe. We are never responsible + // for changing the version number of an immutable. Immutables are only responsible for changing + // their own version number. This would be really bad if an immutable version number is bumped by us + // due to a bug, hence the extra check and early bail if that is detected. + if (immutable === true) { + if (updateProperties.version != null) { + // we are an immutable rule but we are asking to update the version number so go ahead + // and update it to what is asked. + return updateProperties.version; } else { - // really should never hit this code but to just be - // safe let us always check the prev version and if - // its null or undefined return a 1 - return 1; + // we are immutable and not asking to update the version number so return the existing version + return currentVersion; } + } + + // white list all properties but the enabled/disabled flag. We don't want to auto-increment + // the version number if only the enabled/disabled flag is being set. Likewise if we get other + // properties we are not expecting such as updatedAt we do not to cause a version number bump + // on that either. + const removedNullValues = pickBy( + (value: unknown) => value != null, + updateProperties + ); + if (isEmpty(removedNullValues)) { + return currentVersion; } else { - // The user wants to custom update their version number which - // means this could be in the past. Up to the user if they want - // to do this - return nextVersion; + return currentVersion + 1; } }; @@ -90,7 +103,7 @@ export const updateRules = async ({ name, severity, tags, - threats, + threat, to, type, references, @@ -101,15 +114,35 @@ export const updateRules = async ({ return null; } - // TODO: Remove this as cast as soon as rule.actions TypeScript bug is fixed - // where it is trying to return AlertAction[] or RawAlertAction[] - const actions = (rule.actions as AlertAction[] | undefined) || []; - - const params = rule.params || {}; + const calculatedVersion = calculateVersion(rule.params.immutable, rule.params.version, { + description, + falsePositives, + query, + language, + outputIndex, + savedId, + timelineId, + timelineTitle, + meta, + filters, + from, + index, + interval, + maxSignals, + riskScore, + name, + severity, + tags, + threat, + to, + type, + references, + version, + }); const nextParams = defaults( { - ...params, + ...rule.params, }, { description, @@ -128,40 +161,29 @@ export const updateRules = async ({ maxSignals, riskScore, severity, - threats, + threat, to, type, updatedAt: new Date().toISOString(), references, - version: calculateVersion(rule.params.version, version), + version: calculatedVersion, } ); - const ruleCurrentStatus = savedObjectsClient - ? await savedObjectsClient.find({ - type: ruleStatusSavedObjectType, - perPage: 1, - sortField: 'statusDate', - sortOrder: 'desc', - search: rule.id, - searchFields: ['alertId'], - }) - : null; - if (rule.enabled && enabled === false) { await alertsClient.disable({ id: rule.id }); - // set current status for this rule to null to represent disabled, - // but keep last_success_at / last_failure_at properties intact for - // use on frontend while rule is disabled. - if (ruleCurrentStatus && ruleCurrentStatus.saved_objects.length > 0) { - const currentStatusToDisable = ruleCurrentStatus.saved_objects[0]; - currentStatusToDisable.attributes.status = null; - await savedObjectsClient?.update(ruleStatusSavedObjectType, currentStatusToDisable.id, { - ...currentStatusToDisable.attributes, - }); - } } else if (!rule.enabled && enabled === true) { await alertsClient.enable({ id: rule.id }); + const ruleCurrentStatus = savedObjectsClient + ? await savedObjectsClient.find({ + type: ruleStatusSavedObjectType, + perPage: 1, + sortField: 'statusDate', + sortOrder: 'desc', + search: rule.id, + searchFields: ['alertId'], + }) + : null; // set current status for this rule to be 'going to run' if (ruleCurrentStatus && ruleCurrentStatus.saved_objects.length > 0) { const currentStatusToDisable = ruleCurrentStatus.saved_objects[0]; @@ -183,16 +205,9 @@ export const updateRules = async ({ ), name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { - interval: calculateInterval( - interval, - // TODO: we assume the schedule is an interval schedule due to a problem - // in the Alerting api, which should be addressed by the following - // issue: https://github.com/elastic/kibana/issues/49703 - // Once this issue is closed, the type should be correctly returned by alerting - (rule.schedule as IntervalSchedule).interval - ), + interval: calculateInterval(interval, rule.schedule.interval), }, - actions, + actions: rule.actions, params: nextParams, }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/import/multiple_ruleid_queries.ndjson b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/import/multiple_ruleid_queries.ndjson index 4c45ac7a1b38b..e395916363ab6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/import/multiple_ruleid_queries.ndjson +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/import/multiple_ruleid_queries.ndjson @@ -1,3 +1,3 @@ -{"created_at":"2020-01-09T01:38:00.740Z","updated_at":"2020-01-09T01:38:00.740Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"6688f367-1aa2-4895-a5a8-b3701eecf57d","immutable":false,"interval":"5m","rule_id":"query-rule-id-1","language":"kuery","output_index":".siem-signals-frank-default","max_signals":100,"risk_score":1,"name":"Query with a rule id Number 1","query":"user.name: root or user.name: admin","references":[],"severity":"high","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threats":[],"version":1} -{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-2","language":"kuery","output_index":".siem-signals-frank-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threats":[],"version":1} +{"created_at":"2020-01-09T01:38:00.740Z","updated_at":"2020-01-09T01:38:00.740Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"6688f367-1aa2-4895-a5a8-b3701eecf57d","immutable":false,"interval":"5m","rule_id":"query-rule-id-1","language":"kuery","output_index":".siem-signals-frank-default","max_signals":100,"risk_score":1,"name":"Query with a rule id Number 1","query":"user.name: root or user.name: admin","references":[],"severity":"high","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threat":[],"version":1} +{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-2","language":"kuery","output_index":".siem-signals-frank-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threat":[],"version":1} {"exported_count":2,"missing_rules":[],"missing_rules_count":0} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_mitre_attack.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_mitre_attack.json index f728e3b988206..fec582ce77295 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_mitre_attack.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_mitre_attack.json @@ -1,11 +1,11 @@ { "name": "Query which has Mitre Attack Data", - "description": "Example query which has Mitre Attack Data as threats", + "description": "Example query which has Mitre Attack Data as threat", "risk_score": 1, "severity": "high", "type": "query", "query": "user.name: root or user.name: admin", - "threats": [ + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -13,7 +13,7 @@ "name": "impact", "reference": "https://attack.mitre.org/tactics/TA0040/" }, - "techniques": [ + "technique": [ { "id": "T1499", "name": "endpoint denial of service", @@ -28,7 +28,7 @@ "name": "Automated Exfiltration", "reference": "https://attack.mitre.org/techniques/T1020/" }, - "techniques": [ + "technique": [ { "id": "T1002", "name": "Data Compressed", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json index 4a90d904f31ab..082dd5205a142 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json @@ -40,7 +40,7 @@ "from": "now-6m", "severity": "high", "type": "query", - "threats": [ + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -48,7 +48,7 @@ "name": "impact", "reference": "https://attack.mitre.org/tactics/TA0040/" }, - "techniques": [ + "technique": [ { "id": "T1499", "name": "endpoint denial of service", @@ -63,7 +63,7 @@ "name": "Some other name", "reference": "https://example.com" }, - "techniques": [ + "technique": [ { "id": "some-other-id", "name": "some other technique name", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json index 2b7dbc8cccf0e..9d8b0d6b4922d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json @@ -40,7 +40,7 @@ "from": "now-6m", "severity": "high", "type": "saved_query", - "threats": [ + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -48,7 +48,7 @@ "name": "impact", "reference": "https://attack.mitre.org/tactics/TA0040/" }, - "techniques": [ + "technique": [ { "id": "T1499", "name": "endpoint denial of service", @@ -63,7 +63,7 @@ "name": "Some other name", "reference": "https://example.com" }, - "techniques": [ + "technique": [ { "id": "some-other-id", "name": "some other technique name", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/imports/multiple_ruleid_queries_corrupted.ndjson b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/imports/multiple_ruleid_queries_corrupted.ndjson index 744bd1e078a41..31acaa5bd9803 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/imports/multiple_ruleid_queries_corrupted.ndjson +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/imports/multiple_ruleid_queries_corrupted.ndjson @@ -1,4 +1,4 @@ -{"created_at":"2020-01-09T01:38:00.740Z","updated_at":"2020-01-09T01:38:00.740Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"6688f367-1aa2-4895-a5a8-b3701eecf57d","immutable":false,"interval":"5m","rule_id":"query-rule-id-1","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":1,"name":"Query with a rule id Number 1","query":"user.name: root or user.name: admin","references":[],"severity":"high","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threats":[],"version":1}, -{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-2","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threats":[],"version":1} -{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-3","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threats":[],"version":1} +{"created_at":"2020-01-09T01:38:00.740Z","updated_at":"2020-01-09T01:38:00.740Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"6688f367-1aa2-4895-a5a8-b3701eecf57d","immutable":false,"interval":"5m","rule_id":"query-rule-id-1","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":1,"name":"Query with a rule id Number 1","query":"user.name: root or user.name: admin","references":[],"severity":"high","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threat":[],"version":1}, +{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-2","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threat":[],"version":1} +{"created_at":"2020-01-09T01:38:00.745Z","updated_at":"2020-01-09T01:38:00.745Z","created_by":"elastic_kibana","description":"Query with a rule_id that acts like an external id","enabled":true,"false_positives":[],"from":"now-6m","id":"7a912444-6cfa-4c8f-83f4-2b26fb2a2ed9","immutable":false,"interval":"5m","rule_id":"query-rule-id-3","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":2,"name":"Query with a rule id Number 2","query":"user.name: root or user.name: admin","references":[],"severity":"low","updated_by":"elastic_kibana","tags":[],"to":"now","type":"query","threat":[],"version":1} {"exported_count":2,"missing_rules":[],"missing_rules_count":0} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/depth_test/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/depth_test/README.md index ff3e9a8cf0948..2310ba979da20 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/depth_test/README.md +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/depth_test/README.md @@ -123,7 +123,7 @@ ancestors -> An array tracking all of the parents of this particular signal. As ``` This is indicating that you have a single parent of an event from the signal (signal -> event) and this document has a single -ancestor of that event. Each 30 seconds that goes it will use de-duplication techniques to ensure that this signal is not re-inserted. If after +ancestor of that event. Each 30 seconds that goes it will use de-duplication technique to ensure that this signal is not re-inserted. If after each 30 seconds you DO SEE multiple signals then the bug is a de-duplication bug and a critical bug. If you ever see a duplicate rule in the ancestors array then that is another CRITICAL bug which needs to be fixed. diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/halting_test/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/halting_test/README.md index 7895e579de3a6..b1a83f5317776 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/halting_test/README.md +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/test_cases/signals_on_signals/halting_test/README.md @@ -102,7 +102,7 @@ ancestors -> An array tracking all of the parents of this particular signal. As ``` This is indicating that you have a single parent of an event from the signal (signal -> event) and this document has a single -ancestor of that event. Each 30 seconds that goes it will use de-duplication techniques to ensure that this signal is not re-inserted. If after +ancestor of that event. Each 30 seconds that goes it will use de-duplication technique to ensure that this signal is not re-inserted. If after each 30 seconds you DO SEE multiple signals then the bug is a de-duplication bug and a critical bug. If you ever see a duplicate rule in the ancestors array then that is another CRITICAL bug which needs to be fixed. diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json index a47d0155727d8..eb210cd8153d7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json @@ -40,7 +40,7 @@ "from": "now-6m", "severity": "high", "type": "query", - "threats": [ + "threat": [ { "framework": "MITRE ATT&CK", "tactic": { @@ -48,7 +48,7 @@ "name": "impact", "reference": "https://attack.mitre.org/tactics/TA0040/" }, - "techniques": [ + "technique": [ { "id": "T1499", "name": "endpoint denial of service", @@ -63,7 +63,7 @@ "name": "Some other name", "reference": "https://example.com" }, - "techniques": [ + "technique": [ { "id": "some-other-id", "name": "some other technique name", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/sample_signal.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/sample_signal.json index 8cba054d4d205..48b00930412b4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/sample_signal.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/sample_signal.json @@ -13,13 +13,8 @@ "id": "e6f890af316a25920c951acefc46bfef", "name": "suricata-iowa", "containerized": false, - "ip": [ - "10.128.0.21", - "fe80::4001:aff:fe80:15" - ], - "mac": [ - "42:01:0a:80:00:15" - ], + "ip": ["10.128.0.21", "fe80::4001:aff:fe80:15"], + "mac": ["42:01:0a:80:00:15"], "hostname": "suricata-iowa" }, "client": { @@ -73,11 +68,8 @@ "bytes": 80 }, "process": { - "args": [ - "\/usr\/bin\/python3", - "\/usr\/bin\/google_network_daemon" - ], - "executable": "\/usr\/bin\/python3.7", + "args": ["/usr/bin/python3", "/usr/bin/google_network_daemon"], + "executable": "/usr/bin/python3.7", "created": "2019-11-04T15:01:04.190Z", "pid": 808, "name": "google_network_" @@ -137,38 +129,26 @@ "rule": { "id": "8d9bb7f1-9d8a-4f13-ae18-ccaf4ff61893", "rule_id": "rule-1", - "false_positives": [ - - ], + "false_positives": [], "max_signals": 100, "risk_score": 1, "description": "Detecting root and admin users", "from": "now-6s", "immutable": false, - "index": [ - "auditbeat-*", - "filebeat-*", - "packetbeat-*", - "winlogbeat-*" - ], + "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5s", "language": "kuery", - "name": "Detect Root\/Admin Users", + "name": "Detect Root/Admin Users", "query": "user.name: root or user.name: admin", - "references": [ - "http:\/\/www.example.com", - "https:\/\/ww.example.com" - ], + "references": ["http://www.example.com", "https://ww.example.com"], "severity": "high", - "tags": [ - - ], + "tags": [], "type": "query", "to": "now", "enabled": true, "created_by": "elastic", "updated_by": "elastic", - "threats": [ + "threat": [ { "framework": "fake", "technique": { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts index 9a79b27bac7e9..6507e6ca73ede 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -33,7 +33,7 @@ export const sampleRuleAlertParams = ( timelineId: undefined, timelineTitle: undefined, meta: undefined, - threats: undefined, + threat: undefined, version: 1, updatedAt: '2019-12-17T15:04:25.343Z', createdAt: '2019-12-17T15:04:37.105Z', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts index 1093ff3a8a462..ba1b2f695156b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts @@ -56,7 +56,7 @@ export const buildRule = ({ filters: ruleParams.filters, created_by: createdBy, updated_by: updatedBy, - threats: ruleParams.threats, + threat: ruleParams.threat, version: ruleParams.version, created_at: ruleParams.createdAt, updated_at: ruleParams.updatedAt, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 143fad602daea..370ed65280849 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -53,7 +53,7 @@ export const signalRulesAlertType = ({ maxSignals: schema.number({ defaultValue: DEFAULT_MAX_SIGNALS }), riskScore: schema.number(), severity: schema.string(), - threats: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + threat: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), to: schema.string(), type: schema.string(), updatedAt: schema.string(), @@ -264,8 +264,6 @@ export const signalRulesAlertType = ({ } } } catch (err) { - // TODO: Error handling and writing of errors into a signal that has error - // handling/conditions logger.error( `Error from signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}", ${err.message}` ); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts index c7bd92322360a..d1c9845dbbcfc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts @@ -18,7 +18,7 @@ export interface IMitreAttack { export interface ThreatParams { framework: string; tactic: IMitreAttack; - techniques: IMitreAttack[]; + technique: IMitreAttack[]; } export interface RuleAlertParams { @@ -46,7 +46,7 @@ export interface RuleAlertParams { to: string; timelineId: string | undefined | null; timelineTitle: string | undefined | null; - threats: ThreatParams[] | undefined | null; + threat: ThreatParams[] | undefined | null; type: 'query' | 'saved_query'; version: number; updatedAt: string; diff --git a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts index 237e419368376..f85fb2c9fd753 100644 --- a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts @@ -307,7 +307,7 @@ export const signalFieldsMap: Readonly> = { 'signal.rule.references': 'signal.rule.references', 'signal.rule.severity': 'signal.rule.severity', 'signal.rule.tags': 'signal.rule.tags', - 'signal.rule.threats': 'signal.rule.threats', + 'signal.rule.threat': 'signal.rule.threat', 'signal.rule.type': 'signal.rule.type', 'signal.rule.size': 'signal.rule.size', 'signal.rule.enabled': 'signal.rule.enabled', diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.test.ts b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.test.ts index 858c90258247e..0106970cf9c38 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.test.ts +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.test.ts @@ -83,20 +83,20 @@ describe('loadAlerts', () => { const result = await loadAlerts({ http, searchText: 'apples', page: { index: 0, size: 10 } }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": undefined, - "page": 1, - "per_page": 10, - "search": "apples", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": undefined, + "page": 1, + "per_page": 10, + "search": "apples", + "search_fields": "[\\"name\\",\\"tags\\"]", + }, + }, + ] + `); }); test('should call find API with actionTypesFilter', async () => { @@ -115,20 +115,20 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": undefined, - "page": 1, - "per_page": 10, - "search": "foo", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": undefined, + "page": 1, + "per_page": 10, + "search": "foo", + "search_fields": "[\\"name\\",\\"tags\\"]", + }, + }, + ] + `); }); test('should call find API with typesFilter', async () => { @@ -147,20 +147,20 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": undefined, - "search_fields": undefined, - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + }, + }, + ] + `); }); test('should call find API with actionTypesFilter and typesFilter', async () => { @@ -180,20 +180,20 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": "baz", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": "baz", + "search_fields": "[\\"name\\",\\"tags\\"]", + }, + }, + ] + `); }); test('should call find API with searchText and tagsFilter and typesFilter', async () => { @@ -213,20 +213,20 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": "apples, foo, baz", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": "apples, foo, baz", + "search_fields": "[\\"name\\",\\"tags\\"]", + }, + }, + ] + `); }); }); @@ -258,10 +258,17 @@ describe('createAlert', () => { tags: ['foo'], enabled: true, alertTypeId: 'test', - interval: '1m', + schedule: { + interval: '1m', + }, actions: [], params: {}, throttle: null, + consumer: '', + createdAt: new Date('1970-01-01T00:00:00.000Z'), + updatedAt: new Date('1970-01-01T00:00:00.000Z'), + apiKey: null, + apiKeyOwner: null, }; const resolvedValue: Alert = { ...alertToCreate, @@ -279,7 +286,7 @@ describe('createAlert', () => { Array [ "/api/alert", Object { - "body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"interval\\":\\"1m\\",\\"actions\\":[],\\"params\\":{},\\"throttle\\":null}", + "body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"consumer\\":\\"\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", }, ] `); @@ -292,9 +299,16 @@ describe('updateAlert', () => { throttle: '1m', name: 'test', tags: ['foo'], - interval: '1m', + schedule: { + interval: '1m', + }, params: {}, actions: [], + consumer: '', + createdAt: new Date('1970-01-01T00:00:00.000Z'), + updatedAt: new Date('1970-01-01T00:00:00.000Z'), + apiKey: null, + apiKeyOwner: null, }; const resolvedValue: Alert = { ...alertToUpdate, @@ -314,7 +328,7 @@ describe('updateAlert', () => { Array [ "/api/alert/123", Object { - "body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"interval\\":\\"1m\\",\\"params\\":{},\\"actions\\":[]}", + "body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[],\\"consumer\\":\\"\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", }, ] `); diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.ts b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.ts index 9867acbd7a622..0b4f5731c1315 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.ts +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/lib/alert_api.ts @@ -83,7 +83,7 @@ export async function updateAlert({ id, }: { http: HttpSetup; - alert: Pick; + alert: Pick; id: string; }): Promise { return await http.put(`${BASE_ALERT_API_PATH}/${id}`, { diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alert_add/alert_add.tsx b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alert_add/alert_add.tsx index 9380392112c8e..f11b0b948b2a7 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alert_add/alert_add.tsx +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alert_add/alert_add.tsx @@ -72,7 +72,7 @@ function validateBaseProperties(alertObject: Alert) { }) ); } - if (!alertObject.interval) { + if (!(alertObject.schedule && alertObject.schedule.interval)) { errors.interval.push( i18n.translate('xpack.triggersActionsUI.sections.alertAdd.error.requiredIntervalText', { defaultMessage: 'Check interval is required.', @@ -94,7 +94,9 @@ export const AlertAdd = ({ refreshList }: Props) => { const initialAlert = { params: {}, alertTypeId: null, - interval: '1m', + schedule: { + interval: '1m', + }, actions: [], tags: [], }; @@ -148,7 +150,9 @@ export const AlertAdd = ({ refreshList }: Props) => { value: { params: {}, alertTypeId: null, - interval: '1m', + schedule: { + interval: '1m', + }, actions: [], tags: [], }, @@ -674,7 +678,9 @@ export const AlertAdd = ({ refreshList }: Props) => { const interval = e.target.value !== '' ? parseInt(e.target.value, 10) : null; setAlertInterval(interval); - setAlertProperty('interval', `${e.target.value}${alertIntervalUnit}`); + setAlertProperty('schedule', { + interval: `${e.target.value}${alertIntervalUnit}`, + }); }} /> @@ -686,7 +692,9 @@ export const AlertAdd = ({ refreshList }: Props) => { options={getTimeOptions((alertInterval ? alertInterval : 1).toString())} onChange={(e: any) => { setAlertIntervalUnit(e.target.value); - setAlertProperty('interval', `${alertInterval}${e.target.value}`); + setAlertProperty('schedule', { + interval: `${alertInterval}${e.target.value}`, + }); }} /> diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.test.tsx index 8f8aef5a16bd5..9f77bfb3f8760 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -145,7 +145,7 @@ describe('alerts_list component with items', () => { tags: ['tag1'], enabled: true, alertTypeId: 'test_alert_type', - interval: '5d', + schedule: { interval: '5d' }, actions: [], params: { name: 'test alert type name' }, scheduledTaskId: null, @@ -162,7 +162,7 @@ describe('alerts_list component with items', () => { tags: ['tag1'], enabled: true, alertTypeId: 'test_alert_type', - interval: '5d', + schedule: { interval: '5d' }, actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }], params: { name: 'test alert type name' }, scheduledTaskId: null, @@ -348,7 +348,7 @@ describe('alerts_list with show only capability', () => { tags: ['tag1'], enabled: true, alertTypeId: 'test_alert_type', - interval: '5d', + schedule: { interval: '5d' }, actions: [], params: { name: 'test alert type name' }, scheduledTaskId: null, @@ -365,7 +365,7 @@ describe('alerts_list with show only capability', () => { tags: ['tag1'], enabled: true, alertTypeId: 'test_alert_type', - interval: '5d', + schedule: { interval: '5d' }, actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }], params: { name: 'test alert type name' }, scheduledTaskId: null, diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.tsx index 64f06521c0f9d..4b5e0d1948bfb 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/alerts_list.tsx @@ -31,6 +31,17 @@ import { hasDeleteAlertsCapability, hasSaveAlertsCapability } from '../../../lib const ENTER_KEY = 13; +interface AlertTypeState { + isLoading: boolean; + isInitialized: boolean; + data: AlertTypeIndex; +} +interface AlertState { + isLoading: boolean; + data: Alert[]; + totalItemCount: number; +} + export const AlertsList: React.FunctionComponent = () => { const { http, @@ -43,20 +54,24 @@ export const AlertsList: React.FunctionComponent = () => { const createAlertUiEnabled = injectedMetadata.getInjectedVar('createAlertUiEnabled'); const [actionTypes, setActionTypes] = useState([]); - const [alertTypesIndex, setAlertTypesIndex] = useState(undefined); - const [alerts, setAlerts] = useState([]); - const [data, setData] = useState([]); const [selectedIds, setSelectedIds] = useState([]); - const [isLoadingAlertTypes, setIsLoadingAlertTypes] = useState(false); - const [isLoadingAlerts, setIsLoadingAlerts] = useState(false); const [isPerformingAction, setIsPerformingAction] = useState(false); - const [totalItemCount, setTotalItemCount] = useState(0); const [page, setPage] = useState({ index: 0, size: 10 }); const [searchText, setSearchText] = useState(); const [inputText, setInputText] = useState(); const [typesFilter, setTypesFilter] = useState([]); const [actionTypesFilter, setActionTypesFilter] = useState([]); const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); + const [alertTypesState, setAlertTypesState] = useState({ + isLoading: false, + isInitialized: false, + data: {}, + }); + const [alertsState, setAlertsState] = useState({ + isLoading: false, + data: [], + totalItemCount: 0, + }); useEffect(() => { loadAlertsData(); @@ -66,13 +81,13 @@ export const AlertsList: React.FunctionComponent = () => { useEffect(() => { (async () => { try { - setIsLoadingAlertTypes(true); + setAlertTypesState({ ...alertTypesState, isLoading: true }); const alertTypes = await loadAlertTypes({ http }); const index: AlertTypeIndex = {}; for (const alertType of alertTypes) { index[alertType.id] = alertType; } - setAlertTypesIndex(index); + setAlertTypesState({ isLoading: false, data: index, isInitialized: true }); } catch (e) { toastNotifications.addDanger({ title: i18n.translate( @@ -80,8 +95,7 @@ export const AlertsList: React.FunctionComponent = () => { { defaultMessage: 'Unable to load alert types' } ), }); - } finally { - setIsLoadingAlertTypes(false); + setAlertTypesState({ ...alertTypesState, isLoading: false }); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -104,23 +118,8 @@ export const AlertsList: React.FunctionComponent = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - // Avoid flickering before alert types load - if (typeof alertTypesIndex === 'undefined') { - return; - } - const updatedData = alerts.map(alert => ({ - ...alert, - tagsText: alert.tags.join(', '), - alertType: alertTypesIndex[alert.alertTypeId] - ? alertTypesIndex[alert.alertTypeId].name - : alert.alertTypeId, - })); - setData(updatedData); - }, [alerts, alertTypesIndex]); - async function loadAlertsData() { - setIsLoadingAlerts(true); + setAlertsState({ ...alertsState, isLoading: true }); try { const alertsResponse = await loadAlerts({ http, @@ -129,8 +128,11 @@ export const AlertsList: React.FunctionComponent = () => { typesFilter, actionTypesFilter, }); - setAlerts(alertsResponse.data); - setTotalItemCount(alertsResponse.total); + setAlertsState({ + isLoading: false, + data: alertsResponse.data, + totalItemCount: alertsResponse.total, + }); } catch (e) { toastNotifications.addDanger({ title: i18n.translate( @@ -140,8 +142,7 @@ export const AlertsList: React.FunctionComponent = () => { } ), }); - } finally { - setIsLoadingAlerts(false); + setAlertsState({ ...alertsState, isLoading: false }); } } @@ -200,7 +201,7 @@ export const AlertsList: React.FunctionComponent = () => { setTypesFilter(types)} - options={Object.values(alertTypesIndex || {}) + options={Object.values(alertTypesState.data) .map(alertType => ({ value: alertType.id, name: alertType.name, @@ -241,7 +242,10 @@ export const AlertsList: React.FunctionComponent = () => { {selectedIds.length > 0 && canDelete && ( setIsPerformingAction(true)} onActionPerformed={() => { loadAlertsData(); @@ -282,8 +286,13 @@ export const AlertsList: React.FunctionComponent = () => { ({ @@ -296,7 +305,9 @@ export const AlertsList: React.FunctionComponent = () => { pagination={{ pageIndex: page.index, pageSize: page.size, - totalItemCount, + /* Don't display alert count until we have the alert types initialized */ + totalItemCount: + alertTypesState.isInitialized === false ? 0 : alertsState.totalItemCount, }} selection={ canDelete @@ -318,13 +329,14 @@ export const AlertsList: React.FunctionComponent = () => { ); }; -function pickFromData(data: AlertTableItem[], ids: string[]): AlertTableItem[] { - const result: AlertTableItem[] = []; - for (const id of ids) { - const match = data.find(item => item.id === id); - if (match) { - result.push(match); - } - } - return result; +function filterAlertsById(alerts: Alert[], ids: string[]): Alert[] { + return alerts.filter(alert => ids.includes(alert.id)); +} + +function convertAlertsToTableItems(alerts: Alert[], alertTypesIndex: AlertTypeIndex) { + return alerts.map(alert => ({ + ...alert, + tagsText: alert.tags.join(', '), + alertType: alertTypesIndex[alert.alertTypeId]?.name ?? alert.alertTypeId, + })); } diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/collapsed_item_actions.tsx b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/collapsed_item_actions.tsx index f063ab4f7cde3..dc6fb15f0f236 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/collapsed_item_actions.tsx +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/application/sections/alerts_list/components/collapsed_item_actions.tsx @@ -44,8 +44,6 @@ export const CollapsedItemActions: React.FunctionComponent = ({ const canDelete = hasDeleteAlertsCapability(capabilities.get()); const canSave = hasSaveAlertsCapability(capabilities.get()); - const [isEnabled, setIsEnabled] = useState(item.enabled); - const [isMuted, setIsMuted] = useState(item.muteAll); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const button = ( @@ -72,14 +70,12 @@ export const CollapsedItemActions: React.FunctionComponent = ({ { - if (isEnabled) { - setIsEnabled(false); + if (item.enabled) { await disableAlerts({ http, ids: [item.id] }); } else { - setIsEnabled(true); await enableAlerts({ http, ids: [item.id] }); } onAlertChanged(); @@ -95,15 +91,13 @@ export const CollapsedItemActions: React.FunctionComponent = ({ { - if (isMuted) { - setIsMuted(false); + if (item.muteAll) { await unmuteAlerts({ http, ids: [item.id] }); } else { - setIsMuted(true); await muteAlerts({ http, ids: [item.id] }); } onAlertChanged(); diff --git a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts index 4cf28d3bbd06f..7a8a0ead5e8c5 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts +++ b/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts @@ -5,6 +5,8 @@ */ import { capabilities } from 'ui/capabilities'; import { TypeRegistry } from './application/type_registry'; +import { SanitizedAlert as Alert } from '../../../alerting/common'; +export { SanitizedAlert as Alert, AlertAction } from '../../../alerting/common'; export type ActionTypeIndex = Record; export type AlertTypeIndex = Record; @@ -71,30 +73,6 @@ export interface AlertType { name: string; } -export interface AlertAction { - group: string; - id: string; - params: Record; -} - -export interface Alert { - id: string; - name: string; - tags: string[]; - enabled: boolean; - alertTypeId: string; - interval: string; - actions: AlertAction[]; - params: Record; - scheduledTaskId?: string; - createdBy: string | null; - updatedBy: string | null; - apiKeyOwner?: string; - throttle: string | null; - muteAll: boolean; - mutedInstanceIds: string[]; -} - export type AlertWithoutId = Omit; export interface AlertTableItem extends Alert { diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/query_context.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/query_context.ts index 03e228952f0e7..d97b7653402a3 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/query_context.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/monitor_states/search/query_context.ts @@ -55,19 +55,15 @@ export class QueryContext { return clauses; } - async dateRangeFilter(forceNoTimespan?: boolean): Promise { + async dateRangeFilter(): Promise { const timestampClause = { range: { '@timestamp': { gte: this.dateRangeStart, lte: this.dateRangeEnd } }, }; - if (forceNoTimespan === true || !(await this.hasTimespan())) { + if (!(await this.hasTimespan())) { return timestampClause; } - // @ts-ignore - const tsStart = DateMath.parse(this.dateRangeEnd).subtract(10, 'seconds'); - const tsEnd = DateMath.parse(this.dateRangeEnd)!; - return { bool: { filter: [ @@ -75,14 +71,7 @@ export class QueryContext { { bool: { should: [ - { - range: { - 'monitor.timespan': { - gte: tsStart.toISOString(), - lte: tsEnd.toISOString(), - }, - }, - }, + this.timespanClause(), { bool: { must_not: { exists: { field: 'monitor.timespan' } }, @@ -96,6 +85,29 @@ export class QueryContext { }; } + // timeRangeClause queries the given date range using the monitor timespan field + // which is a bit dicey since we may have data that predates this field existing, + // or we may have data that has it, but a slow ingestion process. + timespanClause() { + // We subtract 5m from the start to account for data that shows up late, + // for instance, with a large value for the elasticsearch refresh interval + // setting it lower can work very well on someone's laptop, but with real world + // latencies and slowdowns that's dangerous. Making this value larger makes things + // only slower, but only marginally so, and prevents people from seeing weird + // behavior. + const tsStart = DateMath.parse(this.dateRangeEnd)!.subtract(5, 'minutes'); + const tsEnd = DateMath.parse(this.dateRangeEnd)!; + + return { + range: { + 'monitor.timespan': { + gte: tsStart.toISOString(), + lte: tsEnd.toISOString(), + }, + }, + }; + } + async hasTimespan(): Promise { if (this.hasTimespanCache) { return this.hasTimespanCache; @@ -107,10 +119,7 @@ export class QueryContext { body: { query: { bool: { - filter: [ - await this.dateRangeFilter(true), - { exists: { field: 'monitor.timespan' } }, - ], + filter: [this.timespanClause()], }, }, }, diff --git a/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js b/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js index 55da11221bb10..ae929045cf570 100644 --- a/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js +++ b/x-pack/legacy/plugins/xpack_main/server/lib/__tests__/replace_injected_vars.js @@ -16,6 +16,7 @@ const buildRequest = (path = '/app/kibana') => { return { path, route: { settings: {} }, + headers: {}, raw: { req: { socket: {}, diff --git a/x-pack/plugins/actions/common/index.ts b/x-pack/plugins/actions/common/index.ts new file mode 100644 index 0000000000000..9f4141dbcae7d --- /dev/null +++ b/x-pack/plugins/actions/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './types'; diff --git a/x-pack/plugins/actions/common/types.ts b/x-pack/plugins/actions/common/types.ts new file mode 100644 index 0000000000000..784125b83859d --- /dev/null +++ b/x-pack/plugins/actions/common/types.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ActionType { + id: string; + name: string; + enabled: boolean; +} diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 9f494a60be02e..42e0ee9f523e1 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { RunContext, TaskManagerSetupContract } from '../../task_manager/server'; import { ExecutorError, TaskRunnerFactory } from './lib'; import { ActionType } from './types'; +import { ActionType as CommonActionType } from '../common'; import { ActionsConfigurationUtilities } from './actions_config'; interface ConstructorOptions { @@ -98,7 +99,7 @@ export class ActionTypeRegistry { /** * Returns a list of registered action types [{ id, name, enabled }] */ - public list() { + public list(): CommonActionType[] { return Array.from(this.actionTypes).map(([actionTypeId, actionType]) => ({ id: actionTypeId, name: actionType.name, diff --git a/x-pack/plugins/licensing/public/plugin.test.ts b/x-pack/plugins/licensing/public/plugin.test.ts index 01545ee8d48b4..f68e1dcfaf62b 100644 --- a/x-pack/plugins/licensing/public/plugin.test.ts +++ b/x-pack/plugins/licensing/public/plugin.test.ts @@ -64,10 +64,8 @@ describe('licensing plugin', () => { await refresh(); - expect(coreSetup.http.get.mock.calls[0][1]).toMatchObject({ - headers: { - 'kbn-system-api': 'true', - }, + expect(coreSetup.http.get.mock.calls[0][0]).toMatchObject({ + asSystemRequest: true, }); }); }); diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index 7d2498b0f7ff6..dab4c4048ce4c 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -132,10 +132,9 @@ export class LicensingPlugin implements Plugin { private fetchLicense = async (core: CoreSetup): Promise => { try { - const response = await core.http.get(this.infoEndpoint, { - headers: { - 'kbn-system-api': 'true', - }, + const response = await core.http.get({ + path: this.infoEndpoint, + asSystemRequest: true, }); return new License({ license: response.license, diff --git a/x-pack/plugins/security/public/authentication/authentication_service.ts b/x-pack/plugins/security/public/authentication/authentication_service.ts index 23c45c88e563a..2679bc20d6a7d 100644 --- a/x-pack/plugins/security/public/authentication/authentication_service.ts +++ b/x-pack/plugins/security/public/authentication/authentication_service.ts @@ -23,7 +23,7 @@ export class AuthenticationService { return { async getCurrentUser() { return (await http.get('/internal/security/me', { - headers: { 'kbn-system-api': true }, + asSystemRequest: true, })) as AuthenticatedUser; }, }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 2b3d7c811f6de..e183eae08d1e1 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -155,7 +155,7 @@ function getProps({ const { fatalErrors } = coreMock.createSetup(); const { http, docLinks, notifications } = coreMock.createStart(); - http.get.mockImplementation(async path => { + http.get.mockImplementation(async (path: any) => { if (path === '/api/features') { return buildFeatures(); } @@ -509,7 +509,7 @@ describe('', () => { it('can render if features are not available', async () => { const { http } = coreMock.createStart(); - http.get.mockImplementation(async path => { + http.get.mockImplementation(async (path: any) => { if (path === '/api/features') { const error = { response: { status: 404 } }; throw error; diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts index 7561161368405..21a4f5c9499ec 100644 --- a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts +++ b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts @@ -16,7 +16,7 @@ describe('RolesAPIClient', () => { await rolesAPIClient.saveRole({ role, spacesEnabled }); expect(httpMock.put).toHaveBeenCalledTimes(1); - return JSON.parse(httpMock.put.mock.calls[0][1]?.body as any); + return JSON.parse((httpMock.put.mock.calls[0] as any)[1]?.body as any); } describe('spaces disabled', () => { diff --git a/x-pack/plugins/security/public/session/session_timeout.tsx b/x-pack/plugins/security/public/session/session_timeout.tsx index e237201699b9b..bd6dbad7dbf14 100644 --- a/x-pack/plugins/security/public/session/session_timeout.tsx +++ b/x-pack/plugins/security/public/session/session_timeout.tsx @@ -104,9 +104,8 @@ export class SessionTimeout implements ISessionTimeout { */ private fetchSessionInfoAndResetTimers = async (extend = false) => { const method = extend ? 'POST' : 'GET'; - const headers = extend ? {} : { 'kbn-system-api': 'true' }; try { - const result = await this.http.fetch(SESSION_ROUTE, { method, headers }); + const result = await this.http.fetch(SESSION_ROUTE, { method, asSystemRequest: !extend }); this.handleSessionInfoAndResetTimers(result); diff --git a/x-pack/plugins/security/public/session/session_timeout_http_interceptor.test.ts b/x-pack/plugins/security/public/session/session_timeout_http_interceptor.test.ts index ffbd625590b15..427bdb04f9c61 100644 --- a/x-pack/plugins/security/public/session/session_timeout_http_interceptor.test.ts +++ b/x-pack/plugins/security/public/session/session_timeout_http_interceptor.test.ts @@ -58,7 +58,7 @@ describe('response', () => { http.intercept(interceptor); fetchMock.mock('*', 200); - await http.fetch('/foo-api', { headers: { 'kbn-system-api': 'true' } }); + await http.fetch('/foo-api', { asSystemRequest: true }); expect(sessionTimeoutMock.extend).not.toHaveBeenCalled(); }); @@ -99,9 +99,9 @@ describe('responseError', () => { http.intercept(interceptor); fetchMock.mock('*', 401); - await expect( - http.fetch('/foo-api', { headers: { 'kbn-system-api': 'true' } }) - ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized]`); + await expect(http.fetch('/foo-api', { asSystemRequest: true })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized]` + ); expect(sessionTimeoutMock.extend).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/security/public/session/session_timeout_http_interceptor.ts b/x-pack/plugins/security/public/session/session_timeout_http_interceptor.ts index 8a2251f3f7f7c..4033c04378a52 100644 --- a/x-pack/plugins/security/public/session/session_timeout_http_interceptor.ts +++ b/x-pack/plugins/security/public/session/session_timeout_http_interceptor.ts @@ -6,38 +6,34 @@ import { HttpInterceptor, - HttpErrorResponse, - IHttpResponse, + HttpInterceptorResponseError, + HttpResponse, IAnonymousPaths, } from 'src/core/public'; import { ISessionTimeout } from './session_timeout'; -const isSystemAPIRequest = (request: Request) => { - return request.headers.has('kbn-system-api'); -}; - export class SessionTimeoutHttpInterceptor implements HttpInterceptor { constructor(private sessionTimeout: ISessionTimeout, private anonymousPaths: IAnonymousPaths) {} - response(httpResponse: IHttpResponse) { + response(httpResponse: HttpResponse) { if (this.anonymousPaths.isAnonymous(window.location.pathname)) { return; } - if (isSystemAPIRequest(httpResponse.request)) { + if (httpResponse.fetchOptions.asSystemRequest) { return; } this.sessionTimeout.extend(httpResponse.request.url); } - responseError(httpErrorResponse: HttpErrorResponse) { + responseError(httpErrorResponse: HttpInterceptorResponseError) { if (this.anonymousPaths.isAnonymous(window.location.pathname)) { return; } - if (isSystemAPIRequest(httpErrorResponse.request)) { + if (httpErrorResponse.fetchOptions.asSystemRequest) { return; } diff --git a/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.ts b/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.ts index a0ef2fdb86b47..6b5eadcab7441 100644 --- a/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.ts +++ b/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.ts @@ -6,7 +6,7 @@ import { HttpInterceptor, - HttpErrorResponse, + HttpInterceptorResponseError, IHttpInterceptController, IAnonymousPaths, } from 'src/core/public'; @@ -16,7 +16,10 @@ import { SessionExpired } from './session_expired'; export class UnauthorizedResponseHttpInterceptor implements HttpInterceptor { constructor(private sessionExpired: SessionExpired, private anonymousPaths: IAnonymousPaths) {} - responseError(httpErrorResponse: HttpErrorResponse, controller: IHttpInterceptController) { + responseError( + httpErrorResponse: HttpInterceptorResponseError, + controller: IHttpInterceptController + ) { if (this.anonymousPaths.isAnonymous(window.location.pathname)) { return; } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 07deb1870aa12..d1563ff4a0844 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1103,307 +1103,6 @@ "kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "デフォルトの WMS プロパティ", "kbn.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。", "kbn.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする", - "home.tutorials.common.auditbeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.auditbeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.auditbeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.auditbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.auditbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatCloudInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.auditbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.auditbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.auditbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.auditbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.auditbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.auditbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.auditbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.auditbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.auditbeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.auditbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", - "home.tutorials.common.auditbeatInstructions.install.debTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.auditbeatInstructions.install.debTitle": "Auditbeat のダウンロードとインストール", - "home.tutorials.common.auditbeatInstructions.install.osxTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.auditbeatInstructions.install.osxTitle": "Auditbeat のダウンロードとインストール", - "home.tutorials.common.auditbeatInstructions.install.rpmTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", - "home.tutorials.common.auditbeatInstructions.install.rpmTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.auditbeatInstructions.install.rpmTitle": "Auditbeat のダウンロードとインストール", - "home.tutorials.common.auditbeatInstructions.install.windowsTextPost": "{auditbeatPath} ファイルの {propertyName} を Elasticsearch のインストールに設定します。", - "home.tutorials.common.auditbeatInstructions.install.windowsTextPre": "Auditbeat は初めてですか?[入門ガイド]({guideLinkUrl}) をご覧ください。\n 1.[ダウンロード]({auditbeatLinkUrl}) ページから Auditbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Auditbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Auditbeat を Windows サービスとしてインストールします。", - "home.tutorials.common.auditbeatInstructions.install.windowsTitle": "Auditbeat のダウンロードとインストール", - "home.tutorials.common.auditbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.auditbeatInstructions.start.debTitle": "Auditbeat を起動", - "home.tutorials.common.auditbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.auditbeatInstructions.start.osxTitle": "Auditbeat を起動", - "home.tutorials.common.auditbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.auditbeatInstructions.start.rpmTitle": "Auditbeat を起動", - "home.tutorials.common.auditbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.auditbeatInstructions.start.windowsTitle": "Auditbeat を起動", - "home.tutorials.common.auditbeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.auditbeatStatusCheck.errorText": "まだデータを受信していません", - "home.tutorials.common.auditbeatStatusCheck.successText": "データを受信しました", - "home.tutorials.common.auditbeatStatusCheck.text": "Auditbeat からデータを受け取ったことを確認してください。", - "home.tutorials.common.auditbeatStatusCheck.title": "ステータス", - "home.tutorials.common.filebeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.filebeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.filebeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.filebeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.filebeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatCloudInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.filebeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.filebeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatCloudInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.filebeatEnableInstructions.debTextPost": "「/etc/filebeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", - "home.tutorials.common.filebeatEnableInstructions.debTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.filebeatEnableInstructions.osxTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", - "home.tutorials.common.filebeatEnableInstructions.osxTextPre": "インストールディレクトリから次のファイルを実行します:", - "home.tutorials.common.filebeatEnableInstructions.osxTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.filebeatEnableInstructions.rpmTextPost": "「/etc/filebeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", - "home.tutorials.common.filebeatEnableInstructions.rpmTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.filebeatEnableInstructions.windowsTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", - "home.tutorials.common.filebeatEnableInstructions.windowsTextPre": "「{path}」フォルダから次のファイルを実行します:", - "home.tutorials.common.filebeatEnableInstructions.windowsTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.filebeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.filebeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.filebeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.filebeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.filebeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.filebeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.filebeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.filebeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.filebeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.filebeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", - "home.tutorials.common.filebeatInstructions.install.debTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.filebeatInstructions.install.debTitle": "Filebeat のダウンロードとインストール", - "home.tutorials.common.filebeatInstructions.install.osxTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.filebeatInstructions.install.osxTitle": "Filebeat のダウンロードとインストール", - "home.tutorials.common.filebeatInstructions.install.rpmTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", - "home.tutorials.common.filebeatInstructions.install.rpmTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", - "home.tutorials.common.filebeatInstructions.install.rpmTitle": "Filebeat のダウンロードとインストール", - "home.tutorials.common.filebeatInstructions.install.windowsTextPost": "{filebeatPath} ファイルの {propertyName} を Elasticsearch のインストールに設定します。", - "home.tutorials.common.filebeatInstructions.install.windowsTextPre": "Filebeat は初めてですか?[入門ガイド]({guideLinkUrl}) をご覧ください。\n 1.[ダウンロード]({filebeatLinkUrl}) ページから Filebeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Filebeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Filebeat を Windows サービスとしてインストールします。", - "home.tutorials.common.filebeatInstructions.install.windowsTitle": "Filebeat のダウンロードとインストール", - "home.tutorials.common.filebeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.filebeatInstructions.start.debTitle": "Filebeat を起動します", - "home.tutorials.common.filebeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.filebeatInstructions.start.osxTitle": "Filebeat を起動します", - "home.tutorials.common.filebeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.filebeatInstructions.start.rpmTitle": "Filebeat を起動します", - "home.tutorials.common.filebeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.filebeatInstructions.start.windowsTitle": "Filebeat を起動します", - "home.tutorials.common.filebeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.filebeatStatusCheck.errorText": "モジュールからまだデータを受け取っていません", - "home.tutorials.common.filebeatStatusCheck.successText": "このモジュールからデータを受け取りました", - "home.tutorials.common.filebeatStatusCheck.text": "Filebeat の「{moduleName}」モジュールからデータを受け取ったことを確認してください。", - "home.tutorials.common.filebeatStatusCheck.title": "モジュールステータス", - "home.tutorials.common.functionbeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.functionbeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.functionbeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.functionbeatAWSInstructions.textPost": "「」と「」がアカウント 認証情報で、「us-east-1」が希望の地域です。", - "home.tutorials.common.functionbeatAWSInstructions.textPre": "環境で AWS アカウント認証情報を設定します。", - "home.tutorials.common.functionbeatAWSInstructions.title": "AWS 認証情報の設定", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTextPost": "「」が投入するロググループの名前で、「」が Functionbeat デプロイのステージングに使用されるが有効な S3 バケット名です。", - "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTitle": "Cloudwatch ロググループの構成", - "home.tutorials.common.functionbeatEnableOnPremInstructionsOSXLinux.textPre": "「functionbeat.yml」ファイルで設定を変更します。", - "home.tutorials.common.functionbeatEnableOnPremInstructionsWindows.textPre": "{path} ファイルで設定を変更します。", - "home.tutorials.common.functionbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.functionbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.functionbeatInstructions.config.osxTitle": "Elastic クラスターの構成", - "home.tutorials.common.functionbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.functionbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.functionbeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.functionbeatInstructions.deploy.osxTextPre": "これにより Functionbeat が Lambda 関数としてインストールされます「setup」コマンドで Elasticsearch の構成を確認し、Kibana インデックスパターンを読み込みます。通常このコマンドを省いても大丈夫です。", - "home.tutorials.common.functionbeatInstructions.deploy.osxTitle": "Functionbeat を AWS Lambda にデプロイ", - "home.tutorials.common.functionbeatInstructions.deploy.windowsTextPre": "これにより Functionbeat が Lambda 関数としてインストールされます「setup」コマンドで Elasticsearch の構成を確認し、Kibana インデックスパターンを読み込みます。通常このコマンドを省いても大丈夫です。", - "home.tutorials.common.functionbeatInstructions.deploy.windowsTitle": "Functionbeat を AWS Lambda にデプロイ", - "home.tutorials.common.functionbeatInstructions.install.linuxTextPre": "Functionbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.functionbeatInstructions.install.linuxTitle": "Functionbeat のダウンロードとインストール", - "home.tutorials.common.functionbeatInstructions.install.osxTextPre": "Functionbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.functionbeatInstructions.install.osxTitle": "Functionbeat のダウンロードとインストール", - "home.tutorials.common.functionbeatInstructions.install.windowsTextPre": "Functionbeat は初めてですか?[入門ガイド]({functionbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Functionbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.{directoryName} ディレクトリの名前を「Functionbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトから、Functionbeat ディレクトリに移動します:", - "home.tutorials.common.functionbeatInstructions.install.windowsTitle": "Functionbeat のダウンロードとインストール", - "home.tutorials.common.functionbeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.functionbeatStatusCheck.errorText": "Functionbeat からまだデータを受け取っていません", - "home.tutorials.common.functionbeatStatusCheck.successText": "Functionbeat からデータを受け取りました", - "home.tutorials.common.functionbeatStatusCheck.text": "Functionbeat からデータを受け取ったことを確認してください。", - "home.tutorials.common.functionbeatStatusCheck.title": "Functionbeat ステータス", - "home.tutorials.common.heartbeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.heartbeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.heartbeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.heartbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.heartbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatCloudInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.heartbeatEnableCloudInstructions.debTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTextPost": "Heartbeat のモニターの設定の詳細は、[Heartbeat 設定ドキュメント]({configureLink}) をご覧ください。", - "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTitle": "設定の変更 - モニターの追加", - "home.tutorials.common.heartbeatEnableCloudInstructions.osxTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableCloudInstructions.rpmTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableCloudInstructions.windowsTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.debTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTextPost": "{hostTemplate} は監視対象の URL です。Heartbeat のモニターの設定の詳細は、[Heartbeat 設定ドキュメント]({configureLink}) をご覧ください。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTitle": "設定の変更 - モニターの追加", - "home.tutorials.common.heartbeatEnableOnPremInstructions.osxTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.rpmTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.windowsTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", - "home.tutorials.common.heartbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.heartbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.heartbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.heartbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.heartbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.heartbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.heartbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.heartbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.heartbeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.heartbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({link}) をご覧ください。", - "home.tutorials.common.heartbeatInstructions.install.debTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.heartbeatInstructions.install.debTitle": "Heartbeat のダウンロードとインストール", - "home.tutorials.common.heartbeatInstructions.install.osxTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.heartbeatInstructions.install.osxTitle": "Heartbeat のダウンロードとインストール", - "home.tutorials.common.heartbeatInstructions.install.rpmTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.heartbeatInstructions.install.rpmTitle": "Heartbeat のダウンロードとインストール", - "home.tutorials.common.heartbeatInstructions.install.windowsTextPre": "Heartbeat は初めてですか?[入門ガイド]({heartbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Heartbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Heartbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Heartbeat を Windows サービスとしてインストールします。", - "home.tutorials.common.heartbeatInstructions.install.windowsTitle": "Heartbeat のダウンロードとインストール", - "home.tutorials.common.heartbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", - "home.tutorials.common.heartbeatInstructions.start.debTitle": "Heartbeat を起動します", - "home.tutorials.common.heartbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", - "home.tutorials.common.heartbeatInstructions.start.osxTitle": "Heartbeat を起動します", - "home.tutorials.common.heartbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", - "home.tutorials.common.heartbeatInstructions.start.rpmTitle": "Heartbeat を起動します", - "home.tutorials.common.heartbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", - "home.tutorials.common.heartbeatInstructions.start.windowsTitle": "Heartbeat を起動します", - "home.tutorials.common.heartbeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.heartbeatStatusCheck.errorText": "Heartbeat からまだデータを受け取っていません", - "home.tutorials.common.heartbeatStatusCheck.successText": "Heartbeat からデータを受け取りました", - "home.tutorials.common.heartbeatStatusCheck.text": "Heartbeat からデータを受け取ったことを確認してください。", - "home.tutorials.common.heartbeatStatusCheck.title": "Heartbeat のステータス", - "home.tutorials.common.logstashInstructions.install.java.osxTextPre": "[こちら]({link}) のインストール手順に従ってください。", - "home.tutorials.common.logstashInstructions.install.java.osxTitle": "Java Runtime Environment のダウンロードとインストール", - "home.tutorials.common.logstashInstructions.install.java.windowsTextPre": "[こちら]({link}) のインストール手順に従ってください。", - "home.tutorials.common.logstashInstructions.install.java.windowsTitle": "Java Runtime Environment のダウンロードとインストール", - "home.tutorials.common.logstashInstructions.install.logstash.osxTextPre": "Logstash は初めてですか? [入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.logstashInstructions.install.logstash.osxTitle": "Logstash のダウンロードとインストール", - "home.tutorials.common.logstashInstructions.install.logstash.windowsTextPre": "Logstash は初めてですか? [入門ガイド]({logstashLink}) をご覧ください。\n 1. Logstash Windows zip ファイルを [ダウンロード]({elasticLink}) します。\n 2.zip ファイルのコンテンツを展開します。", - "home.tutorials.common.logstashInstructions.install.logstash.windowsTitle": "Logstash のダウンロードとインストール", - "home.tutorials.common.metricbeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.metricbeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.metricbeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.metricbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.metricbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatCloudInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.metricbeatEnableInstructions.debTextPost": "「/etc/metricbeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", - "home.tutorials.common.metricbeatEnableInstructions.debTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.metricbeatEnableInstructions.osxTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", - "home.tutorials.common.metricbeatEnableInstructions.osxTextPre": "インストールディレクトリから次のファイルを実行します:", - "home.tutorials.common.metricbeatEnableInstructions.osxTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.metricbeatEnableInstructions.rpmTextPost": "「/etc/metricbeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", - "home.tutorials.common.metricbeatEnableInstructions.rpmTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.metricbeatEnableInstructions.windowsTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", - "home.tutorials.common.metricbeatEnableInstructions.windowsTextPre": "「{path}」フォルダから次のファイルを実行します:", - "home.tutorials.common.metricbeatEnableInstructions.windowsTitle": "{moduleName} モジュールを有効にし構成します", - "home.tutorials.common.metricbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.metricbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatInstructions.config.debTitle": "構成の変更", - "home.tutorials.common.metricbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.metricbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatInstructions.config.osxTitle": "構成の変更", - "home.tutorials.common.metricbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.metricbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatInstructions.config.rpmTitle": "構成の変更", - "home.tutorials.common.metricbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.metricbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.metricbeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.metricbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({link}) をご覧ください。", - "home.tutorials.common.metricbeatInstructions.install.debTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.metricbeatInstructions.install.debTitle": "Metricbeat のダウンロードとインストール", - "home.tutorials.common.metricbeatInstructions.install.osxTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.metricbeatInstructions.install.osxTitle": "Metricbeat のダウンロードとインストール", - "home.tutorials.common.metricbeatInstructions.install.rpmTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", - "home.tutorials.common.metricbeatInstructions.install.rpmTitle": "Metricbeat のダウンロードとインストール", - "home.tutorials.common.metricbeatInstructions.install.windowsTextPost": "{path} ファイルの「output.elasticsearch」を Elasticsearch のインストールに設定します。", - "home.tutorials.common.metricbeatInstructions.install.windowsTextPre": "Metricbeat は初めてですか?[入門ガイド]({metricbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Metricbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Metricbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Metricbeat を Windows サービスとしてインストールします。", - "home.tutorials.common.metricbeatInstructions.install.windowsTitle": "Metricbeat のダウンロードとインストール", - "home.tutorials.common.metricbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.metricbeatInstructions.start.debTitle": "Metricbeat を起動します", - "home.tutorials.common.metricbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.metricbeatInstructions.start.osxTitle": "Metricbeat を起動します", - "home.tutorials.common.metricbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.metricbeatInstructions.start.rpmTitle": "Metricbeat を起動します", - "home.tutorials.common.metricbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.metricbeatInstructions.start.windowsTitle": "Metricbeat を起動します", - "home.tutorials.common.metricbeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.metricbeatStatusCheck.errorText": "モジュールからまだデータを受け取っていません", - "home.tutorials.common.metricbeatStatusCheck.successText": "このモジュールからデータを受け取りました", - "home.tutorials.common.metricbeatStatusCheck.text": "Metricbeat の「{moduleName}」モジュールからデータを受け取ったことを確認してください", - "home.tutorials.common.metricbeatStatusCheck.title": "モジュールステータス", - "home.tutorials.common.premCloudInstructions.option1.textPre": "[Elastic Cloud] ({link}) にアクセスします。アカウントをお持ちでない場合は新規登録してください。14 日間の無料トライアルがご利用いただけます。\n\nElastic Cloud コンソールにログインします\n\nElastic Cloud コンソールで次の手順を実行し、クラスターを作成します。\n 1.「デプロイを作成」、「デプロイ名」の順にクリックします\n 2.必要に応じて他のデプロイオプションを変更します (デフォルトも使い始めるのに有効です)\n 3.「デプロイを作成」をクリックします\n 4.デプロイの作成が完了するまで待ちます\n 5.新規クラウド Kibana インスタンスにアクセスし、Kibana ホームの手順に従います。", - "home.tutorials.common.premCloudInstructions.option1.title": "オプション 1:Elastic Cloud でお試しください", - "home.tutorials.common.premCloudInstructions.option2.textPre": "この Kibana インスタンスをマネージド Elasticsearch インスタンスに対して実行している場合は、手動セットアップを行います。.\n\n「Elasticsearch」エンドポイントを {urlTemplate} として保存し、クラスターの「パスワード」を {passwordTemplate} として保存します。", - "home.tutorials.common.premCloudInstructions.option2.title": "オプション 2:Kibana を Cloud インスタンスに接続", - "home.tutorials.common.winlogbeat.cloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.winlogbeat.premCloudInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.winlogbeat.premInstructions.gettingStarted.title": "はじめに", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.winlogbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", - "home.tutorials.common.winlogbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", - "home.tutorials.common.winlogbeatInstructions.config.windowsTitle": "構成の変更", - "home.tutorials.common.winlogbeatInstructions.install.windowsTextPost": "{path} ファイルの「output.elasticsearch」を Elasticsearch のインストールに設定します。", - "home.tutorials.common.winlogbeatInstructions.install.windowsTextPre": "Winlogbeat は初めてですか?[入門ガイド]({winlogbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Winlogbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.{directoryName} ディレクトリの名前を「Winlogbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Winlogbeat を Windows サービスとしてインストールします。", - "home.tutorials.common.winlogbeatInstructions.install.windowsTitle": "Winlogbeat のダウンロードとインストール", - "home.tutorials.common.winlogbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", - "home.tutorials.common.winlogbeatInstructions.start.windowsTitle": "Winlogbeat を起動", - "home.tutorials.common.winlogbeatStatusCheck.buttonLabel": "データを確認してください", - "home.tutorials.common.winlogbeatStatusCheck.errorText": "まだデータを受信していません", - "home.tutorials.common.winlogbeatStatusCheck.successText": "データを受信しました", - "home.tutorials.common.winlogbeatStatusCheck.text": "Winlogbeat からデータを受け取ったことを確認してください。", - "home.tutorials.common.winlogbeatStatusCheck.title": "モジュールステータス", "kbn.context.breadcrumb": "{indexPatternTitle}#{docId} のコンテキスト", "kbn.context.failedToLoadAnchorDocumentDescription": "別ののドキュメントの読み込みに失敗しました", "kbn.context.failedToLoadAnchorDocumentErrorDescription": "別のドキュメントの読み込みに失敗しました。", @@ -2050,6 +1749,368 @@ "kbn.management.settings.searchBarAriaLabel": "高度な設定を検索", "kbn.management.settings.sectionLabel": "高度な設定", "kbn.managementTitle": "管理", + "kbn.settings.advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "{query} を検索しました。{sectionLenght, plural, one {# セクション} other {# セクション}}に{optionLenght, plural, one {# オプション} other { # オプション}}があります。", + "kbn.topNavMenu.openInspectorButtonLabel": "検査", + "kbn.topNavMenu.refreshButtonLabel": "更新", + "kbn.topNavMenu.saveVisualizationButtonLabel": "保存", + "kbn.topNavMenu.shareVisualizationButtonLabel": "共有", + "kbn.visualize.badge.readOnly.text": "読み込み専用", + "kbn.visualize.badge.readOnly.tooltip": "ビジュアライゼーションを保存できません", + "kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPattern または savedSearchId が必要です", + "kbn.visualize.editor.createBreadcrumb": "作成", + "kbn.visualize.experimentalVisInfoText": "このビジュアライゼーションは実験的なものです。", + "kbn.visualize.linkedToSearch.unlinkButtonTooltip": "保存された検索からリンクを解除するにはダブルクリックします", + "kbn.visualize.linkedToSearch.unlinkSuccessNotificationText": "保存された検索「{searchTitle}」からリンクが解除されました", + "kbn.visualize.linkedToSearchInfoText": "保存された検索にリンクされています", + "kbn.visualize.listing.betaTitle": "ベータ", + "kbn.visualize.listing.betaTooltip": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "kbn.visualize.listing.breadcrumb": "可視化", + "kbn.visualize.listing.createNew.createButtonLabel": "新規ビジュアライゼーションを追加", + "kbn.visualize.listing.createNew.description": "データに基づき異なるビジュアライゼーションを作成できます。", + "kbn.visualize.listing.createNew.title": "最初のビジュアライゼーションの作成", + "kbn.visualize.listing.experimentalTitle": "実験的", + "kbn.visualize.listing.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", + "kbn.visualize.listing.noItemsMessage": "ビジュアライゼーションがないようです。", + "kbn.visualize.listing.table.entityName": "ビジュアライゼーション", + "kbn.visualize.listing.table.entityNamePlural": "ビジュアライゼーション", + "kbn.visualize.listing.table.listTitle": "ビジュアライゼーション", + "kbn.visualize.listing.table.titleColumnName": "タイトル", + "kbn.visualize.listing.table.typeColumnName": "タイプ", + "kbn.visualize.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "kbn.visualize.newVisWizard.betaTitle": "ベータ", + "kbn.visualize.newVisWizard.chooseSourceTitle": "ソースの選択", + "kbn.visualize.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。", + "kbn.visualize.newVisWizard.experimentalTitle": "実験的", + "kbn.visualize.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", + "kbn.visualize.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング", + "kbn.visualize.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。", + "kbn.visualize.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモダルを閉じます。Tab キーを押して次に進みます。", + "kbn.visualize.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", + "kbn.visualize.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました", + "kbn.visualize.newVisWizard.searchSelection.notFoundLabel": "一致するインデックスまたは保存された検索が見つかりませんでした。", + "kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", + "kbn.visualize.newVisWizard.searchSelection.savedObjectType.search": "保存された検索", + "kbn.visualize.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください", + "kbn.visualize.newVisWizard.title": "新規ビジュアライゼーション", + "kbn.visualize.newVisWizard.visTypeAliasDescription": "Visualize外でKibanaアプリケーションを開きます。", + "kbn.visualize.newVisWizard.visTypeAliasTitle": "Kibanaアプリケーション", + "kbn.visualize.pageHeading": "{chartName} {chartType} ビジュアライゼーション", + "kbn.visualize.saveDialog.saveAndAddToDashboardButtonLabel": "保存してダッシュボードに追加", + "kbn.visualize.topNavMenu.openInspectorButtonAriaLabel": "ビジュアライゼーションのインスペクターを開く", + "kbn.visualize.topNavMenu.openInspectorDisabledButtonTooltip": "このビジュアライゼーションはインスペクターをサポートしていません。", + "kbn.visualize.topNavMenu.refreshButtonAriaLabel": "更新", + "kbn.visualize.topNavMenu.saveVisualization.failureNotificationText": "「{visTitle}」の保存中にエラーが発生しました", + "kbn.visualize.topNavMenu.saveVisualization.successNotificationText": "「{visTitle}」が保存されました", + "kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel": "ビジュアライゼーションを保存", + "kbn.visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "保存する前に変更を適用または破棄", + "kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel": "ビジュアライゼーションを共有", + "kbn.visualize.visualizeDescription": "ビジュアライゼーションを作成して Elasticsearch インデックスに保存されたデータを集約します。", + "kbn.visualize.visualizeListingBreadcrumbsTitle": "可視化", + "kbn.visualize.visualizeListingDeleteErrorTitle": "ビジュアライゼーションの削除中にエラーが発生", + "kbn.visualize.wizard.step1Breadcrumb": "作成", + "kbn.visualize.wizard.step2Breadcrumb": "作成", + "kbn.visualizeTitle": "可視化", + "home.tutorials.common.auditbeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.auditbeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.auditbeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.auditbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.auditbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatCloudInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.auditbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.auditbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.auditbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.auditbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.auditbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.auditbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.auditbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.auditbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.auditbeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.auditbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", + "home.tutorials.common.auditbeatInstructions.install.debTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.auditbeatInstructions.install.debTitle": "Auditbeat のダウンロードとインストール", + "home.tutorials.common.auditbeatInstructions.install.osxTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.auditbeatInstructions.install.osxTitle": "Auditbeat のダウンロードとインストール", + "home.tutorials.common.auditbeatInstructions.install.rpmTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", + "home.tutorials.common.auditbeatInstructions.install.rpmTextPre": "Auditbeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.auditbeatInstructions.install.rpmTitle": "Auditbeat のダウンロードとインストール", + "home.tutorials.common.auditbeatInstructions.install.windowsTextPost": "{auditbeatPath} ファイルの {propertyName} を Elasticsearch のインストールに設定します。", + "home.tutorials.common.auditbeatInstructions.install.windowsTextPre": "Auditbeat は初めてですか?[入門ガイド]({guideLinkUrl}) をご覧ください。\n 1.[ダウンロード]({auditbeatLinkUrl}) ページから Auditbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Auditbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Auditbeat を Windows サービスとしてインストールします。", + "home.tutorials.common.auditbeatInstructions.install.windowsTitle": "Auditbeat のダウンロードとインストール", + "home.tutorials.common.auditbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.auditbeatInstructions.start.debTitle": "Auditbeat を起動", + "home.tutorials.common.auditbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.auditbeatInstructions.start.osxTitle": "Auditbeat を起動", + "home.tutorials.common.auditbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.auditbeatInstructions.start.rpmTitle": "Auditbeat を起動", + "home.tutorials.common.auditbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.auditbeatInstructions.start.windowsTitle": "Auditbeat を起動", + "home.tutorials.common.auditbeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.auditbeatStatusCheck.errorText": "まだデータを受信していません", + "home.tutorials.common.auditbeatStatusCheck.successText": "データを受信しました", + "home.tutorials.common.auditbeatStatusCheck.text": "Auditbeat からデータを受け取ったことを確認してください。", + "home.tutorials.common.auditbeatStatusCheck.title": "ステータス", + "home.tutorials.common.filebeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.filebeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.filebeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.filebeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.filebeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatCloudInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.filebeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.filebeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatCloudInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.filebeatEnableInstructions.debTextPost": "「/etc/filebeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", + "home.tutorials.common.filebeatEnableInstructions.debTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.filebeatEnableInstructions.osxTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", + "home.tutorials.common.filebeatEnableInstructions.osxTextPre": "インストールディレクトリから次のファイルを実行します:", + "home.tutorials.common.filebeatEnableInstructions.osxTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.filebeatEnableInstructions.rpmTextPost": "「/etc/filebeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", + "home.tutorials.common.filebeatEnableInstructions.rpmTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.filebeatEnableInstructions.windowsTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", + "home.tutorials.common.filebeatEnableInstructions.windowsTextPre": "「{path}」フォルダから次のファイルを実行します:", + "home.tutorials.common.filebeatEnableInstructions.windowsTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.filebeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.filebeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.filebeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.filebeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.filebeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.filebeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.filebeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.filebeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.filebeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.filebeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", + "home.tutorials.common.filebeatInstructions.install.debTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.filebeatInstructions.install.debTitle": "Filebeat のダウンロードとインストール", + "home.tutorials.common.filebeatInstructions.install.osxTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.filebeatInstructions.install.osxTitle": "Filebeat のダウンロードとインストール", + "home.tutorials.common.filebeatInstructions.install.rpmTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({linkUrl}) をご覧ください。", + "home.tutorials.common.filebeatInstructions.install.rpmTextPre": "Filebeat は初めてですか?[入門ガイド]({linkUrl}) をご覧ください。", + "home.tutorials.common.filebeatInstructions.install.rpmTitle": "Filebeat のダウンロードとインストール", + "home.tutorials.common.filebeatInstructions.install.windowsTextPost": "{filebeatPath} ファイルの {propertyName} を Elasticsearch のインストールに設定します。", + "home.tutorials.common.filebeatInstructions.install.windowsTextPre": "Filebeat は初めてですか?[入門ガイド]({guideLinkUrl}) をご覧ください。\n 1.[ダウンロード]({filebeatLinkUrl}) ページから Filebeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Filebeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Filebeat を Windows サービスとしてインストールします。", + "home.tutorials.common.filebeatInstructions.install.windowsTitle": "Filebeat のダウンロードとインストール", + "home.tutorials.common.filebeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.filebeatInstructions.start.debTitle": "Filebeat を起動します", + "home.tutorials.common.filebeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.filebeatInstructions.start.osxTitle": "Filebeat を起動します", + "home.tutorials.common.filebeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.filebeatInstructions.start.rpmTitle": "Filebeat を起動します", + "home.tutorials.common.filebeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.filebeatInstructions.start.windowsTitle": "Filebeat を起動します", + "home.tutorials.common.filebeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.filebeatStatusCheck.errorText": "モジュールからまだデータを受け取っていません", + "home.tutorials.common.filebeatStatusCheck.successText": "このモジュールからデータを受け取りました", + "home.tutorials.common.filebeatStatusCheck.text": "Filebeat の「{moduleName}」モジュールからデータを受け取ったことを確認してください。", + "home.tutorials.common.filebeatStatusCheck.title": "モジュールステータス", + "home.tutorials.common.functionbeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.functionbeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.functionbeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.functionbeatAWSInstructions.textPost": "「」と「」がアカウント 認証情報で、「us-east-1」が希望の地域です。", + "home.tutorials.common.functionbeatAWSInstructions.textPre": "環境で AWS アカウント認証情報を設定します。", + "home.tutorials.common.functionbeatAWSInstructions.title": "AWS 認証情報の設定", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTextPost": "「」が投入するロググループの名前で、「」が Functionbeat デプロイのステージングに使用されるが有効な S3 バケット名です。", + "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTitle": "Cloudwatch ロググループの構成", + "home.tutorials.common.functionbeatEnableOnPremInstructionsOSXLinux.textPre": "「functionbeat.yml」ファイルで設定を変更します。", + "home.tutorials.common.functionbeatEnableOnPremInstructionsWindows.textPre": "{path} ファイルで設定を変更します。", + "home.tutorials.common.functionbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.functionbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.functionbeatInstructions.config.osxTitle": "Elastic クラスターの構成", + "home.tutorials.common.functionbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.functionbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.functionbeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.functionbeatInstructions.deploy.osxTextPre": "これにより Functionbeat が Lambda 関数としてインストールされます「setup」コマンドで Elasticsearch の構成を確認し、Kibana インデックスパターンを読み込みます。通常このコマンドを省いても大丈夫です。", + "home.tutorials.common.functionbeatInstructions.deploy.osxTitle": "Functionbeat を AWS Lambda にデプロイ", + "home.tutorials.common.functionbeatInstructions.deploy.windowsTextPre": "これにより Functionbeat が Lambda 関数としてインストールされます「setup」コマンドで Elasticsearch の構成を確認し、Kibana インデックスパターンを読み込みます。通常このコマンドを省いても大丈夫です。", + "home.tutorials.common.functionbeatInstructions.deploy.windowsTitle": "Functionbeat を AWS Lambda にデプロイ", + "home.tutorials.common.functionbeatInstructions.install.linuxTextPre": "Functionbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.functionbeatInstructions.install.linuxTitle": "Functionbeat のダウンロードとインストール", + "home.tutorials.common.functionbeatInstructions.install.osxTextPre": "Functionbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.functionbeatInstructions.install.osxTitle": "Functionbeat のダウンロードとインストール", + "home.tutorials.common.functionbeatInstructions.install.windowsTextPre": "Functionbeat は初めてですか?[入門ガイド]({functionbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Functionbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.{directoryName} ディレクトリの名前を「Functionbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトから、Functionbeat ディレクトリに移動します:", + "home.tutorials.common.functionbeatInstructions.install.windowsTitle": "Functionbeat のダウンロードとインストール", + "home.tutorials.common.functionbeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.functionbeatStatusCheck.errorText": "Functionbeat からまだデータを受け取っていません", + "home.tutorials.common.functionbeatStatusCheck.successText": "Functionbeat からデータを受け取りました", + "home.tutorials.common.functionbeatStatusCheck.text": "Functionbeat からデータを受け取ったことを確認してください。", + "home.tutorials.common.functionbeatStatusCheck.title": "Functionbeat ステータス", + "home.tutorials.common.heartbeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.heartbeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.heartbeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.heartbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.heartbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatCloudInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.heartbeatEnableCloudInstructions.debTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTextPost": "Heartbeat のモニターの設定の詳細は、[Heartbeat 設定ドキュメント]({configureLink}) をご覧ください。", + "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTitle": "設定の変更 - モニターの追加", + "home.tutorials.common.heartbeatEnableCloudInstructions.osxTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableCloudInstructions.rpmTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableCloudInstructions.windowsTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.debTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTextPost": "{hostTemplate} は監視対象の URL です。Heartbeat のモニターの設定の詳細は、[Heartbeat 設定ドキュメント]({configureLink}) をご覧ください。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTitle": "設定の変更 - モニターの追加", + "home.tutorials.common.heartbeatEnableOnPremInstructions.osxTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.rpmTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.windowsTextPre": "「heartbeat.yml」ファイルの「heartbeat.monitors」設定を変更します。", + "home.tutorials.common.heartbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.heartbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.heartbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.heartbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.heartbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.heartbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.heartbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.heartbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.heartbeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.heartbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({link}) をご覧ください。", + "home.tutorials.common.heartbeatInstructions.install.debTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.heartbeatInstructions.install.debTitle": "Heartbeat のダウンロードとインストール", + "home.tutorials.common.heartbeatInstructions.install.osxTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.heartbeatInstructions.install.osxTitle": "Heartbeat のダウンロードとインストール", + "home.tutorials.common.heartbeatInstructions.install.rpmTextPre": "Heartbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.heartbeatInstructions.install.rpmTitle": "Heartbeat のダウンロードとインストール", + "home.tutorials.common.heartbeatInstructions.install.windowsTextPre": "Heartbeat は初めてですか?[入門ガイド]({heartbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Heartbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Heartbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Heartbeat を Windows サービスとしてインストールします。", + "home.tutorials.common.heartbeatInstructions.install.windowsTitle": "Heartbeat のダウンロードとインストール", + "home.tutorials.common.heartbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", + "home.tutorials.common.heartbeatInstructions.start.debTitle": "Heartbeat を起動します", + "home.tutorials.common.heartbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", + "home.tutorials.common.heartbeatInstructions.start.osxTitle": "Heartbeat を起動します", + "home.tutorials.common.heartbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", + "home.tutorials.common.heartbeatInstructions.start.rpmTitle": "Heartbeat を起動します", + "home.tutorials.common.heartbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のインデックスパターンを読み込みます。", + "home.tutorials.common.heartbeatInstructions.start.windowsTitle": "Heartbeat を起動します", + "home.tutorials.common.heartbeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.heartbeatStatusCheck.errorText": "Heartbeat からまだデータを受け取っていません", + "home.tutorials.common.heartbeatStatusCheck.successText": "Heartbeat からデータを受け取りました", + "home.tutorials.common.heartbeatStatusCheck.text": "Heartbeat からデータを受け取ったことを確認してください。", + "home.tutorials.common.heartbeatStatusCheck.title": "Heartbeat のステータス", + "home.tutorials.common.logstashInstructions.install.java.osxTextPre": "[こちら]({link}) のインストール手順に従ってください。", + "home.tutorials.common.logstashInstructions.install.java.osxTitle": "Java Runtime Environment のダウンロードとインストール", + "home.tutorials.common.logstashInstructions.install.java.windowsTextPre": "[こちら]({link}) のインストール手順に従ってください。", + "home.tutorials.common.logstashInstructions.install.java.windowsTitle": "Java Runtime Environment のダウンロードとインストール", + "home.tutorials.common.logstashInstructions.install.logstash.osxTextPre": "Logstash は初めてですか? [入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.logstashInstructions.install.logstash.osxTitle": "Logstash のダウンロードとインストール", + "home.tutorials.common.logstashInstructions.install.logstash.windowsTextPre": "Logstash は初めてですか? [入門ガイド]({logstashLink}) をご覧ください。\n 1. Logstash Windows zip ファイルを [ダウンロード]({elasticLink}) します。\n 2.zip ファイルのコンテンツを展開します。", + "home.tutorials.common.logstashInstructions.install.logstash.windowsTitle": "Logstash のダウンロードとインストール", + "home.tutorials.common.metricbeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.metricbeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.metricbeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.metricbeatCloudInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.metricbeatCloudInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatCloudInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.metricbeatEnableInstructions.debTextPost": "「/etc/metricbeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", + "home.tutorials.common.metricbeatEnableInstructions.debTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.metricbeatEnableInstructions.osxTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", + "home.tutorials.common.metricbeatEnableInstructions.osxTextPre": "インストールディレクトリから次のファイルを実行します:", + "home.tutorials.common.metricbeatEnableInstructions.osxTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.metricbeatEnableInstructions.rpmTextPost": "「/etc/metricbeat/modules.d/{moduleName}.yml」ファイルで設定を変更します。", + "home.tutorials.common.metricbeatEnableInstructions.rpmTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.metricbeatEnableInstructions.windowsTextPost": "「modules.d/{moduleName}.yml」」ファイルで設定を変更します。", + "home.tutorials.common.metricbeatEnableInstructions.windowsTextPre": "「{path}」フォルダから次のファイルを実行します:", + "home.tutorials.common.metricbeatEnableInstructions.windowsTitle": "{moduleName} モジュールを有効にし構成します", + "home.tutorials.common.metricbeatInstructions.config.debTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.metricbeatInstructions.config.debTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatInstructions.config.debTitle": "構成の変更", + "home.tutorials.common.metricbeatInstructions.config.osxTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.metricbeatInstructions.config.osxTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatInstructions.config.osxTitle": "構成の変更", + "home.tutorials.common.metricbeatInstructions.config.rpmTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.metricbeatInstructions.config.rpmTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatInstructions.config.rpmTitle": "構成の変更", + "home.tutorials.common.metricbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.metricbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.metricbeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.metricbeatInstructions.install.debTextPost": "32 ビットパッケージをお探しですか?[ダウンロードページ]({link}) をご覧ください。", + "home.tutorials.common.metricbeatInstructions.install.debTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.metricbeatInstructions.install.debTitle": "Metricbeat のダウンロードとインストール", + "home.tutorials.common.metricbeatInstructions.install.osxTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.metricbeatInstructions.install.osxTitle": "Metricbeat のダウンロードとインストール", + "home.tutorials.common.metricbeatInstructions.install.rpmTextPre": "Metricbeat は初めてですか?[入門ガイド]({link}) をご覧ください。", + "home.tutorials.common.metricbeatInstructions.install.rpmTitle": "Metricbeat のダウンロードとインストール", + "home.tutorials.common.metricbeatInstructions.install.windowsTextPost": "{path} ファイルの「output.elasticsearch」を Elasticsearch のインストールに設定します。", + "home.tutorials.common.metricbeatInstructions.install.windowsTextPre": "Metricbeat は初めてですか?[入門ガイド]({metricbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Metricbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.「{directoryName}」ディレクトリの名前を「Metricbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Metricbeat を Windows サービスとしてインストールします。", + "home.tutorials.common.metricbeatInstructions.install.windowsTitle": "Metricbeat のダウンロードとインストール", + "home.tutorials.common.metricbeatInstructions.start.debTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.metricbeatInstructions.start.debTitle": "Metricbeat を起動します", + "home.tutorials.common.metricbeatInstructions.start.osxTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.metricbeatInstructions.start.osxTitle": "Metricbeat を起動します", + "home.tutorials.common.metricbeatInstructions.start.rpmTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.metricbeatInstructions.start.rpmTitle": "Metricbeat を起動します", + "home.tutorials.common.metricbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.metricbeatInstructions.start.windowsTitle": "Metricbeat を起動します", + "home.tutorials.common.metricbeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.metricbeatStatusCheck.errorText": "モジュールからまだデータを受け取っていません", + "home.tutorials.common.metricbeatStatusCheck.successText": "このモジュールからデータを受け取りました", + "home.tutorials.common.metricbeatStatusCheck.text": "Metricbeat の「{moduleName}」モジュールからデータを受け取ったことを確認してください", + "home.tutorials.common.metricbeatStatusCheck.title": "モジュールステータス", + "home.tutorials.common.premCloudInstructions.option1.textPre": "[Elastic Cloud] ({link}) にアクセスします。アカウントをお持ちでない場合は新規登録してください。14 日間の無料トライアルがご利用いただけます。\n\nElastic Cloud コンソールにログインします\n\nElastic Cloud コンソールで次の手順を実行し、クラスターを作成します。\n 1.「デプロイを作成」、「デプロイ名」の順にクリックします\n 2.必要に応じて他のデプロイオプションを変更します (デフォルトも使い始めるのに有効です)\n 3.「デプロイを作成」をクリックします\n 4.デプロイの作成が完了するまで待ちます\n 5.新規クラウド Kibana インスタンスにアクセスし、Kibana ホームの手順に従います。", + "home.tutorials.common.premCloudInstructions.option1.title": "オプション 1:Elastic Cloud でお試しください", + "home.tutorials.common.premCloudInstructions.option2.textPre": "この Kibana インスタンスをマネージド Elasticsearch インスタンスに対して実行している場合は、手動セットアップを行います。.\n\n「Elasticsearch」エンドポイントを {urlTemplate} として保存し、クラスターの「パスワード」を {passwordTemplate} として保存します。", + "home.tutorials.common.premCloudInstructions.option2.title": "オプション 2:Kibana を Cloud インスタンスに接続", + "home.tutorials.common.winlogbeat.cloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.winlogbeat.premCloudInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.winlogbeat.premInstructions.gettingStarted.title": "はじめに", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワードです。", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.winlogbeatInstructions.config.windowsTextPost": "{passwordTemplate} が「Elastic」ユーザーのパスワード、{esUrlTemplate} が Elasticsearch の URL、{kibanaUrlTemplate} が Kibana の URL です。", + "home.tutorials.common.winlogbeatInstructions.config.windowsTextPre": "{path} を変更して Elastic Cloud への接続情報を設定します:", + "home.tutorials.common.winlogbeatInstructions.config.windowsTitle": "構成の変更", + "home.tutorials.common.winlogbeatInstructions.install.windowsTextPost": "{path} ファイルの「output.elasticsearch」を Elasticsearch のインストールに設定します。", + "home.tutorials.common.winlogbeatInstructions.install.windowsTextPre": "Winlogbeat は初めてですか?[入門ガイド]({winlogbeatLink}) をご覧ください。\n 1.[ダウンロード]({elasticLink}) ページから Winlogbeat Windows zip ファイルをダウンロードします。\n 2.zip ファイルのコンテンツを {folderPath} に解凍します。\n 3.{directoryName} ディレクトリの名前を「Winlogbeat」に変更します。\n 4.管理者として PowerShell プロンプトを開きます (PowerShell アイコンを右クリックして「管理者として実行」を選択します)。Windows XP をご使用の場合、PowerShell のダウンロードとインストールが必要な場合があります。\n 5.PowerShell プロンプトで次のコマンドを実行し、Winlogbeat を Windows サービスとしてインストールします。", + "home.tutorials.common.winlogbeatInstructions.install.windowsTitle": "Winlogbeat のダウンロードとインストール", + "home.tutorials.common.winlogbeatInstructions.start.windowsTextPre": "「setup」コマンドで Kibana のダッシュボードを読み込みます。ダッシュボードが既にセットアップされている場合、このコマンドは省略します。", + "home.tutorials.common.winlogbeatInstructions.start.windowsTitle": "Winlogbeat を起動", + "home.tutorials.common.winlogbeatStatusCheck.buttonLabel": "データを確認してください", + "home.tutorials.common.winlogbeatStatusCheck.errorText": "まだデータを受信していません", + "home.tutorials.common.winlogbeatStatusCheck.successText": "データを受信しました", + "home.tutorials.common.winlogbeatStatusCheck.text": "Winlogbeat からデータを受け取ったことを確認してください。", + "home.tutorials.common.winlogbeatStatusCheck.title": "モジュールステータス", "home.tutorials.aerospikeMetrics.artifacts.application.label": "ディスカバリ", "home.tutorials.aerospikeMetrics.longDescription": "「aerospike」Metricbeat モジュールは、Aerospike から内部メトリックを取得します。 [詳細]({learnMoreLink})。", "home.tutorials.aerospikeMetrics.nameTitle": "Aerospike メトリック", @@ -2304,67 +2365,6 @@ "home.tutorials.zookeeperMetrics.longDescription": "「{moduleName}」Metricbeat モジュールは、Zookeeper サーバーから内部メトリックを取得します。 [詳細]({learnMoreLink})。", "home.tutorials.zookeeperMetrics.nameTitle": "Zookeeper メトリック", "home.tutorials.zookeeperMetrics.shortDescription": "Zookeeper サーバーから内部メトリックを取得します。", - "kbn.settings.advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "{query} を検索しました。{sectionLenght, plural, one {# セクション} other {# セクション}}に{optionLenght, plural, one {# オプション} other { # オプション}}があります。", - "kbn.topNavMenu.openInspectorButtonLabel": "検査", - "kbn.topNavMenu.refreshButtonLabel": "更新", - "kbn.topNavMenu.saveVisualizationButtonLabel": "保存", - "kbn.topNavMenu.shareVisualizationButtonLabel": "共有", - "kbn.visualize.badge.readOnly.text": "読み込み専用", - "kbn.visualize.badge.readOnly.tooltip": "ビジュアライゼーションを保存できません", - "kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPattern または savedSearchId が必要です", - "kbn.visualize.editor.createBreadcrumb": "作成", - "kbn.visualize.experimentalVisInfoText": "このビジュアライゼーションは実験的なものです。", - "kbn.visualize.linkedToSearch.unlinkButtonTooltip": "保存された検索からリンクを解除するにはダブルクリックします", - "kbn.visualize.linkedToSearch.unlinkSuccessNotificationText": "保存された検索「{searchTitle}」からリンクが解除されました", - "kbn.visualize.linkedToSearchInfoText": "保存された検索にリンクされています", - "kbn.visualize.listing.betaTitle": "ベータ", - "kbn.visualize.listing.betaTooltip": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", - "kbn.visualize.listing.breadcrumb": "可視化", - "kbn.visualize.listing.createNew.createButtonLabel": "新規ビジュアライゼーションを追加", - "kbn.visualize.listing.createNew.description": "データに基づき異なるビジュアライゼーションを作成できます。", - "kbn.visualize.listing.createNew.title": "最初のビジュアライゼーションの作成", - "kbn.visualize.listing.experimentalTitle": "実験的", - "kbn.visualize.listing.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", - "kbn.visualize.listing.noItemsMessage": "ビジュアライゼーションがないようです。", - "kbn.visualize.listing.table.entityName": "ビジュアライゼーション", - "kbn.visualize.listing.table.entityNamePlural": "ビジュアライゼーション", - "kbn.visualize.listing.table.listTitle": "ビジュアライゼーション", - "kbn.visualize.listing.table.titleColumnName": "タイトル", - "kbn.visualize.listing.table.typeColumnName": "タイプ", - "kbn.visualize.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", - "kbn.visualize.newVisWizard.betaTitle": "ベータ", - "kbn.visualize.newVisWizard.chooseSourceTitle": "ソースの選択", - "kbn.visualize.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。", - "kbn.visualize.newVisWizard.experimentalTitle": "実験的", - "kbn.visualize.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", - "kbn.visualize.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング", - "kbn.visualize.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。", - "kbn.visualize.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモダルを閉じます。Tab キーを押して次に進みます。", - "kbn.visualize.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", - "kbn.visualize.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました", - "kbn.visualize.newVisWizard.searchSelection.notFoundLabel": "一致するインデックスまたは保存された検索が見つかりませんでした。", - "kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", - "kbn.visualize.newVisWizard.searchSelection.savedObjectType.search": "保存された検索", - "kbn.visualize.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください", - "kbn.visualize.newVisWizard.title": "新規ビジュアライゼーション", - "kbn.visualize.newVisWizard.visTypeAliasDescription": "Visualize外でKibanaアプリケーションを開きます。", - "kbn.visualize.newVisWizard.visTypeAliasTitle": "Kibanaアプリケーション", - "kbn.visualize.pageHeading": "{chartName} {chartType} ビジュアライゼーション", - "kbn.visualize.saveDialog.saveAndAddToDashboardButtonLabel": "保存してダッシュボードに追加", - "kbn.visualize.topNavMenu.openInspectorButtonAriaLabel": "ビジュアライゼーションのインスペクターを開く", - "kbn.visualize.topNavMenu.openInspectorDisabledButtonTooltip": "このビジュアライゼーションはインスペクターをサポートしていません。", - "kbn.visualize.topNavMenu.refreshButtonAriaLabel": "更新", - "kbn.visualize.topNavMenu.saveVisualization.failureNotificationText": "「{visTitle}」の保存中にエラーが発生しました", - "kbn.visualize.topNavMenu.saveVisualization.successNotificationText": "「{visTitle}」が保存されました", - "kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel": "ビジュアライゼーションを保存", - "kbn.visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "保存する前に変更を適用または破棄", - "kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel": "ビジュアライゼーションを共有", - "kbn.visualize.visualizeDescription": "ビジュアライゼーションを作成して Elasticsearch インデックスに保存されたデータを集約します。", - "kbn.visualize.visualizeListingBreadcrumbsTitle": "可視化", - "kbn.visualize.visualizeListingDeleteErrorTitle": "ビジュアライゼーションの削除中にエラーが発生", - "kbn.visualize.wizard.step1Breadcrumb": "作成", - "kbn.visualize.wizard.step2Breadcrumb": "作成", - "kbn.visualizeTitle": "可視化", "visTypeVislib.area.areaDescription": "折れ線グラフの下の数量を強調します。", "visTypeVislib.area.areaTitle": "エリア", "visTypeVislib.area.countText": "カウント", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6ebba29e59480..27787a11e43ca 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1103,307 +1103,6 @@ "kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "默认 WMS 属性", "kbn.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。", "kbn.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化", - "home.tutorials.common.auditbeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.auditbeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.auditbeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.auditbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.auditbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.auditbeatCloudInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.auditbeatCloudInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.auditbeatCloudInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.auditbeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.auditbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.auditbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.auditbeatInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.auditbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.auditbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.auditbeatInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.auditbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.auditbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.auditbeatInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.auditbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.auditbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.auditbeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.auditbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", - "home.tutorials.common.auditbeatInstructions.install.debTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.auditbeatInstructions.install.debTitle": "下载并安装 Auditbeat", - "home.tutorials.common.auditbeatInstructions.install.osxTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.auditbeatInstructions.install.osxTitle": "下载并安装 Auditbeat", - "home.tutorials.common.auditbeatInstructions.install.rpmTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", - "home.tutorials.common.auditbeatInstructions.install.rpmTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.auditbeatInstructions.install.rpmTitle": "下载并安装 Auditbeat", - "home.tutorials.common.auditbeatInstructions.install.windowsTextPost": "在 {auditbeatPath} 文件中修改 {propertyName} 下的设置以指向您的 Elasticsearch 安装。", - "home.tutorials.common.auditbeatInstructions.install.windowsTextPre": "首次使用 Auditbeat?请参阅[入门指南]({guideLinkUrl})。\n 1.从[下载]({auditbeatLinkUrl})页面下载 Auditbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 `{directoryName}` 目录重命名为 `Auditbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Auditbeat 安装为 Windows 服务。", - "home.tutorials.common.auditbeatInstructions.install.windowsTitle": "下载并安装 Auditbeat", - "home.tutorials.common.auditbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.auditbeatInstructions.start.debTitle": "启动 Auditbeat", - "home.tutorials.common.auditbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.auditbeatInstructions.start.osxTitle": "启动 Auditbeat", - "home.tutorials.common.auditbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.auditbeatInstructions.start.rpmTitle": "启动 Auditbeat", - "home.tutorials.common.auditbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.auditbeatInstructions.start.windowsTitle": "启动 Auditbeat", - "home.tutorials.common.auditbeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.auditbeatStatusCheck.errorText": "尚未接收到数据", - "home.tutorials.common.auditbeatStatusCheck.successText": "已成功接收数据", - "home.tutorials.common.auditbeatStatusCheck.text": "确认从 Auditbeat 收到数据", - "home.tutorials.common.auditbeatStatusCheck.title": "状态", - "home.tutorials.common.filebeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.filebeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.filebeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.filebeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.filebeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.filebeatCloudInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.filebeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.filebeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.filebeatCloudInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.filebeatCloudInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.filebeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.filebeatEnableInstructions.debTextPost": "在 `/etc/filebeat/modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.filebeatEnableInstructions.debTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.filebeatEnableInstructions.osxTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.filebeatEnableInstructions.osxTextPre": "从安装目录中,运行:", - "home.tutorials.common.filebeatEnableInstructions.osxTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.filebeatEnableInstructions.rpmTextPost": "在 `/etc/filebeat/modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.filebeatEnableInstructions.rpmTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.filebeatEnableInstructions.windowsTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.filebeatEnableInstructions.windowsTextPre": "从 {path} 文件夹中,运行:", - "home.tutorials.common.filebeatEnableInstructions.windowsTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.filebeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.filebeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.filebeatInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.filebeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.filebeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.filebeatInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.filebeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.filebeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.filebeatInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.filebeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.filebeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.filebeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.filebeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", - "home.tutorials.common.filebeatInstructions.install.debTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.filebeatInstructions.install.debTitle": "下载并安装 Filebeat", - "home.tutorials.common.filebeatInstructions.install.osxTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.filebeatInstructions.install.osxTitle": "下载并安装 Filebeat", - "home.tutorials.common.filebeatInstructions.install.rpmTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", - "home.tutorials.common.filebeatInstructions.install.rpmTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", - "home.tutorials.common.filebeatInstructions.install.rpmTitle": "下载并安装 Filebeat", - "home.tutorials.common.filebeatInstructions.install.windowsTextPost": "在 {filebeatPath} 文件中修改 {propertyName} 下的设置以指向您的 Elasticsearch 安装。", - "home.tutorials.common.filebeatInstructions.install.windowsTextPre": "首次使用 Filebeat?请参阅[入门指南]({guideLinkUrl})。\n 1.从[下载]({filebeatLinkUrl})页面下载 Filebeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 `{directoryName}` 目录重命名为 `Filebeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Filebeat 安装为 Windows 服务。", - "home.tutorials.common.filebeatInstructions.install.windowsTitle": "下载并安装 Filebeat", - "home.tutorials.common.filebeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.filebeatInstructions.start.debTitle": "启动 Filebeat", - "home.tutorials.common.filebeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.filebeatInstructions.start.osxTitle": "启动 Filebeat", - "home.tutorials.common.filebeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.filebeatInstructions.start.rpmTitle": "启动 Filebeat", - "home.tutorials.common.filebeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.filebeatInstructions.start.windowsTitle": "启动 Filebeat", - "home.tutorials.common.filebeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.filebeatStatusCheck.errorText": "尚未从此模块收到任何数据", - "home.tutorials.common.filebeatStatusCheck.successText": "从此模块成功收到数据", - "home.tutorials.common.filebeatStatusCheck.text": "确认已从 Filebeat `{moduleName}` 模块成功收到数据", - "home.tutorials.common.filebeatStatusCheck.title": "模块状态", - "home.tutorials.common.functionbeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.functionbeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.functionbeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.functionbeatAWSInstructions.textPost": "其中 `` 和 `` 是您的帐户凭据,`us-east-1` 是所需的地区。", - "home.tutorials.common.functionbeatAWSInstructions.textPre": "在环境中设置您的 AWS 帐户凭据:", - "home.tutorials.common.functionbeatAWSInstructions.title": "设置 AWS 凭据", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.functionbeatCloudInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.functionbeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTextPost": "其中 `` 是要采集的日志组名称,`` 是将用于暂存 Functionbeat 部署的有效 S3 存储桶名称。", - "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTitle": "配置 Cloudwatch 日志组", - "home.tutorials.common.functionbeatEnableOnPremInstructionsOSXLinux.textPre": "在 `functionbeat.yml` 文件中修改设置。", - "home.tutorials.common.functionbeatEnableOnPremInstructionsWindows.textPre": "在 {path} 文件中修改设置。", - "home.tutorials.common.functionbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.functionbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.functionbeatInstructions.config.osxTitle": "配置 Elastic 集群", - "home.tutorials.common.functionbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.functionbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.functionbeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.functionbeatInstructions.deploy.osxTextPre": "这会将 Functionbeat 安装为 Lambda 函数。`setup` 命令检查 Elasticsearch 配置并加载 Kibana 索引模式。通常可省略此命令。", - "home.tutorials.common.functionbeatInstructions.deploy.osxTitle": "将 Functionbeat 部署到 AWS Lambda", - "home.tutorials.common.functionbeatInstructions.deploy.windowsTextPre": "这会将 Functionbeat 安装为 Lambda 函数。`setup` 命令检查 Elasticsearch 配置并加载 Kibana 索引模式。通常可省略此命令。", - "home.tutorials.common.functionbeatInstructions.deploy.windowsTitle": "将 Functionbeat 部署到 AWS Lambda", - "home.tutorials.common.functionbeatInstructions.install.linuxTextPre": "首次使用 Functionbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.functionbeatInstructions.install.linuxTitle": "下载并安装 Functionbeat", - "home.tutorials.common.functionbeatInstructions.install.osxTextPre": "首次使用 Functionbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.functionbeatInstructions.install.osxTitle": "下载并安装 Functionbeat", - "home.tutorials.common.functionbeatInstructions.install.windowsTextPre": "首次使用 Functionbeat?请参阅[入门指南]({functionbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Functionbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Functionbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符下,前往 Functionbeat 目录:", - "home.tutorials.common.functionbeatInstructions.install.windowsTitle": "下载并安装 Functionbeat", - "home.tutorials.common.functionbeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.functionbeatStatusCheck.errorText": "尚未从 Functionbeat 收到任何数据", - "home.tutorials.common.functionbeatStatusCheck.successText": "从 Functionbeat 成功收到数据", - "home.tutorials.common.functionbeatStatusCheck.text": "确认从 Functionbeat 收到数据", - "home.tutorials.common.functionbeatStatusCheck.title": "Functionbeat 状态", - "home.tutorials.common.heartbeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.heartbeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.heartbeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.heartbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.heartbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.heartbeatCloudInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.heartbeatCloudInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.heartbeatCloudInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.heartbeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.heartbeatEnableCloudInstructions.debTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTextPost": "有关如何在 Heartbeat 中配置监测的详细信息,请参阅 [Heartbeat 配置文档]({configureLink})", - "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTitle": "编辑配置 - 添加监测", - "home.tutorials.common.heartbeatEnableCloudInstructions.osxTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableCloudInstructions.rpmTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableCloudInstructions.windowsTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.debTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTextPost": "其中 {hostTemplate} 是受监测 URL。有关如何在 Heartbeat 中配置监测的详细信息,请参阅 [Heartbeat 配置文档]({configureLink})", - "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTitle": "编辑配置 - 添加监测", - "home.tutorials.common.heartbeatEnableOnPremInstructions.osxTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.rpmTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatEnableOnPremInstructions.windowsTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", - "home.tutorials.common.heartbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.heartbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.heartbeatInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.heartbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.heartbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.heartbeatInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.heartbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.heartbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.heartbeatInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.heartbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.heartbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.heartbeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.heartbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({link})。", - "home.tutorials.common.heartbeatInstructions.install.debTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.heartbeatInstructions.install.debTitle": "下载并安装 Heartbeat", - "home.tutorials.common.heartbeatInstructions.install.osxTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.heartbeatInstructions.install.osxTitle": "下载并安装 Heartbeat", - "home.tutorials.common.heartbeatInstructions.install.rpmTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.heartbeatInstructions.install.rpmTitle": "下载并安装 Heartbeat", - "home.tutorials.common.heartbeatInstructions.install.windowsTextPre": "首次使用 Heartbeat?请参阅[入门指南]({heartbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Heartbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Heartbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Heartbeat 安装为 Windows 服务。", - "home.tutorials.common.heartbeatInstructions.install.windowsTitle": "下载并安装 Heartbeat", - "home.tutorials.common.heartbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 索引模式。", - "home.tutorials.common.heartbeatInstructions.start.debTitle": "启动 Heartbeat", - "home.tutorials.common.heartbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 索引模式。", - "home.tutorials.common.heartbeatInstructions.start.osxTitle": "启动 Heartbeat", - "home.tutorials.common.heartbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 索引模式。", - "home.tutorials.common.heartbeatInstructions.start.rpmTitle": "启动 Heartbeat", - "home.tutorials.common.heartbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 索引模式。", - "home.tutorials.common.heartbeatInstructions.start.windowsTitle": "启动 Heartbeat", - "home.tutorials.common.heartbeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.heartbeatStatusCheck.errorText": "尚未从 Heartbeat 收到任何数据", - "home.tutorials.common.heartbeatStatusCheck.successText": "从 Heartbeat 成功收到数据", - "home.tutorials.common.heartbeatStatusCheck.text": "确认从 Heartbeat 收到数据", - "home.tutorials.common.heartbeatStatusCheck.title": "Heartbeat 状态", - "home.tutorials.common.logstashInstructions.install.java.osxTextPre": "按照[此处]({link})的安装说明执行操作。", - "home.tutorials.common.logstashInstructions.install.java.osxTitle": "下载并安装 Java Runtime Environment", - "home.tutorials.common.logstashInstructions.install.java.windowsTextPre": "按照[此处]({link})的安装说明执行操作。", - "home.tutorials.common.logstashInstructions.install.java.windowsTitle": "下载并安装 Java Runtime Environment", - "home.tutorials.common.logstashInstructions.install.logstash.osxTextPre": "首次使用 Logstash? 请参阅[入门指南]({link})。", - "home.tutorials.common.logstashInstructions.install.logstash.osxTitle": "下载并安装 Logstash", - "home.tutorials.common.logstashInstructions.install.logstash.windowsTextPre": "首次使用 Logstash? 请参阅[入门指南]({logstashLink})。\n 1. [下载]({elasticLink}) Logstash Windows zip 文件。\n 2.解压缩 zip 文件的内容。", - "home.tutorials.common.logstashInstructions.install.logstash.windowsTitle": "下载并安装 Logstash", - "home.tutorials.common.metricbeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.metricbeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.metricbeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.metricbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.metricbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.metricbeatCloudInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.metricbeatCloudInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.metricbeatCloudInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.metricbeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.metricbeatEnableInstructions.debTextPost": "在 `/etc/metricbeat/modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.metricbeatEnableInstructions.debTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.metricbeatEnableInstructions.osxTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.metricbeatEnableInstructions.osxTextPre": "从安装目录中,运行:", - "home.tutorials.common.metricbeatEnableInstructions.osxTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.metricbeatEnableInstructions.rpmTextPost": "在 `/etc/metricbeat/modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.metricbeatEnableInstructions.rpmTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.metricbeatEnableInstructions.windowsTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", - "home.tutorials.common.metricbeatEnableInstructions.windowsTextPre": "从 {path} 文件夹中,运行:", - "home.tutorials.common.metricbeatEnableInstructions.windowsTitle": "启用和配置 {moduleName} 模块", - "home.tutorials.common.metricbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.metricbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.metricbeatInstructions.config.debTitle": "编辑配置", - "home.tutorials.common.metricbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.metricbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.metricbeatInstructions.config.osxTitle": "编辑配置", - "home.tutorials.common.metricbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.metricbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.metricbeatInstructions.config.rpmTitle": "编辑配置", - "home.tutorials.common.metricbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.metricbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.metricbeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.metricbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({link})。", - "home.tutorials.common.metricbeatInstructions.install.debTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.metricbeatInstructions.install.debTitle": "下载并安装 Metricbeat", - "home.tutorials.common.metricbeatInstructions.install.osxTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.metricbeatInstructions.install.osxTitle": "下载并安装 Metricbeat", - "home.tutorials.common.metricbeatInstructions.install.rpmTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", - "home.tutorials.common.metricbeatInstructions.install.rpmTitle": "下载并安装 Metricbeat", - "home.tutorials.common.metricbeatInstructions.install.windowsTextPost": "在 {path} 文件中修改 `output.elasticsearch` 下的设置以指向您的 Elasticsearch 安装。", - "home.tutorials.common.metricbeatInstructions.install.windowsTextPre": "首次使用 Metricbeat?请参阅[入门指南]({metricbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Metricbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Metricbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Metricbeat 安装为 Windows 服务。", - "home.tutorials.common.metricbeatInstructions.install.windowsTitle": "下载并安装 Metricbeat", - "home.tutorials.common.metricbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.metricbeatInstructions.start.debTitle": "启动 Metricbeat", - "home.tutorials.common.metricbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.metricbeatInstructions.start.osxTitle": "启动 Metricbeat", - "home.tutorials.common.metricbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.metricbeatInstructions.start.rpmTitle": "启动 Metricbeat", - "home.tutorials.common.metricbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.metricbeatInstructions.start.windowsTitle": "启动 Metricbeat", - "home.tutorials.common.metricbeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.metricbeatStatusCheck.errorText": "尚未从此模块收到任何数据", - "home.tutorials.common.metricbeatStatusCheck.successText": "从此模块成功收到数据", - "home.tutorials.common.metricbeatStatusCheck.text": "确认从 Metricbeat `{moduleName}` 模块收到数据", - "home.tutorials.common.metricbeatStatusCheck.title": "模块状态", - "home.tutorials.common.premCloudInstructions.option1.textPre": "前往 [Elastic Cloud]({link})。如果您还没有帐户,请注册。免费试用 14 天。\n\n登录至 Elastic Cloud 控制台\n\n如要创建集群,请在 Elastic Cloud 控制台中:\n 1.选择**创建部署**,然后指定**部署名称**\n 2.根据需要修改其他部署选项(或者不修改,默认值可帮助您快速入门)\n 3.单击**创建部署**\n 4.等候部署创建完成\n 5.前往新的 Cloud Kibana 实例,然后按照 Kibana 主页上的说明执行操作", - "home.tutorials.common.premCloudInstructions.option1.title": "选项 1:在 Elastic Cloud 中尝试", - "home.tutorials.common.premCloudInstructions.option2.textPre": "如果基于托管式 Elasticsearch 实例运行此 Kibana 实例,请继续手动设置。\n\n针对您的记录,分别将 **Elasticsearch** 终端节点另存为 {urlTemplate}将集群**密码**另存为 {passwordTemplate}", - "home.tutorials.common.premCloudInstructions.option2.title": "选项 2:将本地 Kibana 连接到 Cloud 实例", - "home.tutorials.common.winlogbeat.cloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.winlogbeat.premCloudInstructions.gettingStarted.title": "入门", - "home.tutorials.common.winlogbeat.premInstructions.gettingStarted.title": "入门", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", - "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.winlogbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", - "home.tutorials.common.winlogbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", - "home.tutorials.common.winlogbeatInstructions.config.windowsTitle": "编辑配置", - "home.tutorials.common.winlogbeatInstructions.install.windowsTextPost": "在 {path} 文件中修改 `output.elasticsearch` 下的设置以指向您的 Elasticsearch 安装。", - "home.tutorials.common.winlogbeatInstructions.install.windowsTextPre": "首次使用 Winlogbeat?请参阅[入门指南]({winlogbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Winlogbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Winlogbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Winlogbeat 安装为 Windows 服务。", - "home.tutorials.common.winlogbeatInstructions.install.windowsTitle": "下载并安装 Winlogbeat", - "home.tutorials.common.winlogbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", - "home.tutorials.common.winlogbeatInstructions.start.windowsTitle": "启动 Winlogbeat", - "home.tutorials.common.winlogbeatStatusCheck.buttonLabel": "检查数据", - "home.tutorials.common.winlogbeatStatusCheck.errorText": "尚未接收到数据", - "home.tutorials.common.winlogbeatStatusCheck.successText": "已成功接收数据", - "home.tutorials.common.winlogbeatStatusCheck.text": "确认从 Winlogbeat 收到数据", - "home.tutorials.common.winlogbeatStatusCheck.title": "模块状态", "kbn.context.breadcrumb": "{indexPatternTitle}#{docId} 的上下文", "kbn.context.failedToLoadAnchorDocumentDescription": "无法加载该定位点文档", "kbn.context.failedToLoadAnchorDocumentErrorDescription": "无法加载定位点文档。", @@ -2050,6 +1749,368 @@ "kbn.management.settings.searchBarAriaLabel": "搜索高级设置", "kbn.management.settings.sectionLabel": "高级设置", "kbn.managementTitle": "管理", + "kbn.settings.advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "您已搜索 {query}。{sectionLenght, plural, one {# 个部分} other {# 个部分}}中有 {optionLenght, plural, one {# 个选项} other {# 个选项}}", + "kbn.topNavMenu.openInspectorButtonLabel": "检查", + "kbn.topNavMenu.refreshButtonLabel": "刷新", + "kbn.topNavMenu.saveVisualizationButtonLabel": "保存", + "kbn.topNavMenu.shareVisualizationButtonLabel": "共享", + "kbn.visualize.badge.readOnly.text": "只读", + "kbn.visualize.badge.readOnly.tooltip": "无法保存可视化", + "kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId", + "kbn.visualize.editor.createBreadcrumb": "创建", + "kbn.visualize.experimentalVisInfoText": "此可视化标记为“实验”。", + "kbn.visualize.linkedToSearch.unlinkButtonTooltip": "双击可取消与“已保存搜索”的链接", + "kbn.visualize.linkedToSearch.unlinkSuccessNotificationText": "取消与已保存搜索 “{searchTitle}” 的链接", + "kbn.visualize.linkedToSearchInfoText": "链接到“已保存搜索”", + "kbn.visualize.listing.betaTitle": "公测版", + "kbn.visualize.listing.betaTooltip": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "kbn.visualize.listing.breadcrumb": "可视化", + "kbn.visualize.listing.createNew.createButtonLabel": "新建可视化", + "kbn.visualize.listing.createNew.description": "可以根据您的数据创建不同的可视化。", + "kbn.visualize.listing.createNew.title": "创建首个可视化", + "kbn.visualize.listing.experimentalTitle": "实验性", + "kbn.visualize.listing.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", + "kbn.visualize.listing.noItemsMessage": "看起来您还没有任何可视化。", + "kbn.visualize.listing.table.entityName": "可视化", + "kbn.visualize.listing.table.entityNamePlural": "可视化", + "kbn.visualize.listing.table.listTitle": "可视化", + "kbn.visualize.listing.table.titleColumnName": "标题", + "kbn.visualize.listing.table.typeColumnName": "类型", + "kbn.visualize.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "kbn.visualize.newVisWizard.betaTitle": "公测版", + "kbn.visualize.newVisWizard.chooseSourceTitle": "选择源", + "kbn.visualize.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。", + "kbn.visualize.newVisWizard.experimentalTitle": "实验性", + "kbn.visualize.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", + "kbn.visualize.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型", + "kbn.visualize.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。", + "kbn.visualize.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。", + "kbn.visualize.newVisWizard.newVisTypeTitle": "新建{visTypeName}", + "kbn.visualize.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }", + "kbn.visualize.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", + "kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式", + "kbn.visualize.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", + "kbn.visualize.newVisWizard.selectVisType": "选择可视化类型", + "kbn.visualize.newVisWizard.title": "新建可视化", + "kbn.visualize.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。", + "kbn.visualize.newVisWizard.visTypeAliasTitle": "Kibana 应用程序", + "kbn.visualize.pageHeading": "{chartName} {chartType}可视化", + "kbn.visualize.saveDialog.saveAndAddToDashboardButtonLabel": "保存并添加到仪表板", + "kbn.visualize.topNavMenu.openInspectorButtonAriaLabel": "打开检查器查看可视化", + "kbn.visualize.topNavMenu.openInspectorDisabledButtonTooltip": "此可视化不支持任何检查器。", + "kbn.visualize.topNavMenu.refreshButtonAriaLabel": "刷新", + "kbn.visualize.topNavMenu.saveVisualization.failureNotificationText": "保存 “{visTitle}” 时出错", + "kbn.visualize.topNavMenu.saveVisualization.successNotificationText": "已保存“{visTitle}”", + "kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel": "保存可视化", + "kbn.visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "应用或放弃所做更改,然后保存", + "kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel": "共享可视化", + "kbn.visualize.visualizeDescription": "创建可视化并聚合存储在 Elasticsearch 索引中的数据。", + "kbn.visualize.visualizeListingBreadcrumbsTitle": "可视化", + "kbn.visualize.visualizeListingDeleteErrorTitle": "删除可视化时出错", + "kbn.visualize.wizard.step1Breadcrumb": "创建", + "kbn.visualize.wizard.step2Breadcrumb": "创建", + "kbn.visualizeTitle": "可视化", + "home.tutorials.common.auditbeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.auditbeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.auditbeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.auditbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.auditbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.auditbeatCloudInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.auditbeatCloudInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.auditbeatCloudInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.auditbeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.auditbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.auditbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.auditbeatInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.auditbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.auditbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.auditbeatInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.auditbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.auditbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.auditbeatInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.auditbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.auditbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.auditbeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.auditbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", + "home.tutorials.common.auditbeatInstructions.install.debTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.auditbeatInstructions.install.debTitle": "下载并安装 Auditbeat", + "home.tutorials.common.auditbeatInstructions.install.osxTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.auditbeatInstructions.install.osxTitle": "下载并安装 Auditbeat", + "home.tutorials.common.auditbeatInstructions.install.rpmTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", + "home.tutorials.common.auditbeatInstructions.install.rpmTextPre": "首次使用 Auditbeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.auditbeatInstructions.install.rpmTitle": "下载并安装 Auditbeat", + "home.tutorials.common.auditbeatInstructions.install.windowsTextPost": "在 {auditbeatPath} 文件中修改 {propertyName} 下的设置以指向您的 Elasticsearch 安装。", + "home.tutorials.common.auditbeatInstructions.install.windowsTextPre": "首次使用 Auditbeat?请参阅[入门指南]({guideLinkUrl})。\n 1.从[下载]({auditbeatLinkUrl})页面下载 Auditbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 `{directoryName}` 目录重命名为 `Auditbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Auditbeat 安装为 Windows 服务。", + "home.tutorials.common.auditbeatInstructions.install.windowsTitle": "下载并安装 Auditbeat", + "home.tutorials.common.auditbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.auditbeatInstructions.start.debTitle": "启动 Auditbeat", + "home.tutorials.common.auditbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.auditbeatInstructions.start.osxTitle": "启动 Auditbeat", + "home.tutorials.common.auditbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.auditbeatInstructions.start.rpmTitle": "启动 Auditbeat", + "home.tutorials.common.auditbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.auditbeatInstructions.start.windowsTitle": "启动 Auditbeat", + "home.tutorials.common.auditbeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.auditbeatStatusCheck.errorText": "尚未接收到数据", + "home.tutorials.common.auditbeatStatusCheck.successText": "已成功接收数据", + "home.tutorials.common.auditbeatStatusCheck.text": "确认从 Auditbeat 收到数据", + "home.tutorials.common.auditbeatStatusCheck.title": "状态", + "home.tutorials.common.filebeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.filebeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.filebeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.filebeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.filebeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.filebeatCloudInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.filebeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.filebeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.filebeatCloudInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.filebeatCloudInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.filebeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.filebeatEnableInstructions.debTextPost": "在 `/etc/filebeat/modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.filebeatEnableInstructions.debTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.filebeatEnableInstructions.osxTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.filebeatEnableInstructions.osxTextPre": "从安装目录中,运行:", + "home.tutorials.common.filebeatEnableInstructions.osxTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.filebeatEnableInstructions.rpmTextPost": "在 `/etc/filebeat/modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.filebeatEnableInstructions.rpmTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.filebeatEnableInstructions.windowsTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.filebeatEnableInstructions.windowsTextPre": "从 {path} 文件夹中,运行:", + "home.tutorials.common.filebeatEnableInstructions.windowsTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.filebeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.filebeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.filebeatInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.filebeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.filebeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.filebeatInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.filebeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.filebeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.filebeatInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.filebeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.filebeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.filebeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.filebeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", + "home.tutorials.common.filebeatInstructions.install.debTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.filebeatInstructions.install.debTitle": "下载并安装 Filebeat", + "home.tutorials.common.filebeatInstructions.install.osxTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.filebeatInstructions.install.osxTitle": "下载并安装 Filebeat", + "home.tutorials.common.filebeatInstructions.install.rpmTextPost": "寻找 32 位软件包?请参阅[下载页面]({linkUrl})。", + "home.tutorials.common.filebeatInstructions.install.rpmTextPre": "首次使用 Filebeat?请参阅[入门指南]({linkUrl})。", + "home.tutorials.common.filebeatInstructions.install.rpmTitle": "下载并安装 Filebeat", + "home.tutorials.common.filebeatInstructions.install.windowsTextPost": "在 {filebeatPath} 文件中修改 {propertyName} 下的设置以指向您的 Elasticsearch 安装。", + "home.tutorials.common.filebeatInstructions.install.windowsTextPre": "首次使用 Filebeat?请参阅[入门指南]({guideLinkUrl})。\n 1.从[下载]({filebeatLinkUrl})页面下载 Filebeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 `{directoryName}` 目录重命名为 `Filebeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Filebeat 安装为 Windows 服务。", + "home.tutorials.common.filebeatInstructions.install.windowsTitle": "下载并安装 Filebeat", + "home.tutorials.common.filebeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.filebeatInstructions.start.debTitle": "启动 Filebeat", + "home.tutorials.common.filebeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.filebeatInstructions.start.osxTitle": "启动 Filebeat", + "home.tutorials.common.filebeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.filebeatInstructions.start.rpmTitle": "启动 Filebeat", + "home.tutorials.common.filebeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.filebeatInstructions.start.windowsTitle": "启动 Filebeat", + "home.tutorials.common.filebeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.filebeatStatusCheck.errorText": "尚未从此模块收到任何数据", + "home.tutorials.common.filebeatStatusCheck.successText": "从此模块成功收到数据", + "home.tutorials.common.filebeatStatusCheck.text": "确认已从 Filebeat `{moduleName}` 模块成功收到数据", + "home.tutorials.common.filebeatStatusCheck.title": "模块状态", + "home.tutorials.common.functionbeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.functionbeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.functionbeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.functionbeatAWSInstructions.textPost": "其中 `` 和 `` 是您的帐户凭据,`us-east-1` 是所需的地区。", + "home.tutorials.common.functionbeatAWSInstructions.textPre": "在环境中设置您的 AWS 帐户凭据:", + "home.tutorials.common.functionbeatAWSInstructions.title": "设置 AWS 凭据", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.functionbeatCloudInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.functionbeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTextPost": "其中 `` 是要采集的日志组名称,`` 是将用于暂存 Functionbeat 部署的有效 S3 存储桶名称。", + "home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTitle": "配置 Cloudwatch 日志组", + "home.tutorials.common.functionbeatEnableOnPremInstructionsOSXLinux.textPre": "在 `functionbeat.yml` 文件中修改设置。", + "home.tutorials.common.functionbeatEnableOnPremInstructionsWindows.textPre": "在 {path} 文件中修改设置。", + "home.tutorials.common.functionbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.functionbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.functionbeatInstructions.config.osxTitle": "配置 Elastic 集群", + "home.tutorials.common.functionbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.functionbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.functionbeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.functionbeatInstructions.deploy.osxTextPre": "这会将 Functionbeat 安装为 Lambda 函数。`setup` 命令检查 Elasticsearch 配置并加载 Kibana 索引模式。通常可省略此命令。", + "home.tutorials.common.functionbeatInstructions.deploy.osxTitle": "将 Functionbeat 部署到 AWS Lambda", + "home.tutorials.common.functionbeatInstructions.deploy.windowsTextPre": "这会将 Functionbeat 安装为 Lambda 函数。`setup` 命令检查 Elasticsearch 配置并加载 Kibana 索引模式。通常可省略此命令。", + "home.tutorials.common.functionbeatInstructions.deploy.windowsTitle": "将 Functionbeat 部署到 AWS Lambda", + "home.tutorials.common.functionbeatInstructions.install.linuxTextPre": "首次使用 Functionbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.functionbeatInstructions.install.linuxTitle": "下载并安装 Functionbeat", + "home.tutorials.common.functionbeatInstructions.install.osxTextPre": "首次使用 Functionbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.functionbeatInstructions.install.osxTitle": "下载并安装 Functionbeat", + "home.tutorials.common.functionbeatInstructions.install.windowsTextPre": "首次使用 Functionbeat?请参阅[入门指南]({functionbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Functionbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Functionbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符下,前往 Functionbeat 目录:", + "home.tutorials.common.functionbeatInstructions.install.windowsTitle": "下载并安装 Functionbeat", + "home.tutorials.common.functionbeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.functionbeatStatusCheck.errorText": "尚未从 Functionbeat 收到任何数据", + "home.tutorials.common.functionbeatStatusCheck.successText": "从 Functionbeat 成功收到数据", + "home.tutorials.common.functionbeatStatusCheck.text": "确认从 Functionbeat 收到数据", + "home.tutorials.common.functionbeatStatusCheck.title": "Functionbeat 状态", + "home.tutorials.common.heartbeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.heartbeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.heartbeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.heartbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.heartbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.heartbeatCloudInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.heartbeatCloudInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.heartbeatCloudInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.heartbeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.heartbeatEnableCloudInstructions.debTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTextPost": "有关如何在 Heartbeat 中配置监测的详细信息,请参阅 [Heartbeat 配置文档]({configureLink})", + "home.tutorials.common.heartbeatEnableCloudInstructions.defaultTitle": "编辑配置 - 添加监测", + "home.tutorials.common.heartbeatEnableCloudInstructions.osxTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableCloudInstructions.rpmTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableCloudInstructions.windowsTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.debTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTextPost": "其中 {hostTemplate} 是受监测 URL。有关如何在 Heartbeat 中配置监测的详细信息,请参阅 [Heartbeat 配置文档]({configureLink})", + "home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTitle": "编辑配置 - 添加监测", + "home.tutorials.common.heartbeatEnableOnPremInstructions.osxTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.rpmTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatEnableOnPremInstructions.windowsTextPre": "在 `heartbeat.yml` 文件中编辑 `heartbeat.monitors` 设置。", + "home.tutorials.common.heartbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.heartbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.heartbeatInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.heartbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.heartbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.heartbeatInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.heartbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.heartbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.heartbeatInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.heartbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.heartbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.heartbeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.heartbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({link})。", + "home.tutorials.common.heartbeatInstructions.install.debTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.heartbeatInstructions.install.debTitle": "下载并安装 Heartbeat", + "home.tutorials.common.heartbeatInstructions.install.osxTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.heartbeatInstructions.install.osxTitle": "下载并安装 Heartbeat", + "home.tutorials.common.heartbeatInstructions.install.rpmTextPre": "首次使用 Heartbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.heartbeatInstructions.install.rpmTitle": "下载并安装 Heartbeat", + "home.tutorials.common.heartbeatInstructions.install.windowsTextPre": "首次使用 Heartbeat?请参阅[入门指南]({heartbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Heartbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Heartbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Heartbeat 安装为 Windows 服务。", + "home.tutorials.common.heartbeatInstructions.install.windowsTitle": "下载并安装 Heartbeat", + "home.tutorials.common.heartbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 索引模式。", + "home.tutorials.common.heartbeatInstructions.start.debTitle": "启动 Heartbeat", + "home.tutorials.common.heartbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 索引模式。", + "home.tutorials.common.heartbeatInstructions.start.osxTitle": "启动 Heartbeat", + "home.tutorials.common.heartbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 索引模式。", + "home.tutorials.common.heartbeatInstructions.start.rpmTitle": "启动 Heartbeat", + "home.tutorials.common.heartbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 索引模式。", + "home.tutorials.common.heartbeatInstructions.start.windowsTitle": "启动 Heartbeat", + "home.tutorials.common.heartbeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.heartbeatStatusCheck.errorText": "尚未从 Heartbeat 收到任何数据", + "home.tutorials.common.heartbeatStatusCheck.successText": "从 Heartbeat 成功收到数据", + "home.tutorials.common.heartbeatStatusCheck.text": "确认从 Heartbeat 收到数据", + "home.tutorials.common.heartbeatStatusCheck.title": "Heartbeat 状态", + "home.tutorials.common.logstashInstructions.install.java.osxTextPre": "按照[此处]({link})的安装说明执行操作。", + "home.tutorials.common.logstashInstructions.install.java.osxTitle": "下载并安装 Java Runtime Environment", + "home.tutorials.common.logstashInstructions.install.java.windowsTextPre": "按照[此处]({link})的安装说明执行操作。", + "home.tutorials.common.logstashInstructions.install.java.windowsTitle": "下载并安装 Java Runtime Environment", + "home.tutorials.common.logstashInstructions.install.logstash.osxTextPre": "首次使用 Logstash? 请参阅[入门指南]({link})。", + "home.tutorials.common.logstashInstructions.install.logstash.osxTitle": "下载并安装 Logstash", + "home.tutorials.common.logstashInstructions.install.logstash.windowsTextPre": "首次使用 Logstash? 请参阅[入门指南]({logstashLink})。\n 1. [下载]({elasticLink}) Logstash Windows zip 文件。\n 2.解压缩 zip 文件的内容。", + "home.tutorials.common.logstashInstructions.install.logstash.windowsTitle": "下载并安装 Logstash", + "home.tutorials.common.metricbeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.metricbeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.metricbeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.metricbeatCloudInstructions.config.debTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.metricbeatCloudInstructions.config.debTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.metricbeatCloudInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.metricbeatCloudInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.metricbeatCloudInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.metricbeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.metricbeatEnableInstructions.debTextPost": "在 `/etc/metricbeat/modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.metricbeatEnableInstructions.debTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.metricbeatEnableInstructions.osxTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.metricbeatEnableInstructions.osxTextPre": "从安装目录中,运行:", + "home.tutorials.common.metricbeatEnableInstructions.osxTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.metricbeatEnableInstructions.rpmTextPost": "在 `/etc/metricbeat/modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.metricbeatEnableInstructions.rpmTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.metricbeatEnableInstructions.windowsTextPost": "在 `modules.d/{moduleName}.yml` 文件中修改设置。", + "home.tutorials.common.metricbeatEnableInstructions.windowsTextPre": "从 {path} 文件夹中,运行:", + "home.tutorials.common.metricbeatEnableInstructions.windowsTitle": "启用和配置 {moduleName} 模块", + "home.tutorials.common.metricbeatInstructions.config.debTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.metricbeatInstructions.config.debTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.metricbeatInstructions.config.debTitle": "编辑配置", + "home.tutorials.common.metricbeatInstructions.config.osxTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.metricbeatInstructions.config.osxTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.metricbeatInstructions.config.osxTitle": "编辑配置", + "home.tutorials.common.metricbeatInstructions.config.rpmTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.metricbeatInstructions.config.rpmTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.metricbeatInstructions.config.rpmTitle": "编辑配置", + "home.tutorials.common.metricbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.metricbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.metricbeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.metricbeatInstructions.install.debTextPost": "寻找 32 位软件包?请参阅[下载页面]({link})。", + "home.tutorials.common.metricbeatInstructions.install.debTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.metricbeatInstructions.install.debTitle": "下载并安装 Metricbeat", + "home.tutorials.common.metricbeatInstructions.install.osxTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.metricbeatInstructions.install.osxTitle": "下载并安装 Metricbeat", + "home.tutorials.common.metricbeatInstructions.install.rpmTextPre": "首次使用 Metricbeat?请参阅[入门指南]({link})。", + "home.tutorials.common.metricbeatInstructions.install.rpmTitle": "下载并安装 Metricbeat", + "home.tutorials.common.metricbeatInstructions.install.windowsTextPost": "在 {path} 文件中修改 `output.elasticsearch` 下的设置以指向您的 Elasticsearch 安装。", + "home.tutorials.common.metricbeatInstructions.install.windowsTextPre": "首次使用 Metricbeat?请参阅[入门指南]({metricbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Metricbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Metricbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Metricbeat 安装为 Windows 服务。", + "home.tutorials.common.metricbeatInstructions.install.windowsTitle": "下载并安装 Metricbeat", + "home.tutorials.common.metricbeatInstructions.start.debTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.metricbeatInstructions.start.debTitle": "启动 Metricbeat", + "home.tutorials.common.metricbeatInstructions.start.osxTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.metricbeatInstructions.start.osxTitle": "启动 Metricbeat", + "home.tutorials.common.metricbeatInstructions.start.rpmTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.metricbeatInstructions.start.rpmTitle": "启动 Metricbeat", + "home.tutorials.common.metricbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.metricbeatInstructions.start.windowsTitle": "启动 Metricbeat", + "home.tutorials.common.metricbeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.metricbeatStatusCheck.errorText": "尚未从此模块收到任何数据", + "home.tutorials.common.metricbeatStatusCheck.successText": "从此模块成功收到数据", + "home.tutorials.common.metricbeatStatusCheck.text": "确认从 Metricbeat `{moduleName}` 模块收到数据", + "home.tutorials.common.metricbeatStatusCheck.title": "模块状态", + "home.tutorials.common.premCloudInstructions.option1.textPre": "前往 [Elastic Cloud]({link})。如果您还没有帐户,请注册。免费试用 14 天。\n\n登录至 Elastic Cloud 控制台\n\n如要创建集群,请在 Elastic Cloud 控制台中:\n 1.选择**创建部署**,然后指定**部署名称**\n 2.根据需要修改其他部署选项(或者不修改,默认值可帮助您快速入门)\n 3.单击**创建部署**\n 4.等候部署创建完成\n 5.前往新的 Cloud Kibana 实例,然后按照 Kibana 主页上的说明执行操作", + "home.tutorials.common.premCloudInstructions.option1.title": "选项 1:在 Elastic Cloud 中尝试", + "home.tutorials.common.premCloudInstructions.option2.textPre": "如果基于托管式 Elasticsearch 实例运行此 Kibana 实例,请继续手动设置。\n\n针对您的记录,分别将 **Elasticsearch** 终端节点另存为 {urlTemplate}将集群**密码**另存为 {passwordTemplate}", + "home.tutorials.common.premCloudInstructions.option2.title": "选项 2:将本地 Kibana 连接到 Cloud 实例", + "home.tutorials.common.winlogbeat.cloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.winlogbeat.premCloudInstructions.gettingStarted.title": "入门", + "home.tutorials.common.winlogbeat.premInstructions.gettingStarted.title": "入门", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPost": "其中 {passwordTemplate} 是 `elastic` 用户的密码。", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTextPre": "修改 {path} 以设置 Elastic Cloud 的连接信息:", + "home.tutorials.common.winlogbeatCloudInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.winlogbeatInstructions.config.windowsTextPost": "其中,{passwordTemplate} 是 `elastic` 用户的密码,{esUrlTemplate} 是 Elasticsearch 的 URL,{kibanaUrlTemplate} 是 Kibana 的 URL。", + "home.tutorials.common.winlogbeatInstructions.config.windowsTextPre": "修改 {path} 以设置连接信息:", + "home.tutorials.common.winlogbeatInstructions.config.windowsTitle": "编辑配置", + "home.tutorials.common.winlogbeatInstructions.install.windowsTextPost": "在 {path} 文件中修改 `output.elasticsearch` 下的设置以指向您的 Elasticsearch 安装。", + "home.tutorials.common.winlogbeatInstructions.install.windowsTextPre": "首次使用 Winlogbeat?请参阅[入门指南]({winlogbeatLink})。\n 1.从[下载]({elasticLink})页面下载 Winlogbeat Windows zip 文件。\n 2.将 zip 文件的内容解压缩到 {folderPath}。\n 3.将 {directoryName} 目录重命名为 `Winlogbeat`。\n 4.以管理员身份打开 PowerShell 提示符(右键单击 PowerShell 图标,然后选择**以管理员身份运行**)。如果您正在运行 Windows XP,您可能需要下载并安装 PowerShell。\n 5.从 PowerShell 提示符处,运行以下命令以将 Winlogbeat 安装为 Windows 服务。", + "home.tutorials.common.winlogbeatInstructions.install.windowsTitle": "下载并安装 Winlogbeat", + "home.tutorials.common.winlogbeatInstructions.start.windowsTextPre": "`setup` 命令加载 Kibana 仪表板。如果仪表板已设置,请省略此命令。", + "home.tutorials.common.winlogbeatInstructions.start.windowsTitle": "启动 Winlogbeat", + "home.tutorials.common.winlogbeatStatusCheck.buttonLabel": "检查数据", + "home.tutorials.common.winlogbeatStatusCheck.errorText": "尚未接收到数据", + "home.tutorials.common.winlogbeatStatusCheck.successText": "已成功接收数据", + "home.tutorials.common.winlogbeatStatusCheck.text": "确认从 Winlogbeat 收到数据", + "home.tutorials.common.winlogbeatStatusCheck.title": "模块状态", "home.tutorials.aerospikeMetrics.artifacts.application.label": "Discover", "home.tutorials.aerospikeMetrics.longDescription": "Metricbeat 模块 `aerospike` 从 Aerospike 提取内部指标。[了解详情]({learnMoreLink})。", "home.tutorials.aerospikeMetrics.nameTitle": "Aerospike 指标", @@ -2304,67 +2365,6 @@ "home.tutorials.zookeeperMetrics.longDescription": "Metricbeat 模块 `{moduleName}` 从 Zookeeper 服务器提取内部指标。[了解详情]({learnMoreLink})。", "home.tutorials.zookeeperMetrics.nameTitle": "Zookeeper 指标", "home.tutorials.zookeeperMetrics.shortDescription": "从 Zookeeper 服务器提取内部指标。", - "kbn.settings.advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "您已搜索 {query}。{sectionLenght, plural, one {# 个部分} other {# 个部分}}中有 {optionLenght, plural, one {# 个选项} other {# 个选项}}", - "kbn.topNavMenu.openInspectorButtonLabel": "检查", - "kbn.topNavMenu.refreshButtonLabel": "刷新", - "kbn.topNavMenu.saveVisualizationButtonLabel": "保存", - "kbn.topNavMenu.shareVisualizationButtonLabel": "共享", - "kbn.visualize.badge.readOnly.text": "只读", - "kbn.visualize.badge.readOnly.tooltip": "无法保存可视化", - "kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId", - "kbn.visualize.editor.createBreadcrumb": "创建", - "kbn.visualize.experimentalVisInfoText": "此可视化标记为“实验”。", - "kbn.visualize.linkedToSearch.unlinkButtonTooltip": "双击可取消与“已保存搜索”的链接", - "kbn.visualize.linkedToSearch.unlinkSuccessNotificationText": "取消与已保存搜索 “{searchTitle}” 的链接", - "kbn.visualize.linkedToSearchInfoText": "链接到“已保存搜索”", - "kbn.visualize.listing.betaTitle": "公测版", - "kbn.visualize.listing.betaTooltip": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", - "kbn.visualize.listing.breadcrumb": "可视化", - "kbn.visualize.listing.createNew.createButtonLabel": "新建可视化", - "kbn.visualize.listing.createNew.description": "可以根据您的数据创建不同的可视化。", - "kbn.visualize.listing.createNew.title": "创建首个可视化", - "kbn.visualize.listing.experimentalTitle": "实验性", - "kbn.visualize.listing.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", - "kbn.visualize.listing.noItemsMessage": "看起来您还没有任何可视化。", - "kbn.visualize.listing.table.entityName": "可视化", - "kbn.visualize.listing.table.entityNamePlural": "可视化", - "kbn.visualize.listing.table.listTitle": "可视化", - "kbn.visualize.listing.table.titleColumnName": "标题", - "kbn.visualize.listing.table.typeColumnName": "类型", - "kbn.visualize.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", - "kbn.visualize.newVisWizard.betaTitle": "公测版", - "kbn.visualize.newVisWizard.chooseSourceTitle": "选择源", - "kbn.visualize.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。", - "kbn.visualize.newVisWizard.experimentalTitle": "实验性", - "kbn.visualize.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", - "kbn.visualize.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型", - "kbn.visualize.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。", - "kbn.visualize.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。", - "kbn.visualize.newVisWizard.newVisTypeTitle": "新建{visTypeName}", - "kbn.visualize.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }", - "kbn.visualize.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", - "kbn.visualize.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式", - "kbn.visualize.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", - "kbn.visualize.newVisWizard.selectVisType": "选择可视化类型", - "kbn.visualize.newVisWizard.title": "新建可视化", - "kbn.visualize.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。", - "kbn.visualize.newVisWizard.visTypeAliasTitle": "Kibana 应用程序", - "kbn.visualize.pageHeading": "{chartName} {chartType}可视化", - "kbn.visualize.saveDialog.saveAndAddToDashboardButtonLabel": "保存并添加到仪表板", - "kbn.visualize.topNavMenu.openInspectorButtonAriaLabel": "打开检查器查看可视化", - "kbn.visualize.topNavMenu.openInspectorDisabledButtonTooltip": "此可视化不支持任何检查器。", - "kbn.visualize.topNavMenu.refreshButtonAriaLabel": "刷新", - "kbn.visualize.topNavMenu.saveVisualization.failureNotificationText": "保存 “{visTitle}” 时出错", - "kbn.visualize.topNavMenu.saveVisualization.successNotificationText": "已保存“{visTitle}”", - "kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel": "保存可视化", - "kbn.visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "应用或放弃所做更改,然后保存", - "kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel": "共享可视化", - "kbn.visualize.visualizeDescription": "创建可视化并聚合存储在 Elasticsearch 索引中的数据。", - "kbn.visualize.visualizeListingBreadcrumbsTitle": "可视化", - "kbn.visualize.visualizeListingDeleteErrorTitle": "删除可视化时出错", - "kbn.visualize.wizard.step1Breadcrumb": "创建", - "kbn.visualize.wizard.step2Breadcrumb": "创建", - "kbn.visualizeTitle": "可视化", "visTypeVislib.area.areaDescription": "突出折线图下方的数量", "visTypeVislib.area.areaTitle": "面积图", "visTypeVislib.area.countText": "计数", diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx b/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx index 3d8ae2894b320..f0a2816dd57b1 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx @@ -5,6 +5,7 @@ */ import React from 'react'; +import { of } from 'rxjs'; import { ComponentType } from 'enzyme'; import { chromeServiceMock, @@ -15,6 +16,7 @@ import { } from '../../../../../../src/core/public/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AppContextProvider } from '../../../public/application/app_context'; +import { LicenseStatus } from '../../../common/types/license_status'; class MockTimeBuckets { setBounds(_domain: any) { @@ -27,9 +29,7 @@ class MockTimeBuckets { } } export const mockContextValue = { - getLicenseStatus: () => ({ - valid: true, - }), + licenseStatus$: of({ valid: true }), docLinks: docLinksServiceMock.createStartContract(), chrome: chromeServiceMock.createStartContract(), MANAGEMENT_BREADCRUMB: { text: 'test' }, diff --git a/x-pack/plugins/watcher/public/application/app.tsx b/x-pack/plugins/watcher/public/application/app.tsx index 83501eca1429b..1f418e4475ed0 100644 --- a/x-pack/plugins/watcher/public/application/app.tsx +++ b/x-pack/plugins/watcher/public/application/app.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { Observable } from 'rxjs'; import { ChromeStart, DocLinksStart, @@ -47,12 +48,17 @@ export interface AppDeps { uiSettings: IUiSettingsClient; euiUtils: any; createTimeBuckets: () => any; - getLicenseStatus: () => LicenseStatus; + licenseStatus$: Observable; MANAGEMENT_BREADCRUMB: any; } export const App = (deps: AppDeps) => { - const { valid, message } = deps.getLicenseStatus(); + const [{ valid, message }, setLicenseStatus] = useState({ valid: true }); + + useEffect(() => { + const s = deps.licenseStatus$.subscribe(setLicenseStatus); + return () => s.unsubscribe(); + }, [deps.licenseStatus$]); if (!valid) { return ( diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index d59041b41253d..7bb422f9a6eb1 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -5,80 +5,90 @@ */ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart } from 'kibana/public'; +import { first, map, skip } from 'rxjs/operators'; import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { LicenseStatus } from '../common/types/license_status'; -import { LICENSE_CHECK_STATE } from '../../licensing/public'; +import { ILicense, LICENSE_CHECK_STATE } from '../../licensing/public'; import { TimeBuckets, MANAGEMENT_BREADCRUMB } from './legacy'; import { PLUGIN } from '../common/constants'; import { Dependencies } from './types'; -export class WatcherUIPlugin implements Plugin { - // Reference for when `mount` gets called, we don't want to render if - // we don't have a valid license. Under certain conditions the Watcher app link - // may still be present so this is a final guard. - private licenseStatus: LicenseStatus = { valid: false }; - private hasRegisteredESManagementSection = false; +const licenseToLicenseStatus = (license: ILicense): LicenseStatus => { + const { state, message } = license.check(PLUGIN.ID, PLUGIN.MINIMUM_LICENSE_REQUIRED); + return { + valid: state === LICENSE_CHECK_STATE.Valid && license.getFeature(PLUGIN.ID).isAvailable, + message, + }; +}; +export class WatcherUIPlugin implements Plugin { setup( { application, notifications, http, uiSettings, getStartServices }: CoreSetup, { licensing, management, data, home }: Dependencies ) { - licensing.license$.subscribe(license => { - const { state, message } = license.check(PLUGIN.ID, PLUGIN.MINIMUM_LICENSE_REQUIRED); - this.licenseStatus = { - valid: state === LICENSE_CHECK_STATE.Valid && license.getFeature(PLUGIN.ID).isAvailable, - message, - }; - if (this.licenseStatus.valid) { - const esSection = management.sections.getSection('elasticsearch'); - if (esSection && !this.hasRegisteredESManagementSection) { - esSection.registerApp({ - id: 'watcher', - title: i18n.translate( - 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', - { defaultMessage: 'Watcher' } - ), - mount: async ({ element }) => { - const [core, plugins] = await getStartServices(); - const { chrome, i18n: i18nDep, docLinks, savedObjects } = core; - const { eui_utils } = plugins as any; - const { boot } = await import('./application/boot'); + const esSection = management.sections.getSection('elasticsearch'); + + const watcherESApp = esSection!.registerApp({ + id: 'watcher', + title: i18n.translate( + 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', + { defaultMessage: 'Watcher' } + ), + mount: async ({ element }) => { + const [core, plugins] = await getStartServices(); + const { chrome, i18n: i18nDep, docLinks, savedObjects } = core; + const { eui_utils } = plugins as any; + const { boot } = await import('./application/boot'); + + return boot({ + // Skip the first license status, because that's already been used to determine + // whether to include Watcher. + licenseStatus$: licensing.license$.pipe(skip(1), map(licenseToLicenseStatus)), + element, + toasts: notifications.toasts, + http, + uiSettings, + docLinks, + chrome, + euiUtils: eui_utils, + savedObjects: savedObjects.client, + I18nContext: i18nDep.Context, + createTimeBuckets: () => new TimeBuckets(uiSettings, data), + MANAGEMENT_BREADCRUMB, + }); + }, + }); + + watcherESApp.disable(); - return boot({ - getLicenseStatus: () => this.licenseStatus, - element, - toasts: notifications.toasts, - http, - uiSettings, - docLinks, - chrome, - euiUtils: eui_utils, - savedObjects: savedObjects.client, - I18nContext: i18nDep.Context, - createTimeBuckets: () => new TimeBuckets(uiSettings, data), - MANAGEMENT_BREADCRUMB, - }); - }, - }); + // TODO: Fix the below dependency on `home` plugin inner workings + // Because the home feature catalogue does not have enable/disable functionality we pass + // the config in but keep a reference for enabling and disabling showing on home based on + // license updates. + const watcherHome = { + id: 'watcher', + title: 'Watcher', // This is a product name so we don't translate it. + category: FeatureCatalogueCategory.ADMIN, + description: i18n.translate('xpack.watcher.watcherDescription', { + defaultMessage: 'Detect changes in your data by creating, managing, and monitoring alerts.', + }), + icon: 'watchesApp', + path: '/app/kibana#/management/elasticsearch/watcher/watches', + showOnHomePage: true, + }; - home.featureCatalogue.register({ - id: 'watcher', - title: 'Watcher', // This is a product name so we don't translate it. - category: FeatureCatalogueCategory.ADMIN, - description: i18n.translate('xpack.watcher.watcherDescription', { - defaultMessage: - 'Detect changes in your data by creating, managing, and monitoring alerts.', - }), - icon: 'watchesApp', - path: '/app/kibana#/management/elasticsearch/watcher/watches', - showOnHomePage: true, - }); + home.featureCatalogue.register(watcherHome); - this.hasRegisteredESManagementSection = true; - } + licensing.license$.pipe(first(), map(licenseToLicenseStatus)).subscribe(({ valid }) => { + if (valid) { + watcherESApp.enable(); + watcherHome.showOnHomePage = true; + } else { + watcherESApp.disable(); + watcherHome.showOnHomePage = false; } }); } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 4fdfe0d32ace3..0276ca8109732 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -40,8 +40,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('alerts', function() { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const alertsTab = await testSubjects.find('alertsTab'); - await alertsTab.click(); + await testSubjects.click('alertsTab'); }); it('should search for alert', async () => { @@ -76,123 +75,100 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ]); }); - // Flaky until https://github.com/elastic/eui/issues/2612 fixed - it.skip('should disable single alert', async () => { + it('should disable single alert', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); - const enableSwitch = await testSubjects.find('enableSwitch'); - await enableSwitch.click(); + await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterDisable = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterDisable.click(); + await testSubjects.click('collapsedItemActions'); const enableSwitchAfterDisable = await testSubjects.find('enableSwitch'); const isChecked = await enableSwitchAfterDisable.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); }); - // Flaky until https://github.com/elastic/eui/issues/2612 fixed - it.skip('should re-enable single alert', async () => { + it('should re-enable single alert', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); - const enableSwitch = await testSubjects.find('enableSwitch'); - await enableSwitch.click(); + await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterDisable = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterDisable.click(); + await testSubjects.click('collapsedItemActions'); - const enableSwitchAfterDisable = await testSubjects.find('enableSwitch'); - await enableSwitchAfterDisable.click(); + await pageObjects.triggersActionsUI.toggleSwitch('enableSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterReEnable = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterReEnable.click(); + await testSubjects.click('collapsedItemActions'); const enableSwitchAfterReEnable = await testSubjects.find('enableSwitch'); const isChecked = await enableSwitchAfterReEnable.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); }); - // Flaky until https://github.com/elastic/eui/issues/2612 fixed - it.skip('should mute single alert', async () => { + it('should mute single alert', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); - const muteSwitch = await testSubjects.find('muteSwitch'); - await muteSwitch.click(); + await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterMute = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterMute.click(); + await testSubjects.click('collapsedItemActions'); const muteSwitchAfterMute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterMute.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); }); - // Flaky until https://github.com/elastic/eui/issues/2612 fixed - it.skip('should unmute single alert', async () => { + it('should unmute single alert', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); - const muteSwitch = await testSubjects.find('muteSwitch'); - await muteSwitch.click(); + await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterMute = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterMute.click(); + await testSubjects.click('collapsedItemActions'); - const muteSwitchAfterMute = await testSubjects.find('muteSwitch'); - await muteSwitchAfterMute.click(); + await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActionsAfterUnmute = await testSubjects.find('collapsedItemActions'); - await collapsedItemActionsAfterUnmute.click(); + await testSubjects.click('collapsedItemActions'); const muteSwitchAfterUnmute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterUnmute.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/53956 - it.skip('should delete single alert', async () => { + it('should delete single alert', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); - const deleteBtn = await testSubjects.find('deleteAlert'); - await deleteBtn.click(); + await testSubjects.click('deleteAlert'); - retry.try(async () => { + await retry.try(async () => { await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); @@ -200,140 +176,114 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/49830 - it.skip('should mute all selection', async () => { + it('should mute all selection', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const checkbox = await testSubjects.find(`checkboxSelectRow-${createdAlert.id}`); - await checkbox.click(); + await testSubjects.click(`checkboxSelectRow-${createdAlert.id}`); - const bulkActionBtn = await testSubjects.find('bulkAction'); - await bulkActionBtn.click(); + await testSubjects.click('bulkAction'); - const muteAllBtn = await testSubjects.find('muteAll'); - await muteAllBtn.click(); + await testSubjects.click('muteAll'); // Unmute all button shows after clicking mute all await testSubjects.existOrFail('unmuteAll'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/49830 - it.skip('should unmute all selection', async () => { + it('should unmute all selection', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const checkbox = await testSubjects.find(`checkboxSelectRow-${createdAlert.id}`); - await checkbox.click(); + await testSubjects.click(`checkboxSelectRow-${createdAlert.id}`); - const bulkActionBtn = await testSubjects.find('bulkAction'); - await bulkActionBtn.click(); + await testSubjects.click('bulkAction'); - const muteAllBtn = await testSubjects.find('muteAll'); - await muteAllBtn.click(); + await testSubjects.click('muteAll'); - const unmuteAllBtn = await testSubjects.find('unmuteAll'); - await unmuteAllBtn.click(); + await testSubjects.click('unmuteAll'); // Mute all button shows after clicking unmute all await testSubjects.existOrFail('muteAll'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/49830 - it.skip('should disable all selection', async () => { + it('should disable all selection', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const checkbox = await testSubjects.find(`checkboxSelectRow-${createdAlert.id}`); - await checkbox.click(); + await testSubjects.click(`checkboxSelectRow-${createdAlert.id}`); - const bulkActionBtn = await testSubjects.find('bulkAction'); - await bulkActionBtn.click(); + await testSubjects.click('bulkAction'); - const disableAllBtn = await testSubjects.find('disableAll'); - await disableAllBtn.click(); + await testSubjects.click('disableAll'); // Enable all button shows after clicking disable all await testSubjects.existOrFail('enableAll'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); const enableSwitch = await testSubjects.find('enableSwitch'); const isChecked = await enableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/49830 - it.skip('should enable all selection', async () => { + it('should enable all selection', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const checkbox = await testSubjects.find(`checkboxSelectRow-${createdAlert.id}`); - await checkbox.click(); + await testSubjects.click(`checkboxSelectRow-${createdAlert.id}`); - const bulkActionBtn = await testSubjects.find('bulkAction'); - await bulkActionBtn.click(); + await testSubjects.click('bulkAction'); - const disableAllBtn = await testSubjects.find('disableAll'); - await disableAllBtn.click(); + await testSubjects.click('disableAll'); - const enableAllBtn = await testSubjects.find('enableAll'); - await enableAllBtn.click(); + await testSubjects.click('enableAll'); // Disable all button shows after clicking enable all await testSubjects.existOrFail('disableAll'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const collapsedItemActions = await testSubjects.find('collapsedItemActions'); - await collapsedItemActions.click(); + await testSubjects.click('collapsedItemActions'); const enableSwitch = await testSubjects.find('enableSwitch'); const isChecked = await enableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); }); - // Flaky, will be fixed with https://github.com/elastic/kibana/issues/53956 - it.skip('should delete all selection', async () => { + it('should delete all selection', async () => { const createdAlert = await createAlert(); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - const checkbox = await testSubjects.find(`checkboxSelectRow-${createdAlert.id}`); - await checkbox.click(); + await testSubjects.click(`checkboxSelectRow-${createdAlert.id}`); - const bulkActionBtn = await testSubjects.find('bulkAction'); - await bulkActionBtn.click(); + await testSubjects.click('bulkAction'); - const deleteAllBtn = await testSubjects.find('deleteAll'); - await deleteAllBtn.click(); + await testSubjects.click('deleteAll'); - retry.try(async () => { + await retry.try(async () => { await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index 7b60685225ac6..d037155a29e12 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -20,8 +20,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Connectors', function() { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const alertsTab = await testSubjects.find('connectorsTab'); - await alertsTab.click(); + await testSubjects.click('connectorsTab'); }); it('should create a connector', async () => { @@ -29,18 +28,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - const serverLogCard = await testSubjects.find('.server-log-card'); - await serverLogCard.click(); + await testSubjects.click('.server-log-card'); const nameInput = await testSubjects.find('nameInput'); await nameInput.click(); await nameInput.clearValue(); await nameInput.type(connectorName); - const saveButton = await find.byCssSelector( - '[data-test-subj="saveActionButton"]:not(disabled)' - ); - await saveButton.click(); + await find.clickByCssSelector('[data-test-subj="saveActionButton"]:not(disabled)'); const toastTitle = await pageObjects.common.closeToast(); expect(toastTitle).to.eql(`Created '${connectorName}'`); @@ -63,18 +58,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - const serverLogCard = await testSubjects.find('.server-log-card'); - await serverLogCard.click(); + await testSubjects.click('.server-log-card'); const nameInput = await testSubjects.find('nameInput'); await nameInput.click(); await nameInput.clearValue(); await nameInput.type(connectorName); - const saveButton = await find.byCssSelector( - '[data-test-subj="saveActionButton"]:not(disabled)' - ); - await saveButton.click(); + await find.clickByCssSelector('[data-test-subj="saveActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); @@ -83,20 +74,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const searchResultsBeforeEdit = await pageObjects.triggersActionsUI.getConnectorsList(); expect(searchResultsBeforeEdit.length).to.eql(1); - const editConnectorBtn = await find.byCssSelector( - '[data-test-subj="connectorsTableCell-name"] button' - ); - await editConnectorBtn.click(); + await find.clickByCssSelector('[data-test-subj="connectorsTableCell-name"] button'); const nameInputToUpdate = await testSubjects.find('nameInput'); await nameInputToUpdate.click(); await nameInputToUpdate.clearValue(); await nameInputToUpdate.type(updatedConnectorName); - const saveEditButton = await find.byCssSelector( - '[data-test-subj="saveActionButton"]:not(disabled)' - ); - await saveEditButton.click(); + await find.clickByCssSelector('[data-test-subj="saveActionButton"]:not(disabled)'); const toastTitle = await pageObjects.common.closeToast(); expect(toastTitle).to.eql(`Updated '${updatedConnectorName}'`); @@ -117,18 +102,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createConnector(connectorName: string) { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - const serverLogCard = await testSubjects.find('.server-log-card'); - await serverLogCard.click(); + await testSubjects.click('.server-log-card'); const nameInput = await testSubjects.find('nameInput'); await nameInput.click(); await nameInput.clearValue(); await nameInput.type(connectorName); - const saveButton = await find.byCssSelector( - '[data-test-subj="saveActionButton"]:not(disabled)' - ); - await saveButton.click(); + await find.clickByCssSelector('[data-test-subj="saveActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); } const connectorName = generateUniqueKey(); @@ -141,12 +122,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const searchResultsBeforeDelete = await pageObjects.triggersActionsUI.getConnectorsList(); expect(searchResultsBeforeDelete.length).to.eql(1); - const deleteConnectorBtn = await testSubjects.find('deleteConnector'); - await deleteConnectorBtn.click(); + await testSubjects.click('deleteConnector'); await testSubjects.existOrFail('deleteConnectorsConfirmation'); await testSubjects.click('deleteConnectorsConfirmation > confirmModalConfirmButton'); await testSubjects.missingOrFail('deleteConnectorsConfirmation'); + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql('Deleted 1 connector'); + await pageObjects.triggersActionsUI.searchConnectors(connectorName); const searchResultsAfterDelete = await pageObjects.triggersActionsUI.getConnectorsList(); @@ -157,18 +140,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createConnector(connectorName: string) { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - const serverLogCard = await testSubjects.find('.server-log-card'); - await serverLogCard.click(); + await testSubjects.click('.server-log-card'); const nameInput = await testSubjects.find('nameInput'); await nameInput.click(); await nameInput.clearValue(); await nameInput.type(connectorName); - const saveButton = await find.byCssSelector( - '[data-test-subj="saveActionButton"]:not(disabled)' - ); - await saveButton.click(); + await find.clickByCssSelector('[data-test-subj="saveActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); } @@ -182,17 +161,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const searchResultsBeforeDelete = await pageObjects.triggersActionsUI.getConnectorsList(); expect(searchResultsBeforeDelete.length).to.eql(1); - const deleteCheckbox = await find.byCssSelector( - '.euiTableRowCellCheckbox .euiCheckbox__input' - ); - await deleteCheckbox.click(); + await find.clickByCssSelector('.euiTableRowCellCheckbox .euiCheckbox__input'); - const bulkDeleteBtn = await testSubjects.find('bulkDelete'); - await bulkDeleteBtn.click(); + await testSubjects.click('bulkDelete'); await testSubjects.existOrFail('deleteConnectorsConfirmation'); await testSubjects.click('deleteConnectorsConfirmation > confirmModalConfirmButton'); await testSubjects.missingOrFail('deleteConnectorsConfirmation'); + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql('Deleted 1 connector'); + await pageObjects.triggersActionsUI.searchConnectors(connectorName); const searchResultsAfterDelete = await pageObjects.triggersActionsUI.getConnectorsList(); diff --git a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts index ce68109771487..a04ecc969a7e1 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts @@ -4,12 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; const ENTER_KEY = '\uE007'; export function TriggersActionsPageProvider({ getService }: FtrProviderContext) { const find = getService('find'); + const retry = getService('retry'); const testSubjects = getService('testSubjects'); return { @@ -93,5 +95,15 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) async changeTabs(tab: 'alertsTab' | 'connectorsTab') { return await testSubjects.click(tab); }, + async toggleSwitch(testSubject: string) { + const switchBtn = await testSubjects.find(testSubject); + const valueBefore = await switchBtn.getAttribute('aria-checked'); + await switchBtn.click(); + await retry.try(async () => { + const switchBtnAfter = await testSubjects.find(testSubject); + const valueAfter = await switchBtnAfter.getAttribute('aria-checked'); + expect(valueAfter).not.to.eql(valueBefore); + }); + }, }; }