diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc
index aaceec07701cbd..e6e426e7a86239 100644
--- a/docs/CHANGELOG.asciidoc
+++ b/docs/CHANGELOG.asciidoc
@@ -54,7 +54,19 @@ Review important information about the {kib} 8.x releases.
[[release-notes-8.10.3]]
== {kib} 8.10.3
-The 8.10.3 release includes the following bug fixes.
+[float]
+[[security-update-8.10.3]]
+=== Security updates
+
+* **Kibana heap buffer overflow vulnerability**
++
+On Sept 11, 2023, Google Chrome announced CVE-2023-4863, described as “Heap buffer overflow in libwebp in Google Chrome prior to 116.0.5845.187 and libwebp 1.3.2 allowed a remote attacker to perform an out of bounds memory write via a crafted HTML page”. Kibana includes a bundled version of headless Chromium that is only used for Kibana’s reporting capabilities and which is affected by this vulnerability. An exploit for Kibana has not been identified, however as a resolution, the bundled version of Chromium is updated in this release.
++
+The issue is resolved in 8.10.3.
++
+For more information, see our related
+https://discuss.elastic.co/t/kibana-8-10-3-7-17-14-security-update/344735[security
+announcement].
[float]
[[enhancement-v8.10.3]]
diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts
index 9784908948f438..074e0f1c921c67 100644
--- a/packages/kbn-doc-links/src/get_doc_links.ts
+++ b/packages/kbn-doc-links/src/get_doc_links.ts
@@ -824,6 +824,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
elasticsearch: `${SEARCH_UI_DOCS}tutorials/elasticsearch`,
},
serverlessClients: {
+ clientLib: `${SERVERLESS_ELASTICSEARCH_DOCS}clients`,
goApiReference: `${SERVERLESS_ELASTICSEARCH_DOCS}go-client-getting-started`,
goGettingStarted: `${SERVERLESS_ELASTICSEARCH_DOCS}go-client-getting-started`,
httpApis: `${SERVERLESS_ELASTICSEARCH_DOCS}http-apis`,
@@ -847,6 +848,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
gettingStartedIngest: `${SERVERLESS_ELASTICSEARCH_DOCS}get-started#ingest`,
gettingStartedSearch: `${SERVERLESS_ELASTICSEARCH_DOCS}get-started#search`,
},
+ serverlessSecurity: {
+ apiKeyPrivileges: `${SERVERLESS_DOCS}api-keys#restrict-privileges`,
+ },
synthetics: {
featureRoles: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/synthetics-feature-roles.html`,
},
diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts
index ebb4fb07fe21f0..7a61d9f3dd30e5 100644
--- a/packages/kbn-doc-links/src/types.ts
+++ b/packages/kbn-doc-links/src/types.ts
@@ -581,6 +581,7 @@ export interface DocLinks {
readonly elasticsearch: string;
};
readonly serverlessClients: {
+ readonly clientLib: string;
readonly goApiReference: string;
readonly goGettingStarted: string;
readonly httpApis: string;
@@ -604,6 +605,9 @@ export interface DocLinks {
readonly integrationsConnectorClient: string;
readonly integrationsLogstash: string;
};
+ readonly serverlessSecurity: {
+ readonly apiKeyPrivileges: string;
+ };
readonly synthetics: {
readonly featureRoles: string;
};
diff --git a/packages/kbn-unified-data-table/src/components/data_table.tsx b/packages/kbn-unified-data-table/src/components/data_table.tsx
index aa61ecdd1bfe66..a1540e88a5cd6f 100644
--- a/packages/kbn-unified-data-table/src/components/data_table.tsx
+++ b/packages/kbn-unified-data-table/src/components/data_table.tsx
@@ -538,6 +538,7 @@ export const UnifiedDataTable = ({
fieldFormats: services.fieldFormats,
maxEntries: maxDocFieldsDisplayed,
externalCustomRenderers,
+ isPlainRecord,
}),
[
dataView,
@@ -547,6 +548,7 @@ export const UnifiedDataTable = ({
maxDocFieldsDisplayed,
services.fieldFormats,
externalCustomRenderers,
+ isPlainRecord,
]
);
diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx
index d522ae70d28b52..7db7ffedfdecf4 100644
--- a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx
+++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx
@@ -75,6 +75,18 @@ const rowsSource: EsHitRecord[] = [
},
];
+const rowsSourceWithEmptyValues: EsHitRecord[] = [
+ {
+ _id: '1',
+ _index: 'test',
+ _score: 1,
+ _source: { bytes: 100, extension: null },
+ highlight: {
+ extension: ['@kibana-highlighted-field.gz@/kibana-highlighted-field'],
+ },
+ },
+];
+
const rowsFields: EsHitRecord[] = [
{
_id: '1',
@@ -344,6 +356,77 @@ describe('Unified data table cell rendering', function () {
`);
});
+ it('renders _source column correctly if on text based mode and have nulls', () => {
+ const DataTableCellValue = getRenderCellValueFn({
+ dataView: dataViewMock,
+ rows: rowsSourceWithEmptyValues.map(build),
+ useNewFieldsApi: false,
+ shouldShowFieldHandler: (fieldName) => ['extension', 'bytes'].includes(fieldName),
+ closePopover: jest.fn(),
+ fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
+ maxEntries: 100,
+ isPlainRecord: true,
+ });
+ const component = shallow(
+
+ );
+ expect(component).toMatchInlineSnapshot(`
+
+
+ bytesDisplayName
+
+
+
+ _index
+
+
+
+ _score
+
+
+
+ `);
+ });
+
it('renders fields-based column correctly', () => {
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx
index 2f9a3dd92b575a..581230b5257572 100644
--- a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx
+++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx
@@ -43,6 +43,7 @@ export const getRenderCellValueFn = ({
fieldFormats,
maxEntries,
externalCustomRenderers,
+ isPlainRecord,
}: {
dataView: DataView;
rows: DataTableRecord[] | undefined;
@@ -55,6 +56,7 @@ export const getRenderCellValueFn = ({
string,
(props: EuiDataGridCellValueElementProps) => React.ReactNode
>;
+ isPlainRecord?: boolean;
}) => {
return ({
rowIndex,
@@ -144,17 +146,22 @@ export const getRenderCellValueFn = ({
compressed
className={classnames('unifiedDataTable__descriptionList', CELL_CLASS)}
>
- {pairs.map(([fieldDisplayName, value]) => (
-
-
- {fieldDisplayName}
-
-
-
- ))}
+ {pairs.map(([fieldDisplayName, value, fieldName]) => {
+ // temporary solution for text based mode. As there are a lot of unsupported fields we want to
+ // hide the empty one from the Document view
+ if (isPlainRecord && fieldName && row.flattened[fieldName] === null) return null;
+ return (
+
+
+ {fieldDisplayName}
+
+
+
+ );
+ })}
);
}
diff --git a/test/functional/apps/visualize/group3/_annotation_listing.ts b/test/functional/apps/visualize/group3/_annotation_listing.ts
index cdb0cb615be472..5d33f714159f0c 100644
--- a/test/functional/apps/visualize/group3/_annotation_listing.ts
+++ b/test/functional/apps/visualize/group3/_annotation_listing.ts
@@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['visualize', 'annotationEditor']);
const listingTable = getService('listingTable');
const kibanaServer = getService('kibanaServer');
+ const testSubjects = getService('testSubjects');
const find = getService('find');
const retry = getService('retry');
const log = getService('log');
@@ -32,6 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visualize.gotoVisualizationLandingPage();
await PageObjects.visualize.selectAnnotationsTab();
+ await listingTable.waitUntilTableIsLoaded();
});
after(async function () {
@@ -156,7 +158,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
- describe.skip('data view switching', () => {
+ describe('data view switching', () => {
it('recovers from missing data view', async () => {
await listingTable.clickItemLink('eventAnnotation', 'missing data view');
@@ -175,7 +177,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.annotationEditor.saveGroup();
});
- it('recovers from missing field in data view', () => {});
+ it('recovers from missing field in data view', async () => {
+ const assertShowingMissingFieldError = async (yes: boolean) => {
+ const [failureExists, canvasExists] = await Promise.all([
+ testSubjects.exists('embeddable-lens-failure'),
+ find.existsByCssSelector('canvas', 1000),
+ ]);
+ expect(failureExists).to.be(yes);
+ expect(canvasExists).to.be(!yes);
+ };
+
+ await listingTable.clickItemLink('eventAnnotation', 'Group with additional fields');
+
+ await assertShowingMissingFieldError(false);
+
+ await retry.try(async () => {
+ await PageObjects.annotationEditor.editGroupMetadata({
+ dataView: 'Data view without fields',
+ });
+
+ await assertShowingMissingFieldError(true);
+ });
+
+ await retry.try(async () => {
+ await PageObjects.annotationEditor.editGroupMetadata({
+ dataView: 'logs*',
+ });
+
+ await assertShowingMissingFieldError(false);
+ });
+ });
});
});
});
diff --git a/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json b/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json
index 34f4fb8ed1b48c..a54a042effb6ec 100644
--- a/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json
+++ b/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json
@@ -21,6 +21,29 @@
"version": "WzIyNywxXQ=="
}
+{
+ "attributes": {
+ "fieldAttrs": "{}",
+ "fieldFormatMap": "{}",
+ "fields": "[]",
+ "name": "Data view without fields",
+ "runtimeFieldMap": "{}",
+ "sourceFilters": "[]",
+ "timeFieldName": "timestamp",
+ "title": "kibana_sample_data_logs",
+ "typeMeta": "{}"
+ },
+ "coreMigrationVersion": "8.8.0",
+ "created_at": "2023-09-07T17:23:20.906Z",
+ "id": "data-view-without-fields",
+ "managed": false,
+ "references": [],
+ "type": "index-pattern",
+ "typeMigrationVersion": "8.0.0",
+ "updated_at": "2023-09-11T15:50:59.227Z",
+ "version": "WzIyNywxXQ=="
+}
+
{
"attributes": {
"fieldAttrs": "{}",
@@ -44,6 +67,44 @@
"version": "WzIyNywxXQ=="
}
+{
+ "attributes": {
+ "annotations": [
+ {
+ "extraFields": [
+ "@message.raw"
+ ],
+ "icon": "triangle",
+ "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843",
+ "key": {
+ "type": "point_in_time"
+ },
+ "label": "Event",
+ "timeField": "@timestamp",
+ "type": "query"
+ }
+ ],
+ "dataViewSpec": null,
+ "description": "",
+ "ignoreGlobalFilters": true,
+ "title": "Group with additional fields"
+ },
+ "coreMigrationVersion": "8.8.0",
+ "created_at": "2023-10-06T17:15:58.790Z",
+ "id": "12371e00-5174-11ee-a5c4-7dce2e3293a7",
+ "managed": false,
+ "references": [
+ {
+ "id": "90943e30-9a47-11e8-b64d-95841ca0b247",
+ "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247",
+ "type": "index-pattern"
+ }
+ ],
+ "type": "event-annotation-group",
+ "updated_at": "2023-10-06T17:17:05.384Z",
+ "version": "WzE4MywxXQ=="
+}
+
{
"attributes": {
"annotations": [
diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts
index d675b1b42bb361..3a5df3768ae96b 100644
--- a/x-pack/plugins/fleet/common/constants/routes.ts
+++ b/x-pack/plugins/fleet/common/constants/routes.ts
@@ -41,6 +41,7 @@ export const EPM_API_ROUTES = {
VERIFICATION_KEY_ID: `${EPM_API_ROOT}/verification_key_id`,
STATS_PATTERN: `${EPM_PACKAGES_MANY}/{pkgName}/stats`,
BULK_ASSETS_PATTERN: `${EPM_API_ROOT}/bulk_assets`,
+ INPUTS_PATTERN: `${EPM_API_ROOT}/templates/{pkgName}/{pkgVersion}/inputs`,
INFO_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json
index a4604a7d7427b0..f87ab5a3edacc3 100644
--- a/x-pack/plugins/fleet/common/openapi/bundled.json
+++ b/x-pack/plugins/fleet/common/openapi/bundled.json
@@ -1420,6 +1420,56 @@
}
]
},
+ "/epm/templates/{pkgName}/{pkgVersion}/inputs": {
+ "get": {
+ "summary": "Get inputs template",
+ "tags": [
+ "Elastic Package Manager (EPM)"
+ ],
+ "responses": {
+ "400": {
+ "$ref": "#/components/responses/error"
+ }
+ },
+ "operationId": "get-inputs-template",
+ "security": [
+ {
+ "basicAuth": []
+ }
+ ]
+ },
+ "parameters": [
+ {
+ "schema": {
+ "type": "string"
+ },
+ "name": "pkgName",
+ "in": "path",
+ "required": true
+ },
+ {
+ "schema": {
+ "type": "string"
+ },
+ "name": "pkgVersion",
+ "in": "path",
+ "required": true
+ },
+ {
+ "schema": {
+ "type": "string",
+ "enum": [
+ "json",
+ "yaml",
+ "yml"
+ ]
+ },
+ "name": "format",
+ "description": "Format of response - json or yaml",
+ "in": "query"
+ }
+ ]
+ },
"/agents/setup": {
"get": {
"summary": "Get agent setup info",
diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml
index be132c9f19e488..4629a95af27e80 100644
--- a/x-pack/plugins/fleet/common/openapi/bundled.yaml
+++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml
@@ -894,6 +894,37 @@ paths:
name: pkgName
in: path
required: true
+ /epm/templates/{pkgName}/{pkgVersion}/inputs:
+ get:
+ summary: Get inputs template
+ tags:
+ - Elastic Package Manager (EPM)
+ responses:
+ '400':
+ $ref: '#/components/responses/error'
+ operationId: get-inputs-template
+ security:
+ - basicAuth: []
+ parameters:
+ - schema:
+ type: string
+ name: pkgName
+ in: path
+ required: true
+ - schema:
+ type: string
+ name: pkgVersion
+ in: path
+ required: true
+ - schema:
+ type: string
+ enum:
+ - json
+ - yaml
+ - yml
+ name: format
+ description: Format of response - json or yaml
+ in: query
/agents/setup:
get:
summary: Get agent setup info
diff --git a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml
index b8a7e024f3c4e3..92bffe4968092f 100644
--- a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml
+++ b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml
@@ -46,6 +46,8 @@ paths:
$ref: paths/epm@get_file.yaml
'/epm/packages/{pkgName}/stats':
$ref: 'paths/epm@packages@{pkg_name}@stats.yaml'
+ '/epm/templates/{pkgName}/{pkgVersion}/inputs':
+ $ref: 'paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml'
# Agent endpoints
/agents/setup:
diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml
new file mode 100644
index 00000000000000..9888fd0036ea17
--- /dev/null
+++ b/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml
@@ -0,0 +1,30 @@
+get:
+ summary: Get inputs template
+ tags:
+ - Elastic Package Manager (EPM)
+ responses:
+ '400':
+ $ref: ../components/responses/error.yaml
+ operationId: get-inputs-template
+ security:
+ - basicAuth: []
+parameters:
+ - schema:
+ type: string
+ name: pkgName
+ in: path
+ required: true
+ - schema:
+ type: string
+ name: pkgVersion
+ in: path
+ required: true
+ - schema:
+ type: string
+ enum:
+ - json
+ - yaml
+ - yml
+ name: format
+ description: 'Format of response - json or yaml'
+ in: query
diff --git a/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts b/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts
index 30389ee45cbde3..18d995c96f2b8a 100644
--- a/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts
+++ b/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts
@@ -39,7 +39,7 @@ export const fullAgentPolicyToYaml = (policy: FullAgentPolicy, toYaml: typeof sa
return _formatSecrets(policy.secret_references, yaml);
};
-function _sortYamlKeys(keyA: string, keyB: string) {
+export function _sortYamlKeys(keyA: string, keyB: string) {
const indexA = POLICY_KEYS_ORDER.indexOf(keyA);
const indexB = POLICY_KEYS_ORDER.indexOf(keyB);
if (indexA >= 0 && indexB < 0) {
diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts
index 5781fa623bb9b2..8ce1bf7006f6b3 100644
--- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts
+++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts
@@ -53,6 +53,7 @@ import type {
GetLimitedPackagesRequestSchema,
GetBulkAssetsRequestSchema,
CreateCustomIntegrationRequestSchema,
+ GetInputsRequestSchema,
} from '../../types';
import {
bulkInstallPackages,
@@ -67,6 +68,7 @@ import {
getLimitedPackages,
getInstallation,
getBulkAssets,
+ getTemplateInputs,
} from '../../services/epm/packages';
import type { BulkInstallResponse } from '../../services/epm/packages';
import { defaultFleetErrorHandler, fleetErrorToResponseOptions, FleetError } from '../../errors';
@@ -650,6 +652,28 @@ export const reauthorizeTransformsHandler: FleetRequestHandler<
}
};
+export const getInputsHandler: FleetRequestHandler<
+ TypeOf,
+ TypeOf,
+ undefined
+> = async (context, request, response) => {
+ const soClient = (await context.fleet).internalSoClient;
+
+ try {
+ const { pkgName, pkgVersion } = request.params;
+ const { format } = request.query;
+ let body;
+ if (format === 'json') {
+ body = await getTemplateInputs(soClient, pkgName, pkgVersion, 'json');
+ } else if (format === 'yml' || format === 'yaml') {
+ body = await getTemplateInputs(soClient, pkgName, pkgVersion, 'yml');
+ }
+ return response.ok({ body });
+ } catch (error) {
+ return defaultFleetErrorHandler({ error, response });
+ }
+};
+
// Don't expose the whole SO in the API response, only selected fields
const soToInstallationInfo = (pkg: PackageListItem | PackageInfo) => {
if ('savedObject' in pkg && pkg.savedObject?.attributes) {
diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts
index 4f354ae77d7f07..6e0000bf4ccbf6 100644
--- a/x-pack/plugins/fleet/server/routes/epm/index.ts
+++ b/x-pack/plugins/fleet/server/routes/epm/index.ts
@@ -47,6 +47,7 @@ import {
ReauthorizeTransformRequestSchema,
GetDataStreamsRequestSchema,
CreateCustomIntegrationRequestSchema,
+ GetInputsRequestSchema,
} from '../../types';
import {
@@ -67,6 +68,7 @@ import {
reauthorizeTransformsHandler,
getDataStreamsHandler,
createCustomIntegrationHandler,
+ getInputsHandler,
} from './handlers';
const MAX_FILE_SIZE_BYTES = 104857600; // 100MB
@@ -145,6 +147,19 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
getStatsHandler
);
+ router.versioned
+ .get({
+ path: EPM_API_ROUTES.INPUTS_PATTERN,
+ fleetAuthz: READ_PACKAGE_INFO_AUTHZ,
+ })
+ .addVersion(
+ {
+ version: API_VERSIONS.public.v1,
+ validate: { request: GetInputsRequestSchema },
+ },
+ getInputsHandler
+ );
+
router.versioned
.get({
path: EPM_API_ROUTES.FILEPATH_PATTERN,
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_template_inputs.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_template_inputs.ts
new file mode 100644
index 00000000000000..04c65535ad0ad8
--- /dev/null
+++ b/x-pack/plugins/fleet/server/services/epm/packages/get_template_inputs.ts
@@ -0,0 +1,122 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import type { SavedObjectsClientContract } from '@kbn/core/server';
+
+import { merge } from 'lodash';
+import { safeDump } from 'js-yaml';
+
+import { packageToPackagePolicy } from '../../../../common/services/package_to_package_policy';
+import { getInputsWithStreamIds, _compilePackagePolicyInputs } from '../../package_policy';
+
+import type {
+ PackageInfo,
+ NewPackagePolicy,
+ PackagePolicyInput,
+ FullAgentPolicyInput,
+ FullAgentPolicyInputStream,
+} from '../../../../common/types';
+import { _sortYamlKeys } from '../../../../common/services/full_agent_policy_to_yaml';
+
+import { getPackageInfo } from '.';
+
+type Format = 'yml' | 'json';
+
+// Function based off storedPackagePolicyToAgentInputs, it only creates the `streams` section instead of the FullAgentPolicyInput
+export const templatePackagePolicyToFullInputs = (
+ packagePolicyInputs: PackagePolicyInput[]
+): FullAgentPolicyInput[] => {
+ const fullInputs: FullAgentPolicyInput[] = [];
+
+ if (!packagePolicyInputs || packagePolicyInputs.length === 0) return fullInputs;
+
+ packagePolicyInputs.forEach((input) => {
+ const fullInput = {
+ ...(input.compiled_input || {}),
+ ...(input.streams.length
+ ? {
+ streams: input.streams.map((stream) => {
+ const fullStream: FullAgentPolicyInputStream = {
+ id: stream.id,
+ type: input.type,
+ data_stream: stream.data_stream,
+ ...stream.compiled_stream,
+ ...Object.entries(stream.config || {}).reduce((acc, [key, { value }]) => {
+ acc[key] = value;
+ return acc;
+ }, {} as { [k: string]: any }),
+ };
+ return fullStream;
+ }),
+ }
+ : {}),
+ };
+
+ // deeply merge the input.config values with the full policy input
+ merge(
+ fullInput,
+ Object.entries(input.config || {}).reduce((acc, [key, { value }]) => {
+ acc[key] = value;
+ return acc;
+ }, {} as Record)
+ );
+ fullInputs.push(fullInput);
+ });
+
+ return fullInputs;
+};
+
+export async function getTemplateInputs(
+ soClient: SavedObjectsClientContract,
+ pkgName: string,
+ pkgVersion: string,
+ format: Format
+) {
+ const packageInfoMap = new Map();
+ let packageInfo: PackageInfo;
+
+ if (packageInfoMap.has(pkgName)) {
+ packageInfo = packageInfoMap.get(pkgName)!;
+ } else {
+ packageInfo = await getPackageInfo({
+ savedObjectsClient: soClient,
+ pkgName,
+ pkgVersion,
+ });
+ }
+ const emptyPackagePolicy = packageToPackagePolicy(packageInfo, '');
+ const inputsWithStreamIds = getInputsWithStreamIds(emptyPackagePolicy, undefined, true);
+
+ const compiledInputs = await _compilePackagePolicyInputs(
+ packageInfo,
+ emptyPackagePolicy.vars || {},
+ inputsWithStreamIds
+ );
+ const packagePolicyWithInputs: NewPackagePolicy = {
+ ...emptyPackagePolicy,
+ inputs: compiledInputs,
+ };
+ const fullAgentPolicyInputs = templatePackagePolicyToFullInputs(
+ packagePolicyWithInputs.inputs as PackagePolicyInput[]
+ );
+ // @ts-ignore-next-line The return type is any because in some case we can have compiled_input instead of input.streams
+ // we don't know what it is. An example is integration APM
+ const inputs: any = fullAgentPolicyInputs.flatMap((input) => input?.streams || input);
+
+ if (format === 'json') {
+ return { inputs };
+ } else if (format === 'yml') {
+ const yaml = safeDump(
+ { inputs },
+ {
+ skipInvalid: true,
+ sortKeys: _sortYamlKeys,
+ }
+ );
+ return yaml;
+ }
+ return { inputs: [] };
+}
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_templates_inputs.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_templates_inputs.test.ts
new file mode 100644
index 00000000000000..e9912cc8d8bd2c
--- /dev/null
+++ b/x-pack/plugins/fleet/server/services/epm/packages/get_templates_inputs.test.ts
@@ -0,0 +1,303 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { PackagePolicyInput } from '../../../../common/types';
+
+import { templatePackagePolicyToFullInputs } from './get_template_inputs';
+
+const packageInfoCache = new Map();
+packageInfoCache.set('mock_package-0.0.0', {
+ name: 'mock_package',
+ version: '0.0.0',
+ policy_templates: [
+ {
+ multiple: true,
+ },
+ ],
+});
+packageInfoCache.set('limited_package-0.0.0', {
+ name: 'limited_package',
+ version: '0.0.0',
+ policy_templates: [
+ {
+ multiple: false,
+ },
+ ],
+});
+
+describe('Fleet - templatePackagePolicyToFullInputs', () => {
+ const mockInput: PackagePolicyInput = {
+ type: 'test-logs',
+ enabled: true,
+ vars: {
+ inputVar: { value: 'input-value' },
+ inputVar2: { value: undefined },
+ inputVar3: {
+ type: 'yaml',
+ value: 'testField: test',
+ },
+ inputVar4: { value: '' },
+ },
+ streams: [
+ {
+ id: 'test-logs-foo',
+ enabled: true,
+ data_stream: { dataset: 'foo', type: 'logs' },
+ vars: {
+ fooVar: { value: 'foo-value' },
+ fooVar2: { value: [1, 2] },
+ },
+ compiled_stream: {
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ },
+ },
+ {
+ id: 'test-logs-bar',
+ enabled: true,
+ data_stream: { dataset: 'bar', type: 'logs' },
+ vars: {
+ barVar: { value: 'bar-value' },
+ barVar2: { value: [1, 2] },
+ barVar3: {
+ type: 'yaml',
+ value:
+ '- namespace: mockNamespace\n #disabledProp: ["test"]\n anotherProp: test\n- namespace: mockNamespace2\n #disabledProp: ["test2"]\n anotherProp: test2',
+ },
+ barVar4: {
+ type: 'yaml',
+ value: '',
+ },
+ barVar5: {
+ type: 'yaml',
+ value: 'testField: test\n invalidSpacing: foo',
+ },
+ },
+ },
+ ],
+ };
+
+ const mockInput2: PackagePolicyInput = {
+ type: 'test-metrics',
+ policy_template: 'some-template',
+ enabled: true,
+ vars: {
+ inputVar: { value: 'input-value' },
+ inputVar2: { value: undefined },
+ inputVar3: {
+ type: 'yaml',
+ value: 'testField: test',
+ },
+ inputVar4: { value: '' },
+ },
+ streams: [
+ {
+ id: 'test-metrics-foo',
+ enabled: true,
+ data_stream: { dataset: 'foo', type: 'metrics' },
+ vars: {
+ fooVar: { value: 'foo-value' },
+ fooVar2: { value: [1, 2] },
+ },
+ compiled_stream: {
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ },
+ },
+ ],
+ };
+
+ it('returns no inputs for package policy with no inputs', async () => {
+ expect(await templatePackagePolicyToFullInputs([])).toEqual([]);
+ });
+
+ it('returns inputs even when inputs where disabled', async () => {
+ expect(await templatePackagePolicyToFullInputs([{ ...mockInput, enabled: false }])).toEqual([
+ {
+ streams: [
+ {
+ data_stream: {
+ dataset: 'foo',
+ type: 'logs',
+ },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ id: 'test-logs-foo',
+ type: 'test-logs',
+ },
+ {
+ data_stream: {
+ dataset: 'bar',
+ type: 'logs',
+ },
+ id: 'test-logs-bar',
+ type: 'test-logs',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('returns agent inputs with streams', async () => {
+ expect(await templatePackagePolicyToFullInputs([mockInput])).toEqual([
+ {
+ streams: [
+ {
+ id: 'test-logs-foo',
+ data_stream: { dataset: 'foo', type: 'logs' },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ type: 'test-logs',
+ },
+ {
+ id: 'test-logs-bar',
+ data_stream: { dataset: 'bar', type: 'logs' },
+ type: 'test-logs',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('returns unique agent inputs IDs, with policy template name if one exists for non-limited packages', async () => {
+ expect(await templatePackagePolicyToFullInputs([mockInput])).toEqual([
+ {
+ streams: [
+ {
+ id: 'test-logs-foo',
+ type: 'test-logs',
+ data_stream: { dataset: 'foo', type: 'logs' },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ },
+ {
+ id: 'test-logs-bar',
+ data_stream: { dataset: 'bar', type: 'logs' },
+ type: 'test-logs',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('returns agent inputs without streams', async () => {
+ expect(await templatePackagePolicyToFullInputs([mockInput2])).toEqual([
+ {
+ streams: [
+ {
+ data_stream: {
+ dataset: 'foo',
+ type: 'metrics',
+ },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ id: 'test-metrics-foo',
+ type: 'test-metrics',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('returns agent inputs without disabled streams', async () => {
+ expect(
+ await templatePackagePolicyToFullInputs([
+ {
+ ...mockInput,
+ streams: [{ ...mockInput.streams[0] }, { ...mockInput.streams[1], enabled: false }],
+ },
+ ])
+ ).toEqual([
+ {
+ streams: [
+ {
+ id: 'test-logs-foo',
+ type: 'test-logs',
+ data_stream: { dataset: 'foo', type: 'logs' },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ },
+ {
+ data_stream: {
+ dataset: 'bar',
+ type: 'logs',
+ },
+ id: 'test-logs-bar',
+ type: 'test-logs',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('returns agent inputs with deeply merged config values', async () => {
+ expect(
+ await templatePackagePolicyToFullInputs([
+ {
+ ...mockInput,
+ compiled_input: {
+ agent_input_template_group1_vars: {
+ inputVar: 'input-value',
+ },
+ agent_input_template_group2_vars: {
+ inputVar3: {
+ testFieldGroup: {
+ subField1: 'subfield1',
+ },
+ testField: 'test',
+ },
+ },
+ },
+ config: {
+ agent_input_template_group1_vars: {
+ value: {
+ inputVar2: {},
+ },
+ },
+ agent_input_template_group2_vars: {
+ value: {
+ inputVar3: {
+ testFieldGroup: {
+ subField2: 'subfield2',
+ },
+ },
+ inputVar4: '',
+ },
+ },
+ },
+ },
+ ])
+ ).toEqual([
+ {
+ agent_input_template_group1_vars: {
+ inputVar: 'input-value',
+ inputVar2: {},
+ },
+ agent_input_template_group2_vars: {
+ inputVar3: {
+ testField: 'test',
+ testFieldGroup: {
+ subField1: 'subfield1',
+ subField2: 'subfield2',
+ },
+ },
+ inputVar4: '',
+ },
+ streams: [
+ {
+ id: 'test-logs-foo',
+ data_stream: { dataset: 'foo', type: 'logs' },
+ fooKey: 'fooValue1',
+ fooKey2: ['fooValue2'],
+ type: 'test-logs',
+ },
+ { id: 'test-logs-bar', data_stream: { dataset: 'bar', type: 'logs' }, type: 'test-logs' },
+ ],
+ },
+ ]);
+ });
+});
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/index.ts b/x-pack/plugins/fleet/server/services/epm/packages/index.ts
index 5895d4a7819c66..f6cee5a34bae0c 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/index.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/index.ts
@@ -27,6 +27,7 @@ export {
export { getBundledPackages } from './bundled_packages';
export { getBulkAssets } from './get_bulk_assets';
+export { getTemplateInputs } from './get_template_inputs';
export type { BulkInstallResponse, IBulkInstallPackageError } from './install';
export { handleInstallPackageFailure, installPackage, ensureInstalledPackage } from './install';
diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts
index 46ac72f69ecabd..983fdd7e8d29b9 100644
--- a/x-pack/plugins/fleet/server/services/package_policy.ts
+++ b/x-pack/plugins/fleet/server/services/package_policy.ts
@@ -1861,16 +1861,23 @@ function validatePackagePolicyOrThrow(packagePolicy: NewPackagePolicy, pkgInfo:
}
}
-function getInputsWithStreamIds(
+// the option `allEnabled` is only used to generate inputs integration templates where everything is enabled by default
+// it shouldn't be used in the normal install flow
+export function getInputsWithStreamIds(
packagePolicy: NewPackagePolicy,
- packagePolicyId: string
+ packagePolicyId?: string,
+ allEnabled?: boolean
): PackagePolicy['inputs'] {
return packagePolicy.inputs.map((input) => {
return {
...input,
+ enabled: !!allEnabled ? true : input.enabled,
streams: input.streams.map((stream) => ({
...stream,
- id: `${input.type}-${stream.data_stream.dataset}-${packagePolicyId}`,
+ enabled: !!allEnabled ? true : stream.enabled,
+ id: packagePolicyId
+ ? `${input.type}-${stream.data_stream.dataset}-${packagePolicyId}`
+ : `${input.type}-${stream.data_stream.dataset}`,
})),
};
});
diff --git a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
index 2c046134598a7b..da0628cbeb9115 100644
--- a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
+++ b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
@@ -247,3 +247,15 @@ export const DeletePackageRequestSchemaDeprecated = {
})
),
};
+
+export const GetInputsRequestSchema = {
+ params: schema.object({
+ pkgName: schema.string(),
+ pkgVersion: schema.string(),
+ }),
+ query: schema.object({
+ format: schema.oneOf([schema.literal('json'), schema.literal('yml'), schema.literal('yaml')], {
+ defaultValue: 'json',
+ }),
+ }),
+};
diff --git a/x-pack/plugins/security/public/components/form_flyout.tsx b/x-pack/plugins/security/public/components/form_flyout.tsx
deleted file mode 100644
index 51ab56a11d225b..00000000000000
--- a/x-pack/plugins/security/public/components/form_flyout.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { EuiButtonProps, EuiFlyoutProps } from '@elastic/eui';
-import {
- EuiButton,
- EuiButtonEmpty,
- EuiFlexGroup,
- EuiFlexItem,
- EuiFlyout,
- EuiFlyoutBody,
- EuiFlyoutFooter,
- EuiFlyoutHeader,
- EuiPortal,
- EuiTitle,
-} from '@elastic/eui';
-import type { FunctionComponent, RefObject } from 'react';
-import React, { useEffect } from 'react';
-
-import { FormattedMessage } from '@kbn/i18n-react';
-
-import { useHtmlId } from './use_html_id';
-
-export interface FormFlyoutProps extends Omit {
- title: string;
- initialFocus?: RefObject;
- onCancel(): void;
- onSubmit(): void;
- submitButtonText: string;
- submitButtonColor?: EuiButtonProps['color'];
- isLoading?: EuiButtonProps['isLoading'];
- isDisabled?: EuiButtonProps['isDisabled'];
- isSubmitButtonHidden?: boolean;
-}
-
-export const FormFlyout: FunctionComponent = ({
- title,
- submitButtonText,
- submitButtonColor,
- onCancel,
- onSubmit,
- isLoading,
- isDisabled,
- isSubmitButtonHidden,
- children,
- initialFocus,
- ...rest
-}) => {
- useEffect(() => {
- if (initialFocus && initialFocus.current) {
- initialFocus.current.focus();
- }
- }, [initialFocus]);
-
- const titleId = useHtmlId('formFlyout', 'title');
-
- return (
-
-
-
-
- {title}
-
-
- {children}
-
-
-
-
-
-
-
- {!isSubmitButtonHidden && (
-
-
- {submitButtonText}
-
-
- )}
-
-
-
-
- );
-};
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
index 6d100f2a8261ee..b3792941fc6d0f 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
@@ -7,11 +7,17 @@
import type { ExclusiveUnion } from '@elastic/eui';
import {
+ EuiButton,
+ EuiButtonEmpty,
EuiCallOut,
EuiCheckableCard,
EuiFieldNumber,
EuiFlexGroup,
EuiFlexItem,
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiFlyoutHeader,
EuiFormFieldset,
EuiFormRow,
EuiHorizontalRule,
@@ -36,10 +42,9 @@ import { ApiKeyBadge, ApiKeyStatus, TimeToolTip, UsernameWithIcon } from './api_
import type { ApiKeyRoleDescriptors } from '../../../../common/model';
import { DocLink } from '../../../components/doc_link';
import { FormField } from '../../../components/form_field';
-import type { FormFlyoutProps } from '../../../components/form_flyout';
-import { FormFlyout } from '../../../components/form_flyout';
import { FormRow } from '../../../components/form_row';
import { useCurrentUser } from '../../../components/use_current_user';
+import { useHtmlId } from '../../../components/use_html_id';
import { useInitialFocus } from '../../../components/use_initial_focus';
import { RolesAPIClient } from '../../roles/roles_api_client';
import { APIKeysAPIClient } from '../api_keys_api_client';
@@ -64,7 +69,7 @@ export interface ApiKeyFormValues {
interface CommonApiKeyFlyoutProps {
initialValues?: ApiKeyFormValues;
- onCancel: FormFlyoutProps['onCancel'];
+ onCancel(): void;
canManageCrossClusterApiKeys?: boolean;
readOnly?: boolean;
}
@@ -175,337 +180,466 @@ export const ApiKeyFlyout: FunctionComponent = ({
const firstFieldRef = useInitialFocus([isLoading]);
+ const titleId = useHtmlId('formFlyout', 'title');
+ const isSubmitButtonHidden = readOnly || (apiKey && !canEdit);
+
+ const isSubmitDisabled =
+ isLoading || (formik.submitCount > 0 && !formik.isValid) || readOnly || (apiKey && !canEdit);
+
+ const title = apiKey
+ ? readOnly || !canEdit
+ ? i18n.translate('xpack.security.accountManagement.apiKeyFlyout.viewTitle', {
+ defaultMessage: `API key details`,
+ })
+ : i18n.translate('xpack.security.accountManagement.apiKeyFlyout.updateTitle', {
+ defaultMessage: `Update API key`,
+ })
+ : i18n.translate('xpack.security.accountManagement.apiKeyFlyout.createTitle', {
+ defaultMessage: `Create API key`,
+ });
+
+ const submitButtonText = apiKey
+ ? i18n.translate('xpack.security.accountManagement.apiKeyFlyout.updateSubmitButton', {
+ defaultMessage: `{isSubmitting, select, true{Updating API key…} other{Update API key}}`,
+ values: { isSubmitting: formik.isSubmitting },
+ })
+ : i18n.translate('xpack.security.accountManagement.apiKeyFlyout.createSubmitButton', {
+ defaultMessage: `{isSubmitting, select, true{Creating API key…} other{Create API key}}`,
+ values: { isSubmitting: formik.isSubmitting },
+ });
+
return (
- 0 && !formik.isValid) ||
- readOnly ||
- (apiKey && !canEdit)
- }
- isSubmitButtonHidden={readOnly || (apiKey && !canEdit)}
- size="m"
- ownFocus
- >
-
- {apiKey && !readOnly ? (
- !isOwner ? (
- <>
-
- }
- />
-
- >
- ) : hasExpired ? (
- <>
-
- }
- />
-
- >
- ) : null
- ) : null}
-
-
-
-
+
+
+
+
);
};
diff --git a/x-pack/plugins/serverless_search/common/doc_links.ts b/x-pack/plugins/serverless_search/common/doc_links.ts
index 4c93763a145c83..0c816e3c7a3899 100644
--- a/x-pack/plugins/serverless_search/common/doc_links.ts
+++ b/x-pack/plugins/serverless_search/common/doc_links.ts
@@ -57,11 +57,11 @@ class ESDocLinks {
this.kibanaFeedback = newDocLinks.kibana.feedback;
this.kibanaRunApiInConsole = newDocLinks.console.serverlessGuide;
this.metadata = newDocLinks.security.mappingRoles;
- this.roleDescriptors = newDocLinks.security.mappingRoles;
+ this.roleDescriptors = newDocLinks.serverlessSecurity.apiKeyPrivileges;
this.securityApis = newDocLinks.apis.securityApis;
// Client links
- this.elasticsearchClients = newDocLinks.serverlessClients.httpApis;
+ this.elasticsearchClients = newDocLinks.serverlessClients.clientLib;
// Go
this.goApiReference = newDocLinks.serverlessClients.goApiReference;
this.goBasicConfig = newDocLinks.serverlessClients.goGettingStarted;
diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts
index 3be3095ca2830c..723646a1763e63 100644
--- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts
+++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts
@@ -18,6 +18,7 @@ import {
getCase,
getCaseSavedObjectsFromES,
resolveCase,
+ findCases,
} from '../../../../common/lib/api';
import { superUser } from '../../../../common/lib/authentication/users';
@@ -73,6 +74,62 @@ export default function createGetTests({ getService }: FtrProviderContext) {
syncAlerts: true,
});
});
+
+ it('should return the cases correctly', async () => {
+ const cases = await findCases({ supertest });
+ const theCase = cases.cases[0];
+
+ const { version, ...caseWithoutVersion } = theCase;
+ const { cases: _, ...caseStats } = cases;
+
+ expect(cases.cases.length).to.eql(1);
+
+ expect(caseStats).to.eql({
+ count_closed_cases: 0,
+ count_in_progress_cases: 0,
+ count_open_cases: 1,
+ page: 1,
+ per_page: 20,
+ total: 1,
+ });
+
+ expect(caseWithoutVersion).to.eql({
+ assignees: [],
+ category: null,
+ closed_at: null,
+ closed_by: null,
+ comments: [],
+ connector: {
+ fields: null,
+ id: 'connector-1',
+ name: 'none',
+ type: '.none',
+ },
+ created_at: '2020-09-28T11:43:52.158Z',
+ created_by: {
+ email: null,
+ full_name: null,
+ username: 'elastic',
+ },
+ customFields: [],
+ description: 'This is a brand new case of a bad meanie defacing data',
+ duration: null,
+ external_service: null,
+ id: 'e1900ac0-017f-11eb-93f8-d161651bf509',
+ owner: 'securitySolution',
+ settings: {
+ syncAlerts: true,
+ },
+ severity: 'low',
+ status: 'open',
+ tags: ['defacement'],
+ title: 'Super Bad Security Issue',
+ totalAlerts: 0,
+ totalComment: 1,
+ updated_at: null,
+ updated_by: null,
+ });
+ });
});
// tests upgrading a 7.11.1 saved object to the latest version
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/telemetry_usage.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/telemetry_usage.ts
index 9135879f3e2eb0..5306937a97ab5b 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/telemetry_usage.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/telemetry_usage.ts
@@ -65,7 +65,8 @@ export default ({ getService }: FtrProviderContext) => {
});
});
- describe('Risk engine enabled', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/168429
+ describe.skip('Risk engine enabled', () => {
let hostId: string;
let userId: string;
diff --git a/x-pack/test/fleet_api_integration/apis/epm/get_templates_inputs.ts b/x-pack/test/fleet_api_integration/apis/epm/get_templates_inputs.ts
new file mode 100644
index 00000000000000..e879269c89fa16
--- /dev/null
+++ b/x-pack/test/fleet_api_integration/apis/epm/get_templates_inputs.ts
@@ -0,0 +1,200 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+import fs from 'fs';
+import path from 'path';
+import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
+import { skipIfNoDockerRegistry } from '../../helpers';
+import { setupFleetAndAgents } from '../agents/services';
+import { testUsers } from '../test_users';
+
+export default function (providerContext: FtrProviderContext) {
+ const { getService } = providerContext;
+
+ const supertest = getService('supertest');
+ const supertestWithoutAuth = getService('supertestWithoutAuth');
+
+ const testPkgName = 'apache';
+ const testPkgVersion = '0.1.4';
+
+ const uninstallPackage = async (name: string, version: string) => {
+ await supertest.delete(`/api/fleet/epm/packages/${name}/${version}`).set('kbn-xsrf', 'xxxx');
+ };
+
+ const testPkgArchiveZip = path.join(
+ path.dirname(__filename),
+ '../fixtures/direct_upload_packages/apache_0.1.4.zip'
+ );
+
+ describe('EPM Templates - Get Inputs', () => {
+ skipIfNoDockerRegistry(providerContext);
+ setupFleetAndAgents(providerContext);
+ before(async () => {
+ const buf = fs.readFileSync(testPkgArchiveZip);
+ await supertest
+ .post(`/api/fleet/epm/packages`)
+ .set('kbn-xsrf', 'xxxx')
+ .type('application/zip')
+ .send(buf)
+ .expect(200);
+ });
+ after(async () => {
+ await uninstallPackage(testPkgName, testPkgVersion);
+ });
+ const expectedYml = `inputs:
+ - id: logfile-apache.access
+ type: logfile
+ data_stream:
+ dataset: apache.access
+ type: logs
+ paths:
+ - /var/log/apache2/access.log*
+ - /var/log/apache2/other_vhosts_access.log*
+ - /var/log/httpd/access_log*
+ exclude_files:
+ - .gz$
+ processors:
+ - add_fields:
+ target: ''
+ fields:
+ ecs.version: 1.5.0
+ - id: logfile-apache.error
+ type: logfile
+ data_stream:
+ dataset: apache.error
+ type: logs
+ paths:
+ - /var/log/apache2/error.log*
+ - /var/log/httpd/error_log*
+ exclude_files:
+ - .gz$
+ processors:
+ - add_locale: null
+ - add_fields:
+ target: ''
+ fields:
+ ecs.version: 1.5.0
+ - id: apache/metrics-apache.status
+ type: apache/metrics
+ data_stream:
+ dataset: apache.status
+ type: metrics
+ metricsets:
+ - status
+ hosts:
+ - 'http://127.0.0.1'
+ period: 10s
+ server_status_path: /server-status
+`;
+ const expectedJson = [
+ {
+ id: 'logfile-apache.access',
+ type: 'logfile',
+ data_stream: {
+ type: 'logs',
+ dataset: 'apache.access',
+ },
+ paths: [
+ '/var/log/apache2/access.log*',
+ '/var/log/apache2/other_vhosts_access.log*',
+ '/var/log/httpd/access_log*',
+ ],
+ exclude_files: ['.gz$'],
+ processors: [
+ {
+ add_fields: {
+ target: '',
+ fields: {
+ 'ecs.version': '1.5.0',
+ },
+ },
+ },
+ ],
+ },
+ {
+ id: 'logfile-apache.error',
+ type: 'logfile',
+ data_stream: {
+ type: 'logs',
+ dataset: 'apache.error',
+ },
+ paths: ['/var/log/apache2/error.log*', '/var/log/httpd/error_log*'],
+ exclude_files: ['.gz$'],
+ processors: [
+ {
+ add_locale: null,
+ },
+ {
+ add_fields: {
+ target: '',
+ fields: {
+ 'ecs.version': '1.5.0',
+ },
+ },
+ },
+ ],
+ },
+ {
+ id: 'apache/metrics-apache.status',
+ type: 'apache/metrics',
+ data_stream: {
+ type: 'metrics',
+ dataset: 'apache.status',
+ },
+ metricsets: ['status'],
+ hosts: ['http://127.0.0.1'],
+ period: '10s',
+ server_status_path: '/server-status',
+ },
+ ];
+
+ it('returns inputs template in json format', async function () {
+ const res = await supertest
+ .get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
+ .expect(200);
+ const inputs = res.body.inputs;
+
+ expect(inputs).to.eql(expectedJson);
+ });
+
+ it('returns inputs template in yaml format if format=yaml', async function () {
+ const res = await supertest
+ .get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=yaml`)
+ .expect(200);
+
+ expect(res.text).to.eql(expectedYml);
+ });
+
+ it('returns inputs template in yaml format if format=yml', async function () {
+ const res = await supertest
+ .get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=yml`)
+ .expect(200);
+ expect(res.text).to.eql(expectedYml);
+ });
+
+ it('returns a 404 for a version that does not exists', async function () {
+ await supertest
+ .get(`/api/fleet/epm/templates/${testPkgName}/0.1.0/inputs?format=json`)
+ .expect(404);
+ });
+
+ it('allows user with only fleet permission to access', async () => {
+ await supertestWithoutAuth
+ .get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
+ .auth(testUsers.fleet_all_only.username, testUsers.fleet_all_only.password)
+ .expect(200);
+ });
+
+ it('allows user with integrations read permission to access', async () => {
+ await supertestWithoutAuth
+ .get(`/api/fleet/epm/templates/${testPkgName}/${testPkgVersion}/inputs?format=json`)
+ .auth(testUsers.fleet_all_int_read.username, testUsers.fleet_all_int_read.password)
+ .expect(200);
+ });
+ });
+}
diff --git a/x-pack/test/fleet_api_integration/apis/epm/index.js b/x-pack/test/fleet_api_integration/apis/epm/index.js
index 045626e95a7406..94f7c12a15ce4d 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/index.js
+++ b/x-pack/test/fleet_api_integration/apis/epm/index.js
@@ -46,5 +46,6 @@ export default function loadTests({ loadTestFile, getService }) {
loadTestFile(require.resolve('./install_dynamic_template_metric'));
loadTestFile(require.resolve('./routing_rules'));
loadTestFile(require.resolve('./install_runtime_field'));
+ loadTestFile(require.resolve('./get_templates_inputs'));
});
}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
index a65d92dea06585..deea626b28f249 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
@@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default ({ getPageObject, getService }: FtrProviderContext) => {
const common = getPageObject('common');
+ const header = getPageObject('header');
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
const svlObltNavigation = getService('svlObltNavigation');
@@ -19,15 +20,15 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const retry = getService('retry');
const find = getService('find');
- describe.only('Configure Case', function () {
+ describe('Configure Case', function () {
// Error: timed out waiting for assertRadioGroupValue: Expected the radio group value to equal "close-by-pushing"
this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
-
await svlObltNavigation.navigateToLandingPage();
-
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'observability-overview:cases' });
+ await common.clickAndValidate('configure-case-button', 'case-configure-title');
+ await header.waitUntilLoadingHasFinished();
});
after(async () => {
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
index e74deac161b26e..5c71abf3ad7ba4 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
@@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../../../ftr_provider_context';
export default ({ getPageObject, getService }: FtrProviderContext) => {
const common = getPageObject('common');
+ const header = getPageObject('header');
const svlCommonPage = getPageObject('svlCommonPage');
const svlSecNavigation = getService('svlSecNavigation');
const testSubjects = getService('testSubjects');
@@ -23,12 +24,10 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
-
await svlSecNavigation.navigateToLandingPage();
-
await testSubjects.click('solutionSideNavItemLink-cases');
-
await common.clickAndValidate('configure-case-button', 'case-configure-title');
+ await header.waitUntilLoadingHasFinished();
});
after(async () => {