From 4ddcee91682a248a63e352cc47e52f0672970a57 Mon Sep 17 00:00:00 2001 From: Madison Caldwell Date: Sun, 9 May 2021 17:07:41 -0400 Subject: [PATCH 1/3] Merge Dario's changes --- .ci/es-snapshots/Jenkinsfile_verify_es | 3 +- .ci/jobs.yml | 1 + .eslintrc.js | 16 + BUILD.bazel | 1 + api_docs/spaces.json | 895 +- api_docs/spaces_oss.json | 313 +- api_docs/usage_collection.json | 741 +- api_docs/usage_collection.mdx | 8 +- .../getting-started/building-kibana.asciidoc | 6 +- .../monorepo-packages.asciidoc | 7 + ...migrating-legacy-plugins-examples.asciidoc | 30 +- ...-plugin-core-public.doclinksstart.links.md | 3 + docs/index.asciidoc | 10 +- docs/management/action-types.asciidoc | 6 + docs/settings/reporting-settings.asciidoc | 4 +- docs/setup/docker.asciidoc | 53 +- .../alerting-troubleshooting.asciidoc | 164 + docs/user/alerting/rule-management.asciidoc | 6 + docs/user/reporting/index.asciidoc | 2 +- docs/user/security/reporting.asciidoc | 2 +- package.json | 15 +- packages/BUILD.bazel | 7 + packages/kbn-analytics/BUILD.bazel | 124 + packages/kbn-analytics/package.json | 9 +- packages/kbn-analytics/scripts/build.js | 85 - packages/kbn-analytics/tsconfig.browser.json | 18 + packages/kbn-analytics/tsconfig.json | 6 +- packages/kbn-cli-dev-mode/package.json | 1 - packages/kbn-config/BUILD.bazel | 97 + packages/kbn-config/package.json | 6 +- packages/kbn-config/tsconfig.json | 2 +- packages/kbn-crypto/BUILD.bazel | 87 + packages/kbn-crypto/package.json | 9 +- packages/kbn-crypto/tsconfig.json | 7 +- packages/kbn-docs-utils/package.json | 3 - packages/kbn-es/.babelrc | 3 + packages/kbn-es/BUILD.bazel | 90 + packages/kbn-es/package.json | 5 - packages/kbn-es/scripts/build.js | 60 - packages/kbn-optimizer/package.json | 1 - .../BUILD.bazel | 84 + .../kbn-securitysolution-constants/README.md | 6 + .../jest.config.js | 14 +- .../package.json | 9 + .../src/index.ts | 75 + .../tsconfig.json | 19 + .../BUILD.bazel | 92 + .../README.md | 10 + .../jest.config.js} | 14 +- .../package.json | 9 + .../src/actions/index.ts | 45 + .../src/constants/index.mock.ts | 24 + .../src/constants/index.ts | 21 + .../src/created_at/index.ts | 13 + .../src/created_by/index.ts | 13 + .../src/deafult_version_number/index.test.ts | 65 + .../src/deafult_version_number/index.ts | 25 + .../src/default_actions_array/index.ts | 19 + .../src/default_array/index.test.ts | 82 + .../src/default_array/index.ts | 27 + .../src/default_boolean_false/index.test.ts | 52 + .../src/default_boolean_false/index.ts | 22 + .../src/default_boolean_true/index.test.ts | 52 + .../src/default_boolean_true/index.ts | 22 + .../src/default_empty_string/index.test.ts | 43 + .../src/default_empty_string/index.ts | 22 + .../default_export_file_name/index.test.ts | 43 + .../src/default_export_file_name/index.ts | 22 + .../src/default_from_string/index.test.ts | 43 + .../src/default_from_string/index.ts | 27 + .../src/default_interval_string/index.test.ts | 43 + .../src/default_interval_string/index.ts | 22 + .../src/default_language_string/index.test.ts | 44 + .../src/default_language_string/index.ts | 23 + .../default_max_signals_number/index.test.ts | 66 + .../src/default_max_signals_number/index.ts | 27 + .../src/default_page/index.test.ts | 85 + .../src/default_page/index.ts | 32 + .../src/default_per_page/index.test.ts | 85 + .../src/default_per_page/index.ts | 32 + .../default_risk_score_mapping_array/index.ts | 27 + .../default_severity_mapping_array/index.ts | 27 + .../src/default_string_array/index.test.ts | 52 + .../src/default_string_array/index.ts | 23 + .../index.test.ts | 101 + .../src/default_string_boolean_false/index.ts | 34 + .../src/default_threat_array/index.test.ts | 66 + .../src/default_threat_array/index.ts | 23 + .../src/default_throttle_null/index.test.ts | 44 + .../src/default_throttle_null/index.ts | 23 + .../src/default_to_string/index.test.ts | 43 + .../src/default_to_string/index.ts | 22 + .../src/default_uuid/index.test.ts | 43 + .../src/default_uuid/index.ts | 25 + .../src/default_version_number/index.test.ts | 65 + .../src/default_version_number/index.ts | 25 + .../src/description/index.ts | 14 + .../src/empty_string_array/index.test.ts | 79 + .../src/empty_string_array/index.ts | 45 + .../src/exact_check/index.test.ts | 177 + .../src/exact_check/index.ts | 94 + .../src/format_errors/index.test.ts | 189 + .../src/format_errors/index.ts | 33 + .../src/from/index.ts | 26 + .../src/id/index.ts | 15 + .../src/index.ts | 74 + .../src/iso_date_string/index.test.ts | 56 + .../src/iso_date_string/index.ts | 38 + .../src/language/index.ts | 12 + .../src/list_types/comment/index.mock.ts | 19 + .../src/list_types/comment/index.test.ts | 237 + .../src/list_types/comment/index.ts | 39 + .../list_types/create_comment/index.mock.ts | 15 + .../list_types/create_comment/index.test.ts | 134 + .../src/list_types/create_comment/index.ts | 23 + .../default_comments_array/index.test.ts | 65 + .../default_comments_array/index.ts | 23 + .../index.test.ts | 65 + .../default_create_comments_array/index.ts | 27 + .../default_namespace/index.test.ts | 61 + .../src/list_types/default_namespace/index.ts | 25 + .../default_namespace_array/index.test.ts | 99 + .../default_namespace_array/index.ts | 44 + .../index.test.ts | 65 + .../default_update_comments_array/index.ts | 27 + .../list_types/endpoint/entries/index.mock.ts | 18 + .../list_types/endpoint/entries/index.test.ts | 111 + .../src/list_types/endpoint/entries/index.ts | 43 + .../endpoint/entry_match/index.mock.ts | 17 + .../endpoint/entry_match/index.test.ts | 102 + .../list_types/endpoint/entry_match/index.ts | 21 + .../endpoint/entry_match_any/index.mock.ts | 17 + .../endpoint/entry_match_any/index.test.ts | 100 + .../endpoint/entry_match_any/index.ts | 22 + .../endpoint/entry_match_wildcard/index.ts | 21 + .../endpoint/entry_nested/index.mock.ts | 18 + .../endpoint/entry_nested/index.test.ts | 137 + .../list_types/endpoint/entry_nested/index.ts | 20 + .../src/list_types/endpoint/index.ts | 13 + .../non_empty_nested_entries_array/index.ts | 46 + .../src/list_types/entries/index.mock.ts | 31 + .../src/list_types/entries/index.test.ts | 148 + .../src/list_types/entries/index.ts | 41 + .../list_types/entries_exist/index.mock.ts | 21 + .../list_types/entries_exist/index.test.ts | 79 + .../src/list_types/entries_exist/index.ts | 21 + .../src/list_types/entries_list/index.mock.ts | 17 + .../src/list_types/entries_list/index.test.ts | 96 + .../src/list_types/entries_list/index.ts | 23 + .../src/list_types/entry_match/index.mock.ts | 22 + .../src/list_types/entry_match/index.test.ts | 107 + .../src/list_types/entry_match/index.ts | 21 + .../list_types/entry_match_any/index.mock.ts | 23 + .../list_types/entry_match_any/index.test.ts | 105 + .../src/list_types/entry_match_any/index.ts | 23 + .../entry_match_wildcard/index.mock.ts | 22 + .../entry_match_wildcard/index.test.ts | 105 + .../list_types/entry_match_wildcard/index.ts | 21 + .../src/list_types/entry_nested/index.mock.ts | 29 + .../src/list_types/entry_nested/index.test.ts | 124 + .../src/list_types/entry_nested/index.ts | 20 + .../src/list_types/exception_list/index.ts | 23 + .../exception_list_item_type/index.ts | 14 + .../src/list_types/index.ts | 32 + .../src/list_types/item_id/index.ts | 17 + .../src/list_types/lists/index.mock.ts | 26 + .../src/list_types/lists/index.test.ts | 126 + .../src/list_types/lists/index.ts | 27 + .../lists_default_array/index.test.ts | 63 + .../list_types/lists_default_array/index.ts | 23 + .../non_empty_entries_array/index.test.ts | 131 + .../non_empty_entries_array/index.ts | 41 + .../index.test.ts | 116 + .../non_empty_nested_entries_array/index.ts | 42 + .../src/list_types/operator/index.ts | 25 + .../src/list_types/os_type/index.ts | 23 + .../src/list_types/type/index.ts | 39 + .../list_types/update_comment/index.mock.ts | 20 + .../list_types/update_comment/index.test.ts | 149 + .../src/list_types/update_comment/index.ts | 30 + .../src/max_signals/index.ts | 15 + .../src/meta/index.ts | 14 + .../src/name/index.ts | 14 + .../src/non_empty_array/index.test.ts | 96 + .../src/non_empty_array/index.ts | 30 + .../index.test.ts | 69 + .../index.ts | 36 + .../src/non_empty_string/index.test.ts | 56 + .../src/non_empty_string/index.ts | 29 + .../src/normalized_ml_job_id/index.ts | 24 + .../src/only_false_allowed/index.test.ts | 54 + .../src/only_false_allowed/index.ts | 33 + .../src/operator/index.ts | 16 +- .../src/parse_schedule_dates/index.ts | 26 + .../src/positive_integer/index.test.ts | 54 + .../src/positive_integer/index.ts | 26 + .../index.test.ts | 56 + .../index.ts | 26 + .../references_default_array/index.test.ts | 52 + .../src/references_default_array/index.ts | 22 + .../src/risk_score/index.test.ts | 61 + .../src/risk_score/index.ts | 28 + .../src/risk_score_mapping/index.ts | 31 + .../src/saved_object_attributes/index.ts | 63 + .../src/severity/index.ts | 15 + .../src/severity_mapping/index.ts | 32 + .../src/string_to_positive_number/index.ts | 37 + .../src/tags/index.ts | 16 + .../src/test_utils/index.ts | 56 + .../src/threat/index.ts | 34 + .../src/threat_mapping/index.test.ts | 237 + .../src/threat_mapping/index.ts | 76 + .../src/threat_subtechnique/index.ts | 23 + .../src/threat_tactic/index.ts | 21 + .../src/threat_technique/index.ts | 32 + .../src/throttle/index.ts | 18 + .../src/updated_at/index.ts | 13 + .../src/updated_by/index.ts | 13 + .../src/uuid/index.test.ts | 43 + .../src/uuid/index.ts | 30 + .../src/validate/index.test.ts | 49 + .../src/validate/index.ts | 56 + .../src/version/index.ts | 18 + .../tsconfig.json | 19 + .../kbn-securitysolution-utils/BUILD.bazel | 86 + packages/kbn-securitysolution-utils/README.md | 4 + .../kbn-securitysolution-utils/jest.config.js | 13 + .../kbn-securitysolution-utils/package.json | 9 + .../src/add_remove_id_to_item/index.test.ts | 78 + .../src/add_remove_id_to_item/index.ts | 51 + .../kbn-securitysolution-utils/src/index.ts | 9 + .../kbn-securitysolution-utils/tsconfig.json | 19 + packages/kbn-server-http-tools/package.json | 5 +- packages/kbn-test/package.json | 1 - .../lib/mocha/setup_mocha.js | 4 + .../lib/mocha/validate_ci_group_tags.js | 75 + packages/kbn-ui-shared-deps/package.json | 1 - rfcs/0000_template.md | 67 +- rfcs/README.md | 19 +- .../public/doc_links/doc_links_service.ts | 6 + src/core/public/http/types.ts | 2 +- src/core/public/public.api.md | 3 + .../migrations/core/document_migrator.ts | 2 +- .../migrations/core/elastic_index.ts | 2 +- .../migrations/kibana/kibana_migrator.test.ts | 4 +- .../catch_retryable_es_client_errors.ts | 6 +- .../migrationsv2/actions/index.test.ts | 54 +- .../migrationsv2/actions/index.ts | 115 +- .../integration_tests/actions.test.ts | 177 +- .../8.0.0_migrated_with_outdated_docs.zip | Bin 0 -> 48381 bytes .../integration_tests/outdated_docs.test.ts | 140 + .../migrations_state_action_machine.test.ts | 204 +- .../saved_objects/migrationsv2/model.test.ts | 206 +- .../saved_objects/migrationsv2/model.ts | 75 +- .../server/saved_objects/migrationsv2/next.ts | 54 +- .../saved_objects/migrationsv2/types.ts | 124 +- .../aggregations/aggs_types/bucket_aggs.ts | 6 +- .../lib/aggregations/validation.test.ts | 86 + .../service/lib/aggregations/validation.ts | 34 +- .../lib/aggregations/validation_utils.ts | 13 +- .../telemetry_collectors/nested_collector.ts | 4 +- .../search_source/search_source.test.ts | 1 + src/plugins/data/kibana.json | 1 - .../collectors/create_usage_collector.ts | 5 +- src/plugins/data/public/public.api.md | 2 +- .../saved_query/saved_query_service.test.ts | 68 + .../query/saved_query/saved_query_service.ts | 14 +- .../collectors/create_usage_collector.ts | 7 +- .../search_interceptor.test.ts | 15 +- .../search_interceptor/search_interceptor.ts | 6 +- .../public/search/search_interceptor/utils.ts | 6 +- .../ui/query_string_input/_query_bar.scss | 2 + src/plugins/data/server/server.api.md | 1 - .../public/__mocks__/index_pattern.ts | 2 +- .../context/api/{_stubs.js => _stubs.ts} | 68 +- .../api/{anchor.test.js => anchor.test.ts} | 73 +- .../context/api/{anchor.js => anchor.ts} | 33 +- ...s.test.js => context.predecessors.test.ts} | 117 +- ...ors.test.js => context.successors.test.ts} | 104 +- .../angular/context/api/context.ts | 9 +- .../api/utils/get_es_query_search_after.ts | 10 +- .../angular/context/api/utils/sorting.ts | 1 - .../action_bar/action_bar_directive.ts | 1 + .../context/query/{actions.js => actions.tsx} | 90 +- .../context/query/{index.js => index.ts} | 1 - .../angular/context/query/state.ts | 17 + .../context/query_parameters/actions.js | 64 - .../context/query_parameters/actions.test.ts | 46 +- .../context/query_parameters/actions.ts | 82 + .../query_parameters/{index.js => index.ts} | 0 .../public/application/angular/context_app.js | 12 +- .../application/angular/context_app_state.ts | 60 + .../application/angular/context_state.test.ts | 5 +- .../application/angular/context_state.ts | 10 +- .../directives/debounce/debounce.test.ts | 3 +- .../public/application/angular/doc.ts | 8 +- .../doc_table/components/pager/index.ts | 2 + .../doc_table/components/table_header.ts | 1 + .../components/table_header/table_header.tsx | 1 - .../angular/doc_table/components/table_row.ts | 6 +- .../doc_table/create_doc_table_react.tsx | 2 + .../angular/doc_table/doc_table.ts | 6 +- .../angular/doc_table/infinite_scroll.ts | 2 + .../doc_table/lib/get_default_sort.test.ts | 2 +- .../angular/doc_table/lib/get_sort.test.ts | 2 +- .../lib/get_sort_for_search_source.test.ts | 2 +- .../doc_table/lib/pager/pager_factory.ts | 2 +- .../public/application/angular/doc_viewer.tsx | 3 +- .../angular/helpers/row_formatter.test.ts | 9 +- .../angular/helpers/row_formatter.tsx | 3 + .../public/application/angular/redirect.ts | 2 + .../context_app/context_app_legacy.tsx | 10 +- .../context_app_legacy_directive.ts | 1 + .../context_error_message.test.tsx | 11 +- .../context_error_message.tsx | 7 +- .../components/create_discover_directive.ts | 1 + .../create_discover_grid_directive.tsx | 1 + .../application/components/discover.tsx | 2 +- .../discover_grid_cell_actions.test.tsx | 2 + .../discover_grid/get_render_cell_value.tsx | 5 +- .../application/components/doc/doc.test.tsx | 4 + .../components/doc/use_es_doc_search.test.tsx | 126 +- .../components/doc/use_es_doc_search.ts | 39 +- .../components/doc_viewer/doc_viewer.test.tsx | 4 + .../components/doc_viewer/doc_viewer_tab.tsx | 30 +- .../json_code_editor/json_code_editor.tsx | 2 +- .../sidebar/discover_field.test.tsx | 4 +- .../sidebar/discover_field_details.test.tsx | 4 +- .../discover_field_details_footer.test.tsx | 4 +- .../sidebar/discover_field_search.test.tsx | 10 +- .../sidebar/discover_index_pattern.test.tsx | 8 +- ...discover_index_pattern_management.test.tsx | 4 +- .../sidebar/discover_sidebar.test.tsx | 6 +- .../discover_sidebar_responsive.test.tsx | 6 +- .../sidebar/lib/field_calculator.test.ts | 10 +- .../components/sidebar/lib/get_details.ts | 2 +- .../sidebar/lib/group_fields.test.ts | 11 +- .../components/table/table.test.tsx | 12 +- .../timechart_header.test.tsx | 4 +- .../doc_views/doc_views_helpers.tsx | 1 + .../doc_views/doc_views_registry.ts | 3 +- .../application/doc_views/doc_views_types.ts | 31 +- .../public/application/helpers/breadcrumbs.ts | 1 + .../application/helpers/calc_field_counts.ts | 2 +- .../helpers/migrate_legacy_query.ts | 2 +- src/plugins/discover/public/build_services.ts | 5 +- .../discover/public/get_inner_angular.ts | 2 +- .../discover/public/kibana_services.ts | 11 +- src/plugins/discover/public/mocks.ts | 6 +- src/plugins/discover/public/plugin.tsx | 8 +- .../discover/public/url_generator.test.ts | 2 +- .../discover/server/saved_objects/search.ts | 2 +- .../server/saved_objects/search_migrations.ts | 3 + .../public/views/requests/_requests.scss | 4 + .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/presentation_util/common/labs.ts | 19 +- src/plugins/spaces_oss/common/types.ts | 46 +- src/plugins/spaces_oss/public/api.ts | 41 +- src/plugins/spaces_oss/public/index.ts | 4 +- src/plugins/spaces_oss/public/types.ts | 20 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../public/components/index.ts | 1 + .../track_application_view.tsx | 2 +- .../track_application_view/types.ts | 2 +- src/plugins/usage_collection/public/index.ts | 4 +- .../usage_collection/public/plugin.tsx | 53 +- .../server/collector/collector.test.ts | 13 - .../server/collector/collector.ts | 151 +- .../server/collector/collector_set.test.ts | 3 - .../server/collector/collector_set.ts | 10 +- .../server/collector/index.ts | 11 +- .../server/collector/types.ts | 197 + .../server/collector/usage_collector.ts | 12 +- src/plugins/usage_collection/server/index.ts | 13 +- src/plugins/usage_collection/server/mocks.ts | 3 +- src/plugins/usage_collection/server/plugin.ts | 84 +- .../server/usage_counters/index.ts | 4 +- .../server/usage_counters/saved_objects.ts | 28 +- .../server/usage_counters/usage_counter.ts | 21 +- .../vis_type_timeseries/common/vis_schema.ts | 1 + .../components/last_value_mode_indicator.tsx | 9 +- .../lib/create_interval_based_formatter.ts | 14 +- .../components/panel_config/timeseries.tsx | 17 + ...fig_query_bar_with_ignore_global_filter.js | 2 +- .../components/timeseries_visualization.tsx | 1 + .../components/vis_types/timeseries/vis.js | 7 +- .../public/application/components/yes_no.tsx | 2 +- .../public/data_model/search_api.ts | 53 +- .../public/vega_view/vega_base_view.js | 2 + .../public/vega_view/vega_map_view/view.ts | 2 +- .../public/vega_view/vega_view.js | 2 +- test/functional/apps/discover/_doc_table.ts | 29 + .../edit_saved_object/mappings.json | 6 +- .../export_transform/mappings.json | 4 +- .../hidden_saved_objects/mappings.json | 4 +- .../hidden_types/mappings.json | 4 +- .../show_relationships/mappings.json | 4 +- test/functional/services/data_grid.ts | 6 + test/scripts/jenkins_xpack_build_kibana.sh | 15 +- typings/elasticsearch/search.d.ts | 6 +- vars/kibanaPipeline.groovy | 31 +- vars/tasks.groovy | 18 +- vars/workers.groovy | 10 +- x-pack/package.json | 1 - .../server/create_execute_function.test.ts | 29 + .../actions/server/create_execute_function.ts | 20 +- x-pack/plugins/actions/server/plugin.ts | 3 +- .../get_import_result_message.test.ts | 53 - .../saved_objects/get_import_warnings.test.ts | 96 + ...sult_message.ts => get_import_warnings.ts} | 19 +- .../actions/server/saved_objects/index.ts | 28 +- .../transform_connectors_for_export.test.ts | 253 + .../transform_connectors_for_export.ts | 50 + .../server/alerts_client/alerts_client.ts | 34 +- .../server/alerts_client/tests/create.test.ts | 191 +- .../server/alerts_client/tests/update.test.ts | 264 +- .../alerting/server/saved_objects/index.ts | 20 +- .../transform_rule_for_export.test.ts | 91 + .../transform_rule_for_export.ts | 26 + .../server/task_runner/task_runner.ts | 1 + .../read_only_user/deep_links.spec.ts | 39 + .../integration/read_only_user/home.spec.ts | 46 +- .../service_overview/header_filters.spec.ts | 145 + .../service_overview/instances_table.spec.ts | 114 + .../service_overview/service_overview.spec.ts | 67 + .../transactions_overview.spec.ts | 48 + .../apm/ftr_e2e/cypress/support/commands.ts | 22 + .../apm/ftr_e2e/cypress/support/types.d.ts | 4 + x-pack/plugins/apm/kibana.json | 12 +- .../public/application/application.test.tsx | 8 +- .../plugins/apm/public/application/csmApp.tsx | 19 +- .../plugins/apm/public/application/index.tsx | 13 +- .../alerting/register_apm_alerts.ts | 98 +- .../ErrorGroupDetails/Distribution/index.tsx | 3 +- .../app/Home/__snapshots__/Home.test.tsx.snap | 14 +- .../service_details/service_detail_tabs.tsx | 17 +- .../service_icons/alert_details.tsx | 30 +- .../service_inventory/ServiceList/index.tsx | 8 +- .../get_columns.tsx | 2 + .../index.tsx | 3 +- .../index.tsx | 1 + .../shared/KueryBar/Typeahead/Suggestions.js | 7 +- .../shared/KueryBar/Typeahead/index.js | 1 + .../Links/apm/service_overview_link.tsx | 26 +- ...ervice_transactions_overview_link.test.tsx | 4 +- .../service_transactions_overview_link.tsx | 32 +- .../Links/apm/transaction_overview_link.tsx | 14 +- .../charts/helper/get_alert_annotations.tsx | 16 +- .../shared/charts/latency_chart/index.tsx | 5 +- .../transaction_error_rate_chart/index.tsx | 3 +- .../shared/transaction_type_select.tsx | 1 + .../context/apm_plugin/apm_plugin_context.tsx | 5 +- .../apm_plugin/mock_apm_plugin_context.tsx | 9 +- x-pack/plugins/apm/public/plugin.ts | 21 +- .../server/lib/alerts/register_apm_alerts.ts | 6 +- .../alerts/register_error_count_alert_type.ts | 24 +- ...egister_transaction_duration_alert_type.ts | 252 +- ...transaction_duration_anomaly_alert_type.ts | 31 +- ...ister_transaction_error_rate_alert_type.ts | 24 +- .../apm/server/lib/alerts/test_utils/index.ts | 24 +- .../server/lib/services/get_service_alerts.ts | 18 +- x-pack/plugins/apm/server/plugin.ts | 103 +- .../server/routes/register_routes/index.ts | 6 +- x-pack/plugins/apm/server/routes/services.ts | 19 +- x-pack/plugins/apm/server/routes/typings.ts | 4 +- x-pack/plugins/apm/server/types.ts | 9 + .../functions/browser/escount.ts | 94 + .../functions/browser/esdocs.ts | 141 + .../functions/browser/essql.ts | 103 + .../functions/browser/index.ts | 14 +- .../functions/server/escount.ts | 5 +- x-pack/plugins/canvas/common/lib/constants.ts | 1 + .../lib/request}/build_bool_array.ts | 2 +- .../lib/request}/build_es_request.js | 0 .../lib => common/lib/request}/filters.ts | 2 +- .../lib/request}/format_response.js | 0 .../lib/request}/get_es_filter.ts | 2 +- .../lib/request}/normalize_type.ts | 0 .../lib/request}/sanitize_name.ts | 0 .../canvas/i18n/functions/dict/escount.ts | 2 +- .../canvas/i18n/functions/dict/esdocs.ts | 2 +- .../canvas/i18n/functions/dict/essql.ts | 8 +- x-pack/plugins/canvas/public/plugin.tsx | 3 +- .../canvas/public/services/context.tsx | 2 + .../canvas/public/services/expressions.ts | 14 +- .../plugins/canvas/public/services/index.ts | 5 + .../plugins/canvas/public/services/search.ts | 24 + .../canvas/public/services/stubs/index.ts | 2 + .../canvas/public/services/stubs/search.ts | 11 + .../canvas/server/lib/essql_strategy.ts | 96 + .../plugins/canvas/server/lib/query_es_sql.ts | 6 +- x-pack/plugins/canvas/server/plugin.ts | 18 +- .../server/routes/es_fields/es_fields.ts | 2 +- x-pack/plugins/canvas/types/index.ts | 1 + x-pack/plugins/canvas/types/strategy.ts | 31 + .../plugins/cases/common/api/cases/comment.ts | 16 + .../plugins/cases/common/api/runtime_types.ts | 4 + x-pack/plugins/cases/common/constants.ts | 2 + .../public/components/callout/translations.ts | 2 +- .../routes/api/cases/alerts/get_cases.ts | 48 + .../plugins/cases/server/routes/api/index.ts | 3 + x-pack/plugins/cases/server/services/index.ts | 58 + x-pack/plugins/cases/server/services/mocks.ts | 1 + .../auto_follow_pattern_list.test.js | 8 +- .../follower_indices_list.test.js | 8 +- .../client_integration/mocks/index.ts | 1 + .../mocks/search_box.mock.tsx | 22 + .../auto_follow_pattern_table.js | 9 +- .../follower_indices_table.js | 3 +- .../flyout_create_drilldown.test.tsx | 4 + .../flyout_create_drilldown.tsx | 30 +- .../flyout_edit_drilldown.test.tsx | 4 + .../flyout_edit_drilldown.tsx | 29 +- .../sessions_mgmt/components/main.test.tsx | 2 +- .../components/table/table.test.tsx | 6 +- .../search/sessions_mgmt/lib/api.test.ts | 29 + .../public/search/sessions_mgmt/lib/api.ts | 34 +- .../search/sessions_mgmt/lib/documentation.ts | 3 +- .../public/search/sessions_mgmt/types.ts | 4 +- .../engine_creation/engine_creation.test.tsx | 10 + .../engine_creation/engine_creation.tsx | 5 +- .../engine_creation_logic.test.ts | 23 + .../engine_creation/engine_creation_logic.ts | 11 + .../engines/components/empty_state.tsx | 1 - .../meta_engine_creation.test.tsx | 9 + .../meta_engine_creation.tsx | 5 +- .../meta_engine_creation_logic.test.ts | 23 + .../meta_engine_creation_logic.ts | 13 +- .../components/license_callout/constants.ts | 20 + .../components/license_callout/index.ts | 8 + .../license_callout/license_callout.test.tsx | 44 + .../license_callout/license_callout.tsx | 44 + .../product_selector.test.tsx | 11 + .../product_selector/product_selector.tsx | 7 +- .../setup_guide/setup_guide_cta.tsx | 7 +- .../components/trial_callout/index.ts | 8 + .../trial_callout/trial_callout.test.tsx | 32 + .../trial_callout/trial_callout.tsx | 49 + .../enterprise_search/constants.ts | 15 + .../shared/licensing/licensing_logic.test.ts | 29 + .../shared/licensing/licensing_logic.ts | 5 + .../schema/add_field_modal/constants.tsx | 44 + .../index.test.tsx} | 31 +- .../shared/schema/add_field_modal/index.tsx | 141 + .../schema/add_field_modal/utils.test.ts | 26 + .../shared/schema/add_field_modal/utils.ts | 13 + .../applications/shared/schema/constants.ts | 67 +- .../schema/errors_accordion/constants.ts | 28 + .../index.test.tsx} | 23 +- .../shared/schema/errors_accordion/index.tsx | 113 + .../schema_errors_accordion.scss | 26 + .../applications/shared/schema/index.ts | 3 +- .../shared/schema/schema_add_field_modal.tsx | 152 - .../schema/schema_errors_accordion.scss | 20 - .../shared/schema/schema_errors_accordion.tsx | 121 - .../schema/schema_change_errors.test.tsx | 2 +- .../schema/schema_change_errors.tsx | 8 +- .../role_mappings/role_mappings_logic.test.ts | 12 +- .../role_mappings/role_mappings_logic.ts | 141 +- .../server/lib/check_access.test.ts | 2 +- .../routes/app_search/search_ui.test.ts | 2 +- .../server/routes/app_search/search_ui.ts | 2 +- x-pack/plugins/features/server/plugin.test.ts | 20 +- x-pack/plugins/features/server/plugin.ts | 10 +- .../file_upload/public/validate_index_name.ts | 6 + x-pack/plugins/file_upload/server/routes.ts | 20 +- .../plugins/fleet/common/constants/agent.ts | 3 - .../plugins/fleet/common/constants/routes.ts | 1 - .../openapi/components/schemas/agent.yaml | 5 - .../paths/agents@{agent_id}@events.yaml | 11 - .../services/is_agent_upgradeable.test.ts | 1 - .../plugins/fleet/common/services/routes.ts | 1 - .../fleet/common/types/models/agent.ts | 34 - .../fleet/common/types/rest_spec/agent.ts | 74 +- .../applications/fleet/constants/index.ts | 1 - .../fleet/hooks/use_request/agents.ts | 15 - .../agents/components/agent_health.tsx | 3 - .../components/new_enrollment_key_flyout.tsx | 10 +- .../enrollment_token_list_page/index.tsx | 20 +- .../public/applications/fleet/types/index.ts | 3 - .../plugins/fleet/server/constants/index.ts | 1 - x-pack/plugins/fleet/server/plugin.ts | 2 - .../fleet/server/routes/agent/handlers.ts | 35 - .../fleet/server/routes/agent/index.ts | 12 - .../fleet/server/saved_objects/index.ts | 29 - .../saved_objects/migrations/to_v7_10_0.ts | 13 - .../fleet/server/services/agents/events.ts | 48 - .../fleet/server/services/agents/helpers.ts | 1 - .../fleet/server/services/agents/index.ts | 1 - .../server/services/agents/saved_objects.ts | 3 - .../fleet/server/services/agents/status.ts | 20 +- .../fleet/server/services/hosts_utils.test.ts | 27 + .../fleet/server/services/hosts_utils.ts | 30 + .../plugins/fleet/server/services/output.ts | 25 +- .../fleet/server/services/settings.test.ts | 21 +- .../plugins/fleet/server/services/settings.ts | 37 +- x-pack/plugins/fleet/server/types/index.tsx | 3 - .../fleet/server/types/models/agent.ts | 49 - .../server/types/models/package_policy.ts | 1 + .../fleet/server/types/rest_spec/agent.ts | 143 - .../client_integration/app/app.helpers.tsx | 3 +- .../edit_policy/edit_policy.helpers.tsx | 233 +- .../features/node_allocation.test.ts | 503 -- .../cloud_aware_behavior.helpers.ts | 37 + .../cloud_aware_behavior.test.ts | 154 + .../node_allocation/cold_phase.helpers.ts | 25 + .../node_allocation/cold_phase.test.ts | 226 + .../general_behavior.helpers.ts | 26 + .../node_allocation/general_behavior.test.ts | 128 + .../node_allocation/warm_phase.helpers.ts | 25 + .../node_allocation/warm_phase.test.ts | 214 + .../edit_policy/init_test_bed.tsx | 55 + .../helpers/create_enable_phase_action.ts | 15 + .../helpers/create_form_set_value_action.ts | 23 + .../helpers/create_form_toggle_action.ts | 21 + .../helpers/create_node_allocation_actions.ts | 83 + .../helpers/global_mocks.tsx | 34 + .../client_integration/helpers/index.ts | 27 +- .../helpers/save_policy_action.ts | 19 + .../helpers/set_replicas_action.ts | 21 + .../client_integration/helpers/types.ts} | 5 +- .../common/types/policies.ts | 2 +- .../get_available_node_roles_for_phase.ts | 6 +- .../components/data_tier_allocation.tsx | 16 +- .../components/default_allocation_notice.tsx | 112 - .../components/default_allocation_warning.tsx | 72 - .../default_to_data_nodes_notice.tsx | 38 + .../default_to_data_tiers_notice.tsx | 60 + .../components/index.ts | 12 +- .../no_custom_attributes_messages.tsx | 37 + .../components/no_node_attributes_warning.tsx | 60 - .../components/no_tiers_available_notice.tsx | 49 + ...available_using_node_attributes_notice.tsx | 36 + .../components/node_allocation.tsx | 32 +- .../node_role_to_fallback_tier_map.ts | 19 + .../components/types.ts | 8 +- .../will_use_fallback_tier_notice.tsx | 49 + ...back_tier_using_node_attributes_notice.tsx | 47 + .../data_tier_allocation_field.tsx | 101 +- .../application/services/documentation.ts | 2 + .../host/metrics/tsvb/host_system_overview.ts | 36 +- .../infra/public/hooks/use_http_request.tsx | 6 +- .../inventory_view/components/table_view.tsx | 29 +- .../components/waffle/group_of_nodes.tsx | 26 +- .../inventory_view/components/waffle/node.tsx | 124 +- .../components/waffle/node_context_menu.tsx | 111 +- .../metric_detail/components/page_error.tsx | 5 +- x-pack/plugins/infra/public/types.ts | 8 + x-pack/plugins/infra/server/index.ts | 1 + .../overview/lib/create_top_nodes_query.ts | 2 +- x-pack/plugins/lens/kibana.json | 3 +- .../embeddable/embeddable.tsx | 2 +- .../dimension_panel/dimension_editor.tsx | 10 - .../dimension_panel/dimension_panel.test.tsx | 17 +- .../dimension_panel/field_select.tsx | 4 +- x-pack/plugins/lists/common/format_errors.ts | 3 + .../lists/common/schemas/common/schemas.ts | 168 + .../lists/common/schemas/types/comment.ts | 22 + .../common/schemas/types/create_comment.ts | 26 + .../schemas/types/default_comments_array.ts | 1 + .../types/default_create_comments_array.ts | 1 + .../common/schemas/types/default_namespace.ts | 1 + .../schemas/types/default_namespace_array.ts | 8 + .../types/default_string_boolean_false.ts | 4 + .../types/default_update_comments_array.ts | 1 + .../schemas/types/empty_string_array.ts | 8 + .../common/schemas/types/endpoint/entries.ts | 16 +- .../schemas/types/endpoint/entry_match.ts | 7 + .../schemas/types/endpoint/entry_match_any.ts | 7 + .../types/endpoint/entry_match_wildcard.ts | 7 + .../schemas/types/endpoint/entry_nested.ts | 7 + .../non_empty_nested_entries_array.ts | 16 +- .../lists/common/schemas/types/entries.ts | 22 + .../common/schemas/types/entry_exists.ts | 7 + .../lists/common/schemas/types/entry_list.ts | 7 + .../lists/common/schemas/types/entry_match.ts | 7 + .../common/schemas/types/entry_match_any.ts | 7 + .../schemas/types/entry_match_wildcard.ts | 7 + .../common/schemas/types/entry_nested.ts | 7 + .../schemas/types/non_empty_entries_array.ts | 9 +- .../types/non_empty_nested_entries_array.ts | 9 +- .../non_empty_or_nullable_string_array.ts | 9 +- .../schemas/types/non_empty_string_array.ts | 8 + .../types/string_to_positive_number.ts | 1 + .../common/schemas/types/update_comment.ts | 22 + x-pack/plugins/lists/common/test_utils.ts | 11 + x-pack/plugins/lists/common/types.ts | 2 +- .../plugins/maps/public/embeddable/index.ts | 1 + .../maps/public/embeddable/map_embeddable.tsx | 1 - x-pack/plugins/maps/public/index.ts | 4 +- x-pack/plugins/ml/common/types/common.ts | 4 + .../plugins/ml/common/types/trained_models.ts | 51 +- .../ml_embedded_map/ml_embedded_map.tsx | 9 +- .../components/vega_chart/vega_chart_view.tsx | 3 +- .../analytics_navigation_bar.tsx | 4 + .../models_management/expanded_row.tsx | 34 +- .../models_management/models_list.tsx | 22 +- .../application/explorer/anomalies_map.tsx | 268 + .../public/application/explorer/explorer.js | 6 + .../explorer/explorer_charts/map_config.ts | 8 +- .../data_frame_analytics/models_provider.ts | 4 +- .../ml/server/routes/trained_models.ts | 25 +- .../public/components/apm/apm_metrics.tsx | 1 + .../components/cluster/overview/apm_panel.js | 1 + .../monitoring/public/lib/apm_agent.ts | 6 +- .../plugins/monitoring/server/config.test.ts | 3 + x-pack/plugins/monitoring/server/config.ts | 1 + .../lib/alerts/fetch_ccr_read_exceptions.ts | 4 +- .../lib/alerts/fetch_disk_usage_node_stats.ts | 4 +- .../lib/alerts/fetch_index_shard_size.ts | 63 +- .../alerts/fetch_memory_usage_node_stats.ts | 4 +- .../alerts/fetch_nodes_from_cluster_stats.ts | 7 +- .../fetch_thread_pool_rejections_stats.ts | 4 +- .../server/lib/elasticsearch/get_ml_jobs.ts | 2 +- .../elasticsearch/shards/get_shard_stats.ts | 2 +- .../server/lib/logstash/get_pipeline.ts | 4 +- .../lib/logstash/get_pipeline_vertex.ts | 4 +- .../server/routes/api/v1/alerts/enable.ts | 22 +- .../server/routes/api/v1/elasticsearch/ccr.ts | 2 +- x-pack/plugins/monitoring/server/types.ts | 3 +- .../rules/observability_rule_field_map.ts | 22 - .../common/utils/formatters/duration.ts | 3 + .../common/utils/formatters/formatters.ts | 2 + x-pack/plugins/observability/kibana.json | 3 +- .../public/application/application.test.tsx | 4 +- .../public/application/index.tsx | 9 +- .../components/app/section/apm/index.test.tsx | 8 +- .../components/app/section/ux/index.test.tsx | 4 +- .../public/context/plugin_context.tsx | 5 +- .../public/hooks/use_time_range.test.ts | 6 +- x-pack/plugins/observability/public/index.ts | 3 +- .../public/pages/alerts/alerts.stories.tsx | 4 +- .../alerts_flyout/alerts_flyout.stories.tsx | 41 +- .../pages/alerts/alerts_flyout/index.tsx | 47 +- .../public/pages/alerts/alerts_table.tsx | 10 +- .../public/pages/alerts/example_data.ts | 60 +- .../public/pages/alerts/index.tsx | 144 +- .../public/pages/cases/index.tsx | 48 +- .../pages/overview/overview.stories.tsx | 4 +- x-pack/plugins/observability/public/plugin.ts | 25 +- ...create_observability_rule_type_registry.ts | 31 + .../public/rules/formatter_rule_registry.ts | 30 - .../rules/observability_rule_registry_mock.ts | 17 - .../observability_rule_type_registry_mock.ts | 16 + .../public/utils/test_helper.tsx | 6 +- .../server/lib/rules/get_top_alerts.ts | 31 +- x-pack/plugins/observability/server/plugin.ts | 21 +- .../server/routes/register_routes.ts | 8 +- .../observability/server/routes/rules.ts | 27 +- .../observability/server/routes/types.ts | 4 +- x-pack/plugins/observability/server/types.ts | 6 - x-pack/plugins/osquery/common/exact_check.ts | 1 + .../plugins/osquery/common/format_errors.ts | 3 + x-pack/plugins/osquery/common/test_utils.ts | 11 + .../list/remote_clusters_list.test.js | 22 +- .../common/lib/cluster_serialization.ts | 12 +- .../server/lib/does_cluster_exist.ts | 9 +- .../plugins/remote_clusters/server/plugin.ts | 5 + .../server/routes/api/add_route.test.ts | 406 +- .../server/routes/api/add_route.ts | 25 +- .../server/routes/api/delete_route.test.ts | 422 +- .../server/routes/api/delete_route.ts | 31 +- .../server/routes/api/get_route.test.ts | 326 +- .../server/routes/api/get_route.ts | 19 +- .../server/routes/api/types.ts | 12 + .../server/routes/api/update_route.test.ts | 505 +- .../server/routes/api/update_route.ts | 25 +- .../remote_clusters/server/shared_imports.ts | 2 +- .../plugins/remote_clusters/server/types.ts | 6 + x-pack/plugins/rule_registry/common/assets.ts | 12 + .../ecs_component_template.ts | 24 + .../technical_component_template.ts | 19 + .../field_maps}/ecs_field_map.ts | 0 .../field_maps/technical_rule_field_map.ts | 56 + .../index_templates/base_index_template.ts | 15 + .../default_lifecycle_policy.ts} | 4 +- .../common/field_map/base_rule_field_map.ts | 36 - .../rule_registry/common/field_map/index.ts | 2 - x-pack/plugins/rule_registry/common/index.ts | 3 +- .../mapping_from_field_map.ts | 6 +- .../common/parse_technical_fields.ts | 25 + .../common/technical_rule_data_field_names.ts | 76 + x-pack/plugins/rule_registry/common/types.ts | 21 + x-pack/plugins/rule_registry/kibana.json | 3 +- x-pack/plugins/rule_registry/public/index.ts | 17 - x-pack/plugins/rule_registry/public/plugin.ts | 56 - .../public/rule_registry/index.ts | 47 - .../public/rule_registry/types.ts | 63 - x-pack/plugins/rule_registry/server/index.ts | 11 +- x-pack/plugins/rule_registry/server/plugin.ts | 57 +- .../server/rule_data_client/index.ts | 125 + .../server/rule_data_client/types.ts | 44 + .../server/rule_data_plugin_service/index.ts | 159 + .../index.ts | 179 - .../types.ts | 57 - .../server/rule_registry/index.ts | 328 - .../create_lifecycle_rule_type_factory.ts | 235 - .../server/rule_registry/types.ts | 42 - x-pack/plugins/rule_registry/server/types.ts | 106 +- .../create_lifecycle_rule_type_factory.ts | 246 + .../server/utils/get_rule_executor_data.ts | 39 + .../utils/with_rule_data_client_factory.ts | 39 + .../es/index_privilege_form.test.tsx | 130 +- .../privileges/es/index_privilege_form.tsx | 64 +- .../privileges/es/index_privileges.test.tsx | 5 +- .../privileges/es/index_privileges.tsx | 50 +- .../roles/indices_api_client.test.ts | 104 + .../management/roles/indices_api_client.ts | 18 +- .../roles/roles_management_app.test.tsx | 6 +- .../server/routes/indices/get_fields.ts | 13 +- .../security_solution/common/constants.ts | 3 + .../schemas/common/schemas.ts | 212 + .../schemas/types/default_actions_array.ts | 1 + .../schemas/types/default_array.ts | 1 + .../schemas/types/default_boolean_false.ts | 1 + .../schemas/types/default_boolean_true.ts | 1 + .../schemas/types/default_empty_string.ts | 1 + .../schemas/types/default_export_file_name.ts | 1 + .../schemas/types/default_from_string.ts | 1 + .../schemas/types/default_interval_string.ts | 1 + .../schemas/types/default_language_string.ts | 1 + .../types/default_max_signals_number.ts | 1 + .../schemas/types/default_page.ts | 1 + .../schemas/types/default_per_page.ts | 1 + .../types/default_risk_score_mapping_array.ts | 1 + .../types/default_severity_mapping_array.ts | 1 + .../schemas/types/default_string_array.ts | 1 + .../types/default_string_boolean_false.ts | 1 + .../schemas/types/default_threat_array.ts | 1 + .../schemas/types/default_throttle_null.ts | 1 + .../schemas/types/default_to_string.ts | 1 + .../schemas/types/default_uuid.ts | 1 + .../schemas/types/default_version_number.ts | 1 + .../schemas/types/iso_date_string.ts | 1 + .../detection_engine/schemas/types/lists.ts | 3 + .../schemas/types/lists_default_array.ts | 1 + .../schemas/types/non_empty_array.ts | 3 + .../schemas/types/non_empty_string.ts | 1 + .../schemas/types/normalized_ml_job_id.ts | 14 + .../schemas/types/only_false_allowed.ts | 1 + .../schemas/types/positive_integer.ts | 1 + .../positive_integer_greater_than_zero.ts | 1 + .../schemas/types/references_default_array.ts | 1 + .../schemas/types/risk_score.ts | 1 + .../schemas/types/threat_mapping.ts | 139 + .../detection_engine/schemas/types/uuid.ts | 1 + .../common/endpoint/index_data.ts | 25 +- .../security_solution/common/exact_check.ts | 1 + .../security_solution/common/format_errors.ts | 3 + .../timeline/events/all/index.ts | 2 +- .../security_solution/common/test_utils.ts | 11 + .../detection_alerts/alerts_details.spec.ts | 68 + ....ts => missing_privileges_callout.spec.ts} | 47 +- .../integration/timelines/notes_tab.spec.ts | 25 +- .../timelines/open_timeline.spec.ts | 7 +- .../security_solution/cypress/objects/rule.ts | 30 +- .../cypress/objects/timeline.ts | 10 + .../cypress/screens/alerts_details.ts | 6 + .../cypress/screens/common/callouts.ts | 2 +- .../cypress/tasks/alerts_details.ts | 7 +- .../cypress/tasks/api_calls/rules.ts | 2 +- .../cypress/tasks/timeline.ts | 40 +- .../security_solution/public/app/app.tsx | 13 +- .../cases/components/callout/translations.ts | 2 +- .../public/cases/pages/case.tsx | 2 +- .../public/cases/pages/case_details.tsx | 2 +- .../callouts/use_callout_storage.ts | 43 +- .../components/event_details/columns.test.tsx | 4 +- .../components/event_details/columns.tsx | 92 +- .../event_details/event_details.tsx | 1 + .../components/exceptions/translations.ts | 12 + .../exception_item/exception_details.test.tsx | 28 + .../exception_item/exception_details.tsx | 8 +- .../viewer/exception_item/index.test.tsx | 25 + .../viewer/exception_item/index.tsx | 8 +- .../exceptions/viewer/helpers.test.tsx | 56 + .../components/exceptions/viewer/helpers.tsx | 26 +- .../navigation/breadcrumbs/index.ts | 2 +- .../comma_separated_values.tsx | 24 + .../missing_privileges_callout/index.tsx | 48 + .../translations.tsx | 147 + .../use_missing_privileges.ts | 91 + .../read_only_alerts_callout/index.tsx | 33 - .../read_only_alerts_callout/translations.tsx | 48 - .../read_only_rules_callout/index.tsx | 33 - .../read_only_rules_callout/translations.tsx | 48 - .../components/user_info/index.test.tsx | 7 +- .../detections/components/user_info/index.tsx | 9 +- .../components/user_privileges/index.tsx | 42 + .../user_privileges/translations.ts | 22 + ..._fetch_detection_engine_privileges.mock.ts | 12 + .../use_fetch_detection_engine_privileges.ts | 47 + .../use_fetch_list_privileges.ts | 62 + .../detection_engine/alerts/translations.ts | 7 - .../alerts/use_alerts_privileges.test.tsx | 196 + .../alerts/use_alerts_privileges.tsx | 78 + .../alerts/use_privilege_user.test.tsx | 238 - .../alerts/use_privilege_user.tsx | 103 - .../alerts/use_signal_index.test.tsx | 6 +- .../alerts/use_signal_index.tsx | 12 +- .../detection_engine/lists/translations.ts | 7 - .../lists/use_lists_index.tsx | 6 +- .../lists/use_lists_privileges.mock.ts | 1 + .../lists/use_lists_privileges.tsx | 119 +- .../detection_engine/detection_engine.tsx | 4 +- .../detection_engine/rules/details/index.tsx | 6 +- .../pages/detection_engine/rules/index.tsx | 4 +- .../public/management/common/breadcrumbs.ts | 41 + .../paginated_content/paginated_content.tsx | 15 +- .../pages/endpoint_hosts/view/index.test.tsx | 2 +- .../management/pages/event_filters/index.tsx | 1 + .../pages/event_filters/service/index.ts | 39 +- .../pages/event_filters/state/index.ts | 16 + .../pages/event_filters/store/action.ts | 17 +- .../pages/event_filters/store/builders.ts | 7 + .../event_filters/store/middleware.test.ts | 3 +- .../pages/event_filters/store/middleware.ts | 115 +- .../pages/event_filters/store/reducer.test.ts | 4 + .../pages/event_filters/store/reducer.ts | 80 +- .../pages/event_filters/store/selector.ts | 125 +- .../event_filters/store/selectors.test.ts | 229 +- .../management/pages/event_filters/types.ts | 22 + .../view/components/empty/index.tsx | 2 +- .../view/event_filters_list_page.tsx | 116 +- .../pages/event_filters/view/hooks.ts | 11 +- .../public/management/pages/index.tsx | 45 +- .../pages/policy/view/agents_summary.tsx | 18 +- .../pages/policy/view/policy_details.test.tsx | 2 +- .../pages/trusted_apps/store/utils.test.ts | 7 +- .../pages/trusted_apps/store/utils.ts | 5 +- .../__snapshots__/index.test.tsx.snap | 338 +- .../pages/trusted_apps/view/translations.ts | 4 +- .../view/trusted_apps_page.test.tsx | 4 +- .../state/async_resource_builders.ts | 45 + .../public/management/state/index.ts | 9 + .../timeline/body/renderers/cti/helpers.ts | 11 +- .../scripts/beat_docs/build.js | 33 +- .../routes/rules/update_rules_route.ts | 4 +- ...collection_cloudtrail_logging_created.json | 4 +- ...llection_microsoft_365_new_inbox_rule.json | 64 + ...ommand_and_control_common_webservices.json | 4 +- ...fer_protocol_activity_to_the_internet.json | 58 - .../command_and_control_iexplore_via_com.json | 7 +- ...hat_protocol_activity_to_the_internet.json | 58 - ...ol_port_8000_activity_to_the_internet.json | 43 - ..._to_point_tunneling_protocol_activity.json | 43 - ...l_proxy_port_activity_to_the_internet.json | 43 - ...ol_remote_file_copy_desktopimgdownldr.json | 8 +- ...and_control_remote_file_copy_mpcmdrun.json | 8 +- ...mand_and_control_smtp_to_the_internet.json | 58 - ..._server_port_activity_to_the_internet.json | 43 - ...ol_ssh_secure_shell_from_the_internet.json | 73 - ...trol_ssh_secure_shell_to_the_internet.json | 43 - ...control_sunburst_c2_activity_detected.json | 8 +- ...d_control_teamviewer_remote_file_copy.json | 8 +- ..._control_tor_activity_to_the_internet.json | 56 - ...d_and_control_tunneling_via_earthworm.json | 49 + ...ial_access_credential_dumping_msbuild.json | 8 +- ...edential_access_credentials_keychains.json | 4 +- ...cess_domain_backup_dpapi_private_keys.json | 8 +- ...ial_access_iam_user_addition_to_group.json | 4 +- ...s_keychain_pwd_retrieval_security_cmd.json | 22 +- ...ial_access_lsass_memdump_file_created.json | 8 +- ...l_access_mimikatz_memssp_default_logs.json | 8 +- ...ential_access_mitm_localhost_webproxy.json | 4 +- ...e_network_logon_provider_modification.json | 69 + .../credential_access_tcpdump_activity.json | 63 - ...den_file_attribute_with_via_attribexe.json | 8 +- ..._base64_encoding_or_decoding_activity.json | 53 - ...e_evasion_clearing_windows_event_logs.json | 8 +- ...se_evasion_cloudtrail_logging_deleted.json | 4 +- ..._evasion_cloudtrail_logging_suspended.json | 4 +- ...nse_evasion_cloudwatch_alarm_deletion.json | 4 +- ...efense_evasion_code_injection_conhost.json | 8 +- ..._evasion_config_service_rule_deletion.json | 11 +- ...vasion_configuration_recorder_stopped.json | 4 +- ...delete_volume_usn_journal_with_fsutil.json | 8 +- ...deleting_backup_catalogs_with_wbadmin.json | 8 +- ...ble_windows_firewall_rules_with_netsh.json | 8 +- ...defense_evasion_ec2_flow_log_deletion.json | 4 +- ...ense_evasion_ec2_network_acl_deletion.json | 4 +- ...coding_or_decoding_files_via_certutil.json | 8 +- ...ecution_msbuild_started_by_office_app.json | 8 +- ...n_execution_msbuild_started_by_script.json | 8 +- ...ion_msbuild_started_by_system_process.json | 8 +- ...ion_execution_msbuild_started_renamed.json | 8 +- ...cution_msbuild_started_unusal_process.json | 8 +- ...execution_suspicious_explorer_winword.json | 8 +- ...ution_via_trusted_developer_utilities.json | 58 - ...storage_bucket_configuration_modified.json | 4 +- ...p_storage_bucket_permissions_modified.json | 4 +- ...e_evasion_guardduty_detector_deletion.json | 4 +- ...ion_hex_encoding_or_decoding_activity.json | 53 - ...ense_evasion_iis_httplogging_disabled.json | 8 +- ...erading_suspicious_werfault_childproc.json | 10 +- ...vasion_masquerading_trusted_directory.json | 4 +- ...e_evasion_modification_of_boot_config.json | 8 +- ..._evasion_modify_environment_launchctl.json | 4 +- ...sion_s3_bucket_configuration_deletion.json | 4 +- ...n_suspicious_managedcode_host_process.json | 8 +- ...efense_evasion_suspicious_scrobj_load.json | 20 +- ..._critical_proc_abnormal_file_activity.json | 8 +- .../defense_evasion_timestomp_touch.json | 4 +- .../defense_evasion_unusual_dir_ads.json | 4 +- ...usual_network_connection_via_rundll32.json | 4 +- ...asion_unusual_system_vp_child_program.json | 8 +- .../defense_evasion_via_filter_manager.json | 12 +- ..._volume_shadow_copy_deletion_via_wmic.json | 8 +- .../defense_evasion_waf_acl_deletion.json | 4 +- .../discovery_net_command_system_account.json | 8 +- ..._post_exploitation_external_ip_lookup.json | 53 + ...exploitation_public_ip_reconnaissance.json | 50 - ...rocess_discovery_via_tasklist_command.json | 49 - .../discovery_query_registry_via_reg.json | 46 - .../discovery_security_software_grep.json | 20 +- ...covery_users_domain_built_in_commands.json | 4 +- .../discovery_whoami_command_activity.json | 8 +- .../discovery_whoami_commmand.json | 48 - ...n_command_shell_started_by_powershell.json | 53 - ...tion_command_shell_started_by_svchost.json | 8 +- ...mand_shell_started_by_unusual_process.json | 8 +- .../execution_command_shell_via_rundll32.json | 7 +- .../execution_from_unusual_path_cmdline.json | 4 +- ...er_program_connecting_to_the_internet.json | 4 +- .../execution_suspicious_pdf_reader.json | 8 +- .../execution_via_compiled_html_file.json | 12 +- .../execution_via_hidden_shell_conhost.json | 8 +- .../execution_via_net_com_assemblies.json | 62 - ...ia_xp_cmdshell_mssql_stored_procedure.json | 8 +- .../impact_cloudtrail_logging_updated.json | 4 +- .../impact_cloudwatch_log_group_deletion.json | 4 +- ...impact_cloudwatch_log_stream_deletion.json | 4 +- .../impact_ec2_disable_ebs_encryption.json | 4 +- .../impact_gcp_storage_bucket_deleted.json | 4 +- ...p_virtual_private_cloud_route_created.json | 4 +- .../impact_hosts_file_modified.json | 8 +- .../impact_iam_deactivate_mfa_device.json | 7 +- .../impact_iam_group_deletion.json | 4 +- .../impact_rds_cluster_deletion.json | 4 +- .../impact_rds_instance_cluster_stoppage.json | 4 +- ...ume_shadow_copy_deletion_via_vssadmin.json | 8 +- .../rules/prepackaged_rules/index.ts | 1048 ++- .../initial_access_console_login_root.json | 4 +- .../initial_access_password_recovery.json | 4 +- ...mote_desktop_protocol_to_the_internet.json | 64 - ...al_access_script_executing_powershell.json | 8 +- ...uspicious_mac_ms_office_child_process.json | 4 +- ...l_access_suspicious_ms_exchange_files.json | 8 +- ...s_suspicious_ms_outlook_child_process.json | 8 +- ...l_access_unusual_dns_service_children.json | 8 +- ...ccess_unusual_dns_service_file_writes.json | 8 +- ...explorer_suspicious_child_parent_args.json | 4 +- ...n_lanman_nullsessionpipe_modification.json | 57 + ...ateral_movement_evasion_rdp_shadowing.json | 50 + ...teral_movement_local_service_commands.json | 46 - ...nt_service_control_spawned_script_int.json | 46 + .../linux_mknod_activity.json | 33 - .../linux_nmap_activity.json | 33 - .../linux_socat_activity.json | 33 - .../ml_cloudtrail_rare_error_code.json | 4 +- .../ml_cloudtrail_rare_method_by_city.json | 4 +- .../ml_cloudtrail_rare_method_by_country.json | 4 +- .../ml_cloudtrail_rare_method_by_user.json | 4 +- .../ml_high_count_network_denies.json | 29 + .../ml_high_count_network_events.json | 29 + .../ml_rare_destination_country.json | 29 + .../ml_spike_in_traffic_to_a_country.json | 29 + .../persistence_adobe_hijack_persistence.json | 8 +- ...ence_azure_automation_webhook_created.json | 3 +- ...l_access_modify_auth_module_or_config.json | 4 +- ...stence_cron_jobs_creation_and_runtime.json | 60 - .../persistence_ec2_network_acl_creation.json | 4 +- ...egistry_startup_shell_folder_modified.json | 54 + ...sistence_gpo_schtask_service_creation.json | 8 +- .../persistence_iam_group_creation.json | 4 +- .../persistence_kernel_module_activity.json | 58 - ...istence_local_scheduled_job_creation.json} | 20 +- ...istence_local_scheduled_task_creation.json | 56 + ...rsistence_login_logout_hooks_defaults.json | 4 +- .../persistence_rds_cluster_creation.json | 4 +- ...persistence_run_key_and_startup_broad.json | 4 +- .../persistence_services_registry.json | 4 +- ...ersistence_system_shells_via_services.json | 8 +- ..._account_added_to_privileged_group_ad.json | 13 +- .../persistence_user_account_creation.json | 8 +- .../persistence_via_application_shimming.json | 8 +- ...rsistence_via_bits_job_notify_command.json | 52 + ...emetrycontroller_scheduledtask_hijack.json | 8 +- ...tence_via_wmi_stdregprov_run_services.json | 83 + ..._printspooler_service_suspicious_file.json | 8 +- ...tion_printspooler_suspicious_spl_file.json | 8 +- ...ation_setuid_setgid_bit_set_via_chmod.json | 4 +- ...alation_uac_bypass_diskcleanup_hijack.json | 4 +- ...ge_escalation_uac_bypass_event_viewer.json | 12 +- .../threat_intel_module_match.json | 198 + .../detection_engine/rules/update_rules.ts | 6 +- .../server/lib/timeline/utils/common.ts | 3 + .../index_fields/index.test.ts | 6 +- .../factory/events/all/helpers.test.ts | 1594 +++- .../timeline/factory/events/all/helpers.ts | 7 + .../timeline/factory/events/all/index.ts | 8 +- .../details/query.events_details.dsl.test.ts | 5 +- .../details/query.events_details.dsl.ts | 2 +- .../server/usage/endpoints/endpoint.mocks.ts | 12 +- .../server/usage/endpoints/endpoint.test.ts | 5 +- .../usage/endpoints/fleet_saved_objects.ts | 25 +- .../server/usage/endpoints/index.ts | 8 +- .../server/utils/beat_schema/fields.ts | 7657 +++++++++++++++-- .../spaces/common/lib/spaces_url_parser.ts | 18 + x-pack/plugins/spaces/common/types.ts | 29 + x-pack/plugins/spaces/public/plugin.tsx | 7 + .../public/space_avatar/space_attributes.ts | 6 +- x-pack/plugins/spaces/server/config.test.ts | 2 +- x-pack/plugins/spaces/server/config.ts | 2 +- x-pack/plugins/spaces/server/index.ts | 2 +- x-pack/plugins/spaces/server/plugin.test.ts | 2 +- x-pack/plugins/spaces/server/plugin.ts | 26 + .../server/spaces_client/spaces_client.ts | 42 +- .../spaces_client/spaces_client_service.ts | 8 + .../server/spaces_service/spaces_service.ts | 35 +- .../spaces_usage_collector.ts | 4 +- .../geo_index_pattern_select.test.tsx.snap | 63 + .../geo_index_pattern_select.test.tsx | 73 + .../geo_index_pattern_select.tsx | 36 +- .../alert_types/geo_containment/alert_type.ts | 1 + .../geo_containment/geo_containment.ts | 6 +- .../tests/es_sample_response.json | 383 +- .../tests/es_sample_response_shapes.json | 428 + .../es_sample_response_with_nesting.json | 308 +- .../tests/geo_containment.test.ts | 315 +- .../schema/xpack_plugins.json | 81 +- .../common/utils/field_utils.test.ts | 59 + .../transform/common/utils/field_utils.ts | 20 + .../public/app/hooks/use_index_data.ts | 34 +- .../common/get_pivot_dropdown_options.ts | 19 +- .../transform/server/routes/api/transforms.ts | 5 +- .../translations/translations/ja-JP.json | 35 +- .../translations/translations/zh-CN.json | 35 +- .../email/email_connector.test.tsx | 21 + .../email/email_connector.tsx | 57 +- .../jira/jira_connectors.test.tsx | 20 + .../jira/jira_connectors.tsx | 30 +- .../builtin_action_types/jira/translations.ts | 8 - .../pagerduty/pagerduty_connectors.test.tsx | 25 + .../pagerduty/pagerduty_connectors.tsx | 45 +- .../resilient/resilient_connectors.test.tsx | 20 + .../resilient/resilient_connectors.tsx | 30 +- .../servicenow/servicenow_connectors.test.tsx | 20 + .../servicenow/servicenow_connectors.tsx | 30 +- .../slack/slack_connectors.test.tsx | 19 + .../slack/slack_connectors.tsx | 45 +- .../teams/teams_connectors.test.tsx | 23 + .../teams/teams_connectors.tsx | 55 +- .../webhook/webhook_connectors.test.tsx | 30 + .../webhook/webhook_connectors.tsx | 51 +- .../get_encrypted_field_notify_label.test.tsx | 70 + .../get_encrypted_field_notify_label.tsx | 68 + .../lib/action_connector_api/connectors.ts | 2 + .../lib/action_connector_api/create.ts | 10 +- .../lib/action_connector_api/update.ts | 8 +- .../action_form.test.tsx | 125 +- .../action_connector_form/action_form.tsx | 2 +- .../connector_edit_flyout.tsx | 3 + .../actions_connectors_list.test.tsx | 11 + .../components/actions_connectors_list.tsx | 85 +- .../triggers_actions_ui/public/types.ts | 1 + .../public/custom_time_range_action.tsx | 11 +- .../tests_client_integration/overview.test.ts | 2 +- .../plugins/uptime/public/state/api/utils.ts | 5 +- .../kibana_telemetry_adapter.test.ts.snap | 14 + .../kibana_telemetry_adapter.test.ts | 4 +- .../telemetry/kibana_telemetry_adapter.ts | 182 +- .../server/lib/adapters/telemetry/types.ts | 4 + .../rest_api/telemetry/log_page_view.ts | 1 + x-pack/tasks/build.ts | 1 + x-pack/test/accessibility/apps/transform.ts | 4 +- .../api_integration/apis/file_upload/index.ts | 1 + .../apis/file_upload/index_exists.ts | 48 + .../apis/security/index_fields.ts | 8 + .../apis/uptime/rest/helper/make_checks.ts | 31 +- .../apis/uptime/rest/helper/make_ping.ts | 8 +- .../api_integration/apis/uptime/rest/index.ts | 11 +- .../apis/uptime/rest/telemetry_collectors.ts | 13 +- .../uptime/rest/telemetry_collectors_fleet.ts | 191 + .../tests/alerts/rule_registry.ts | 230 +- .../basic/tests/cases/alerts/get_cases.ts | 112 + .../case_api_integration/basic/tests/index.ts | 1 + .../security_and_spaces/tests/create_ml.ts | 256 + .../security_and_spaces/tests/index.ts | 1 + .../tests/update_actions.ts | 16 + .../detection_engine_api_integration/utils.ts | 4 +- .../encrypted_saved_objects/mappings.json | 6 +- .../test/fleet_api_integration/apis/index.js | 5 +- .../apis/outputs/crud.ts | 69 + .../apis/outputs/index.js | 12 + .../apis/package_policy/update.ts | 37 + .../apis/settings/update.ts | 12 + .../apps/api_keys/feature_controls/index.ts | 2 - .../feature_controls/index.ts | 2 - x-pack/test/functional/apps/discover/index.ts | 1 + .../apps/discover/saved_searches.ts | 52 + .../feature_controls/index.ts | 2 - .../feature_controls/index.ts | 2 - .../feature_controls/index.ts | 2 - .../feature_controls/index.ts | 2 - .../apps/ml/data_frame_analytics/index.ts | 1 + .../ml/data_frame_analytics/trained_models.ts | 31 + .../remote_clusters/feature_controls/index.ts | 2 - .../test/functional/apps/transform/cloning.ts | 2 +- .../apps/transform/creation_index_pattern.ts | 56 +- .../transform/creation_runtime_mappings.ts | 26 +- .../test/functional/apps/transform/editing.ts | 2 +- .../apps/transform/feature_controls/index.ts | 2 - .../feature_controls/index.ts | 2 - .../feature_controls/security/mappings.json | 6 +- .../feature_controls/spaces/mappings.json | 6 +- .../data/search_sessions/data.json.gz | Bin 2650 -> 2680 bytes .../feature_controls/security/mappings.json | 6 +- .../feature_controls/spaces/mappings.json | 6 +- .../es_archives/fleet/agents/mappings.json | 4 +- .../es_archives/hybrid/kibana/mappings.json | 4 +- .../infra/metrics_anomalies/data.json.gz | Bin 4289452 -> 27992 bytes .../infra/metrics_anomalies/mappings.json | 716 -- .../mappings.json | 6 +- .../es_archives/lens/basic/mappings.json | 8 +- .../es_archives/lens/reporting/mappings.json | 8 +- .../lens/rollup/config/mappings.json | 8 +- .../es_archives/maps/kibana/mappings.json | 4 +- .../es_archives/reporting/nanos/mappings.json | 4 +- .../feature_controls/security/mappings.json | 6 +- .../security/flstest/kibana/mappings.json | 4 +- .../security_solution/anomalies/data.json.gz | Bin 0 -> 1955 bytes .../security_solution/anomalies/mappings.json | 1176 +++ .../spaces/disabled_features/mappings.json | 6 +- .../timelion/feature_controls/mappings.json | 6 +- .../uptime/blank_data_stream/data.json | 0 .../uptime/blank_data_stream/mappings.json | 1333 +++ .../search_sessions_management_page.ts | 2 +- x-pack/test/functional/services/ml/api.ts | 13 + .../test/functional/services/ml/common_ui.ts | 23 + x-pack/test/functional/services/ml/index.ts | 3 + .../test/functional/services/ml/navigation.ts | 7 + ...um_valid_config_classification.json.gz.b64 | 1 + ...inimum_valid_config_regression.json.gz.b64 | 1 + .../functional/services/ml/trained_models.ts | 70 + .../functional/services/transform/wizard.ts | 10 +- x-pack/test/functional_basic/apps/index.ts | 2 - x-pack/test/functional_basic/apps/ml/index.ts | 2 +- .../global_search/basic/mappings.json | 6 +- .../search_sessions/sessions_management.ts | 41 +- .../login_selector/basic_functionality.ts | 3 +- .../es_archives/unmapped_fields/data.json | 14 + .../es_archives/unmapped_fields/mappings.json | 24 + .../apps/endpoint/endpoint_list.ts | 1 - .../apps/endpoint/fleet_integrations.ts | 2 - .../apps/endpoint/index.ts | 2 +- .../apps/endpoint/policy_details.ts | 2 - .../apps/endpoint/trusted_apps_list.ts | 2 - .../apis/index.ts | 2 +- yarn.lock | 38 +- 1261 files changed, 41628 insertions(+), 13960 deletions(-) create mode 100644 packages/kbn-analytics/BUILD.bazel delete mode 100644 packages/kbn-analytics/scripts/build.js create mode 100644 packages/kbn-analytics/tsconfig.browser.json create mode 100644 packages/kbn-config/BUILD.bazel create mode 100644 packages/kbn-crypto/BUILD.bazel create mode 100644 packages/kbn-es/.babelrc create mode 100644 packages/kbn-es/BUILD.bazel delete mode 100644 packages/kbn-es/scripts/build.js create mode 100644 packages/kbn-securitysolution-constants/BUILD.bazel create mode 100644 packages/kbn-securitysolution-constants/README.md rename src/plugins/discover/public/application/components/context_app/constants.ts => packages/kbn-securitysolution-constants/jest.config.js (60%) create mode 100644 packages/kbn-securitysolution-constants/package.json create mode 100644 packages/kbn-securitysolution-constants/src/index.ts create mode 100644 packages/kbn-securitysolution-constants/tsconfig.json create mode 100644 packages/kbn-securitysolution-io-ts-utils/BUILD.bazel create mode 100644 packages/kbn-securitysolution-io-ts-utils/README.md rename packages/{kbn-analytics/babel.config.js => kbn-securitysolution-io-ts-utils/jest.config.js} (52%) create mode 100644 packages/kbn-securitysolution-io-ts-utils/package.json create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/actions/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/description/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/from/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/id/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/language/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/operator/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/name/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts rename src/plugins/discover/public/application/angular/context/query/state.js => packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts (59%) create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/validate/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/src/version/index.ts create mode 100644 packages/kbn-securitysolution-io-ts-utils/tsconfig.json create mode 100644 packages/kbn-securitysolution-utils/BUILD.bazel create mode 100644 packages/kbn-securitysolution-utils/README.md create mode 100644 packages/kbn-securitysolution-utils/jest.config.js create mode 100644 packages/kbn-securitysolution-utils/package.json create mode 100644 packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts create mode 100644 packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts create mode 100644 packages/kbn-securitysolution-utils/src/index.ts create mode 100644 packages/kbn-securitysolution-utils/tsconfig.json create mode 100644 packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js create mode 100644 src/core/server/saved_objects/migrationsv2/integration_tests/archives/8.0.0_migrated_with_outdated_docs.zip create mode 100644 src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts rename src/plugins/discover/public/application/angular/context/api/{_stubs.js => _stubs.ts} (54%) rename src/plugins/discover/public/application/angular/context/api/{anchor.test.js => anchor.test.ts} (73%) rename src/plugins/discover/public/application/angular/context/api/{anchor.js => anchor.ts} (61%) rename src/plugins/discover/public/application/angular/context/api/{context.predecessors.test.js => context.predecessors.test.ts} (77%) rename src/plugins/discover/public/application/angular/context/api/{context.successors.test.js => context.successors.test.ts} (79%) rename src/plugins/discover/public/application/angular/context/query/{actions.js => actions.tsx} (63%) rename src/plugins/discover/public/application/angular/context/query/{index.js => index.ts} (83%) create mode 100644 src/plugins/discover/public/application/angular/context/query/state.ts delete mode 100644 src/plugins/discover/public/application/angular/context/query_parameters/actions.js create mode 100644 src/plugins/discover/public/application/angular/context/query_parameters/actions.ts rename src/plugins/discover/public/application/angular/context/query_parameters/{index.js => index.ts} (100%) create mode 100644 src/plugins/discover/public/application/angular/context_app_state.ts create mode 100644 src/plugins/usage_collection/server/collector/types.ts delete mode 100644 x-pack/plugins/actions/server/saved_objects/get_import_result_message.test.ts create mode 100644 x-pack/plugins/actions/server/saved_objects/get_import_warnings.test.ts rename x-pack/plugins/actions/server/saved_objects/{get_import_result_message.ts => get_import_warnings.ts} (57%) create mode 100644 x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts create mode 100644 x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.ts create mode 100644 x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts create mode 100644 x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.ts create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts create mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts create mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts create mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts rename x-pack/plugins/canvas/{server/lib => common/lib/request}/build_bool_array.ts (92%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/build_es_request.js (100%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/filters.ts (98%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/format_response.js (100%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/get_es_filter.ts (93%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/normalize_type.ts (100%) rename x-pack/plugins/canvas/{server/lib => common/lib/request}/sanitize_name.ts (100%) create mode 100644 x-pack/plugins/canvas/public/services/search.ts create mode 100644 x-pack/plugins/canvas/public/services/stubs/search.ts create mode 100644 x-pack/plugins/canvas/server/lib/essql_strategy.ts create mode 100644 x-pack/plugins/canvas/types/strategy.ts create mode 100644 x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts create mode 100644 x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/search_box.mock.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/constants.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/constants.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/constants.tsx rename x-pack/plugins/enterprise_search/public/applications/shared/schema/{schema_add_field_modal.test.tsx => add_field_modal/index.test.tsx} (74%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/constants.ts rename x-pack/plugins/enterprise_search/public/applications/shared/schema/{schema_errors_accordion.test.tsx => errors_accordion/index.test.tsx} (55%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/schema_errors_accordion.scss delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.scss delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.tsx delete mode 100644 x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@events.yaml delete mode 100644 x-pack/plugins/fleet/server/services/agents/events.ts create mode 100644 x-pack/plugins/fleet/server/services/hosts_utils.test.ts create mode 100644 x-pack/plugins/fleet/server/services/hosts_utils.ts delete mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts rename x-pack/plugins/{apm/server/lib/alerts/create_apm_lifecycle_rule_type.ts => index_lifecycle_management/__jest__/client_integration/helpers/types.ts} (53%) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx create mode 100644 x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx delete mode 100644 x-pack/plugins/observability/common/rules/observability_rule_field_map.ts create mode 100644 x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts delete mode 100644 x-pack/plugins/observability/public/rules/formatter_rule_registry.ts delete mode 100644 x-pack/plugins/observability/public/rules/observability_rule_registry_mock.ts create mode 100644 x-pack/plugins/observability/public/rules/observability_rule_type_registry_mock.ts create mode 100644 x-pack/plugins/remote_clusters/server/routes/api/types.ts create mode 100644 x-pack/plugins/rule_registry/common/assets.ts create mode 100644 x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts create mode 100644 x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts rename x-pack/plugins/rule_registry/common/{field_map => assets/field_maps}/ecs_field_map.ts (100%) create mode 100644 x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts create mode 100644 x-pack/plugins/rule_registry/common/assets/index_templates/base_index_template.ts rename x-pack/plugins/rule_registry/{server/rule_registry/defaults/ilm_policy.ts => common/assets/lifecycle_policies/default_lifecycle_policy.ts} (86%) delete mode 100644 x-pack/plugins/rule_registry/common/field_map/base_rule_field_map.ts rename x-pack/plugins/rule_registry/{server/rule_registry/field_map => common}/mapping_from_field_map.ts (79%) create mode 100644 x-pack/plugins/rule_registry/common/parse_technical_fields.ts create mode 100644 x-pack/plugins/rule_registry/common/technical_rule_data_field_names.ts create mode 100644 x-pack/plugins/rule_registry/common/types.ts delete mode 100644 x-pack/plugins/rule_registry/public/index.ts delete mode 100644 x-pack/plugins/rule_registry/public/plugin.ts delete mode 100644 x-pack/plugins/rule_registry/public/rule_registry/index.ts delete mode 100644 x-pack/plugins/rule_registry/public/rule_registry/types.ts create mode 100644 x-pack/plugins/rule_registry/server/rule_data_client/index.ts create mode 100644 x-pack/plugins/rule_registry/server/rule_data_client/types.ts create mode 100644 x-pack/plugins/rule_registry/server/rule_data_plugin_service/index.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/index.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/types.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_registry/index.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_registry/types.ts create mode 100644 x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts create mode 100644 x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts create mode 100644 x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts create mode 100644 x-pack/plugins/security/public/management/roles/indices_api_client.test.ts create mode 100644 x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts rename x-pack/plugins/security_solution/cypress/integration/detection_alerts/{alerts_detection_callouts_readonly.spec.ts => missing_privileges_callout.spec.ts} (72%) create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/comma_separated_values.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts delete mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/translations.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/translations.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/user_privileges/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.mock.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.tsx create mode 100644 x-pack/plugins/security_solution/public/management/common/breadcrumbs.ts create mode 100644 x-pack/plugins/security_solution/public/management/state/async_resource_builders.ts create mode 100644 x-pack/plugins/security_solution/public/management/state/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ftp_file_transfer_protocol_activity_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_irc_internet_relay_chat_protocol_activity_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_port_8000_activity_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_pptp_point_to_point_tunneling_protocol_activity.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_proxy_port_activity_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_smtp_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sql_server_port_activity_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_from_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_to_the_internet.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tor_activity_to_the_internet.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tunneling_via_earthworm.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_persistence_network_logon_provider_modification.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_tcpdump_activity.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_base64_encoding_or_decoding_activity.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_via_trusted_developer_utilities.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_hex_encoding_or_decoding_activity.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_external_ip_lookup.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_public_ip_reconnaissance.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_process_discovery_via_tasklist_command.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_query_registry_via_reg.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_commmand.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_powershell.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_net_com_assemblies.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_rdp_remote_desktop_protocol_to_the_internet.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_defense_evasion_lanman_nullsessionpipe_modification.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_evasion_rdp_shadowing.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_local_service_commands.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_service_control_spawned_script_int.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_denies.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_events.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_rare_destination_country.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_spike_in_traffic_to_a_country.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_cron_jobs_creation_and_runtime.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_evasion_registry_startup_shell_folder_modified.json delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_kernel_module_activity.json rename x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/{persistence_local_scheduled_task_commands.json => persistence_local_scheduled_job_creation.json} (53%) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_creation.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_bits_job_notify_command.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_wmi_stdregprov_run_services.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/threat_intel_module_match.json create mode 100644 x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/__snapshots__/geo_index_pattern_select.test.tsx.snap create mode 100644 x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.test.tsx create mode 100644 x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_shapes.json create mode 100644 x-pack/plugins/transform/common/utils/field_utils.test.ts create mode 100644 x-pack/plugins/transform/common/utils/field_utils.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx create mode 100644 x-pack/test/api_integration/apis/file_upload/index_exists.ts create mode 100644 x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors_fleet.ts create mode 100644 x-pack/test/case_api_integration/basic/tests/cases/alerts/get_cases.ts create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts create mode 100644 x-pack/test/fleet_api_integration/apis/outputs/crud.ts create mode 100644 x-pack/test/fleet_api_integration/apis/outputs/index.js create mode 100644 x-pack/test/functional/apps/discover/saved_searches.ts create mode 100644 x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts create mode 100644 x-pack/test/functional/es_archives/security_solution/anomalies/data.json.gz create mode 100644 x-pack/test/functional/es_archives/security_solution/anomalies/mappings.json create mode 100644 x-pack/test/functional/es_archives/uptime/blank_data_stream/data.json create mode 100644 x-pack/test/functional/es_archives/uptime/blank_data_stream/mappings.json create mode 100644 x-pack/test/functional/services/ml/resources/trained_model_definitions/minimum_valid_config_classification.json.gz.b64 create mode 100644 x-pack/test/functional/services/ml/resources/trained_model_definitions/minimum_valid_config_regression.json.gz.b64 create mode 100644 x-pack/test/functional/services/ml/trained_models.ts create mode 100644 x-pack/test/security_solution_cypress/es_archives/unmapped_fields/data.json create mode 100644 x-pack/test/security_solution_cypress/es_archives/unmapped_fields/mappings.json diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es index b22406e389276..d56ec61314ac7 100644 --- a/.ci/es-snapshots/Jenkinsfile_verify_es +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -42,8 +42,9 @@ kibanaPipeline(timeoutMinutes: 210) { } task { - kibanaPipeline.buildXpack(10) + kibanaPipeline.buildXpack(10, true) tasks.xpackCiGroups() + tasks.xpackCiGroupDocker() } } } diff --git a/.ci/jobs.yml b/.ci/jobs.yml index 6aa93d4a1056a..1440c6870a86d 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -33,6 +33,7 @@ JOB: - x-pack-ciGroup11 - x-pack-ciGroup12 - x-pack-ciGroup13 + - x-pack-ciGroupDocker - x-pack-accessibility - x-pack-visualRegression diff --git a/.eslintrc.js b/.eslintrc.js index 211aed1da7279..20875a2c2913d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1256,6 +1256,22 @@ module.exports = { }, }, + /** + * Discover overrides + */ + { + files: ['src/plugins/discover/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': false, + }, + ], + }, + }, + /** * Enterprise Search overrides * NOTE: We also have a single rule at the bottom of the file that diff --git a/BUILD.bazel b/BUILD.bazel index 4502daeaacb59..1f6e3030e5d0c 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -3,6 +3,7 @@ exports_files( [ "tsconfig.base.json", + "tsconfig.browser.json", "tsconfig.json", "package.json" ], diff --git a/api_docs/spaces.json b/api_docs/spaces.json index 299bba81678a0..d633b0e653e17 100644 --- a/api_docs/spaces.json +++ b/api_docs/spaces.json @@ -38,7 +38,9 @@ }, ">" ], - "description": [], + "description": [ + "the space." + ], "source": { "path": "x-pack/plugins/spaces/public/space_avatar/space_attributes.ts", "lineNumber": 24 @@ -88,7 +90,9 @@ }, ">" ], - "description": [], + "description": [ + "the space." + ], "source": { "path": "x-pack/plugins/spaces/public/space_avatar/space_attributes.ts", "lineNumber": 64 @@ -138,7 +142,9 @@ }, ">" ], - "description": [], + "description": [ + "the space." + ], "source": { "path": "x-pack/plugins/spaces/public/space_avatar/space_attributes.ts", "lineNumber": 43 @@ -176,7 +182,9 @@ "text": "Space" } ], - "description": [], + "description": [ + "\nResponse format when querying for spaces." + ], "tags": [], "children": [ { @@ -184,10 +192,12 @@ "id": "def-public.GetSpaceResult.authorizedPurposes", "type": "Object", "label": "authorizedPurposes", - "description": [], + "description": [ + "\nA set of flags indicating which purposes the user is authorized for." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 22 + "lineNumber": 51 }, "signature": [ "Record<", @@ -204,7 +214,7 @@ ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 21 + "lineNumber": 47 }, "initialIsOpen": false }, @@ -212,7 +222,9 @@ "id": "def-public.Space", "type": "Interface", "label": "Space", - "description": [], + "description": [ + "\nA Kibana Space." + ], "tags": [], "children": [ { @@ -220,10 +232,12 @@ "id": "def-public.Space.id", "type": "string", "label": "id", - "description": [], + "description": [ + "\nThe unique identifier for this space.\nThe id becomes part of the \"URL Identifier\" of the space.\n\nExample: an id of `marketing` would result in the URL identifier of `/s/marketing`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 10 + "lineNumber": 19 } }, { @@ -231,10 +245,12 @@ "id": "def-public.Space.name", "type": "string", "label": "name", - "description": [], + "description": [ + "\nDisplay name for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 11 + "lineNumber": 24 } }, { @@ -242,10 +258,12 @@ "id": "def-public.Space.description", "type": "string", "label": "description", - "description": [], + "description": [ + "\nOptional description for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 12 + "lineNumber": 29 }, "signature": [ "string | undefined" @@ -256,10 +274,12 @@ "id": "def-public.Space.color", "type": "string", "label": "color", - "description": [], + "description": [ + "\nOptional color (hex code) for this space.\nIf neither `color` nor `imageUrl` is specified, then a color will be automatically generated." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 13 + "lineNumber": 35 }, "signature": [ "string | undefined" @@ -270,10 +290,12 @@ "id": "def-public.Space.initials", "type": "string", "label": "initials", - "description": [], + "description": [ + "\nOptional display initials for this space's avatar. Supports a maximum of 2 characters.\nIf initials are not provided, then they will be derived from the space name automatically.\n\nInitials are not displayed if an `imageUrl` has been specified." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 14 + "lineNumber": 43 }, "signature": [ "string | undefined" @@ -281,50 +303,58 @@ }, { "tags": [], - "id": "def-public.Space.disabledFeatures", - "type": "Array", - "label": "disabledFeatures", - "description": [], + "id": "def-public.Space.imageUrl", + "type": "string", + "label": "imageUrl", + "description": [ + "\nOptional base-64 encoded data image url to show as this space's avatar.\nThis setting takes precedence over any configured `color` or `initials`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 15 + "lineNumber": 49 }, "signature": [ - "string[]" + "string | undefined" ] }, { "tags": [], - "id": "def-public.Space._reserved", - "type": "CompoundType", - "label": "_reserved", - "description": [], + "id": "def-public.Space.disabledFeatures", + "type": "Array", + "label": "disabledFeatures", + "description": [ + "\nThe set of feature ids that should be hidden within this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 16 + "lineNumber": 54 }, "signature": [ - "boolean | undefined" + "string[]" ] }, { - "tags": [], - "id": "def-public.Space.imageUrl", - "type": "string", - "label": "imageUrl", - "description": [], + "tags": [ + "private" + ], + "id": "def-public.Space._reserved", + "type": "CompoundType", + "label": "_reserved", + "description": [ + "\nIndicates that this space is reserved (system controlled).\nReserved spaces cannot be created or deleted by end-users." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 17 + "lineNumber": 61 }, "signature": [ - "string | undefined" + "boolean | undefined" ] } ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 9 + "lineNumber": 12 }, "initialIsOpen": false } @@ -336,10 +366,12 @@ "type": "Type", "label": "GetAllSpacesPurpose", "tags": [], - "description": [], + "description": [ + "\nThe set of purposes to retrieve spaces:\n- `any`: retrieves all spaces the user is authorized to see.\n- `copySavedObjectsIntoSpace`: retrieves all spaces the user is authorized to copy saved objects into.\n- `findSavedObjects`: retrieves all spaces the user is authorized to search within.\n- `shareSavedObjectsIntoSpace`: retrieves all spaces the user is authorized to share saved objects into." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 15 + "lineNumber": 38 }, "signature": [ "\"any\" | \"copySavedObjectsIntoSpace\" | \"findSavedObjects\" | \"shareSavedObjectsIntoSpace\"" @@ -353,10 +385,12 @@ "type": "Type", "label": "SpacesPluginSetup", "tags": [], - "description": [], + "description": [ + "\nSetup contract for the Spaces plugin." + ], "source": { "path": "x-pack/plugins/spaces/public/plugin.tsx", - "lineNumber": 39 + "lineNumber": 42 }, "signature": [ "{}" @@ -369,10 +403,12 @@ "type": "Type", "label": "SpacesPluginStart", "tags": [], - "description": [], + "description": [ + "\nStart contract for the Spaces plugin." + ], "source": { "path": "x-pack/plugins/spaces/public/plugin.tsx", - "lineNumber": 40 + "lineNumber": 47 }, "signature": [ "SpacesApi" @@ -391,7 +427,9 @@ "signature": [ "(basePath: string, spaceId: string, requestedPath: string) => string" ], - "description": [], + "description": [ + "\nGiven a server base path, space id, and requested resource, this will construct a space-aware path\nthat includes a URL identifier with the space id.\n" + ], "children": [ { "id": "def-server.addSpaceIdToPath.$1", @@ -401,10 +439,12 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the server's base path." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 44 + "lineNumber": 62 } }, { @@ -415,10 +455,12 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the space id." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 45 + "lineNumber": 63 } }, { @@ -429,18 +471,22 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the requested path (e.g. `/app/dashboard`)." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 46 + "lineNumber": 64 } } ], "tags": [], - "returnComment": [], + "returnComment": [ + "the space-aware version of the requested path, inclusive of the server's base path." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 43 + "lineNumber": 61 }, "initialIsOpen": false } @@ -450,32 +496,42 @@ "id": "def-server.GetAllSpacesOptions", "type": "Interface", "label": "GetAllSpacesOptions", - "description": [], + "description": [ + "\nControls how spaces are retrieved." + ], "tags": [], "children": [ { - "tags": [], + "tags": [ + "see" + ], "id": "def-server.GetAllSpacesOptions.purpose", "type": "CompoundType", "label": "purpose", - "description": [], + "description": [ + "\nAn optional purpose describing how the set of spaces will be used.\nThe default purpose (`any`) will retrieve all spaces the user is authorized to see,\nwhereas a more specific purpose will retrieve all spaces the user is authorized to perform a specific action within.\n" + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 11 + "lineNumber": 21 }, "signature": [ "\"any\" | \"copySavedObjectsIntoSpace\" | \"findSavedObjects\" | \"shareSavedObjectsIntoSpace\" | undefined" ] }, { - "tags": [], + "tags": [ + "see" + ], "id": "def-server.GetAllSpacesOptions.includeAuthorizedPurposes", "type": "CompoundType", "label": "includeAuthorizedPurposes", - "description": [], + "description": [ + "\nSet to true to return a set of flags indicating which purposes the user is authorized for.\n" + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 12 + "lineNumber": 28 }, "signature": [ "boolean | undefined" @@ -484,7 +540,7 @@ ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 10 + "lineNumber": 13 }, "initialIsOpen": false }, @@ -509,7 +565,9 @@ "text": "Space" } ], - "description": [], + "description": [ + "\nResponse format when querying for spaces." + ], "tags": [], "children": [ { @@ -517,10 +575,12 @@ "id": "def-server.GetSpaceResult.authorizedPurposes", "type": "Object", "label": "authorizedPurposes", - "description": [], + "description": [ + "\nA set of flags indicating which purposes the user is authorized for." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 22 + "lineNumber": 51 }, "signature": [ "Record<", @@ -537,7 +597,289 @@ ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 21 + "lineNumber": 47 + }, + "initialIsOpen": false + }, + { + "id": "def-server.ISpacesClient", + "type": "Interface", + "label": "ISpacesClient", + "description": [ + "\nClient interface for interacting with spaces." + ], + "tags": [], + "children": [ + { + "id": "def-server.ISpacesClient.getAll", + "type": "Function", + "label": "getAll", + "signature": [ + "(options?: ", + { + "pluginId": "spaces", + "scope": "common", + "docId": "kibSpacesPluginApi", + "section": "def-common.GetAllSpacesOptions", + "text": "GetAllSpacesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "spaces", + "scope": "common", + "docId": "kibSpacesPluginApi", + "section": "def-common.GetSpaceResult", + "text": "GetSpaceResult" + }, + "[]>" + ], + "description": [ + "\nRetrieve all available spaces." + ], + "children": [ + { + "id": "def-server.ISpacesClient.getAll.$1", + "type": "Object", + "label": "options", + "isRequired": false, + "signature": [ + { + "pluginId": "spaces", + "scope": "common", + "docId": "kibSpacesPluginApi", + "section": "def-common.GetAllSpacesOptions", + "text": "GetAllSpacesOptions" + }, + " | undefined" + ], + "description": [ + "controls which spaces are retrieved." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 34 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 34 + } + }, + { + "id": "def-server.ISpacesClient.get", + "type": "Function", + "label": "get", + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + }, + ">" + ], + "description": [ + "\nRetrieve a space by its id." + ], + "children": [ + { + "id": "def-server.ISpacesClient.get.$1", + "type": "string", + "label": "id", + "isRequired": true, + "signature": [ + "string" + ], + "description": [ + "the space id." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 40 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 40 + } + }, + { + "id": "def-server.ISpacesClient.create", + "type": "Function", + "label": "create", + "signature": [ + "(space: ", + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + }, + ") => Promise<", + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + }, + ">" + ], + "description": [ + "\nCreates a space." + ], + "children": [ + { + "id": "def-server.ISpacesClient.create.$1", + "type": "Object", + "label": "space", + "isRequired": true, + "signature": [ + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + } + ], + "description": [ + "the space to create." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 46 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 46 + } + }, + { + "id": "def-server.ISpacesClient.update", + "type": "Function", + "label": "update", + "signature": [ + "(id: string, space: ", + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + }, + ") => Promise<", + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + }, + ">" + ], + "description": [ + "\nUpdates a space." + ], + "children": [ + { + "id": "def-server.ISpacesClient.update.$1", + "type": "string", + "label": "id", + "isRequired": true, + "signature": [ + "string" + ], + "description": [ + "the id of the space to update." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 53 + } + }, + { + "id": "def-server.ISpacesClient.update.$2", + "type": "Object", + "label": "space", + "isRequired": true, + "signature": [ + { + "pluginId": "spacesOss", + "scope": "common", + "docId": "kibSpacesOssPluginApi", + "section": "def-common.Space", + "text": "Space" + } + ], + "description": [ + "the updated space." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 53 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 53 + } + }, + { + "id": "def-server.ISpacesClient.delete", + "type": "Function", + "label": "delete", + "signature": [ + "(id: string) => Promise" + ], + "description": [ + "\nDeletes a space, and all saved objects belonging to that space." + ], + "children": [ + { + "id": "def-server.ISpacesClient.delete.$1", + "type": "string", + "label": "id", + "isRequired": true, + "signature": [ + "string" + ], + "description": [ + "the id of the space to delete." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 59 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 59 + } + } + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", + "lineNumber": 29 }, "initialIsOpen": false }, @@ -545,7 +887,9 @@ "id": "def-server.Space", "type": "Interface", "label": "Space", - "description": [], + "description": [ + "\nA Kibana Space." + ], "tags": [], "children": [ { @@ -553,10 +897,12 @@ "id": "def-server.Space.id", "type": "string", "label": "id", - "description": [], + "description": [ + "\nThe unique identifier for this space.\nThe id becomes part of the \"URL Identifier\" of the space.\n\nExample: an id of `marketing` would result in the URL identifier of `/s/marketing`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 10 + "lineNumber": 19 } }, { @@ -564,10 +910,12 @@ "id": "def-server.Space.name", "type": "string", "label": "name", - "description": [], + "description": [ + "\nDisplay name for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 11 + "lineNumber": 24 } }, { @@ -575,10 +923,12 @@ "id": "def-server.Space.description", "type": "string", "label": "description", - "description": [], + "description": [ + "\nOptional description for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 12 + "lineNumber": 29 }, "signature": [ "string | undefined" @@ -589,10 +939,12 @@ "id": "def-server.Space.color", "type": "string", "label": "color", - "description": [], + "description": [ + "\nOptional color (hex code) for this space.\nIf neither `color` nor `imageUrl` is specified, then a color will be automatically generated." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 13 + "lineNumber": 35 }, "signature": [ "string | undefined" @@ -603,10 +955,12 @@ "id": "def-server.Space.initials", "type": "string", "label": "initials", - "description": [], + "description": [ + "\nOptional display initials for this space's avatar. Supports a maximum of 2 characters.\nIf initials are not provided, then they will be derived from the space name automatically.\n\nInitials are not displayed if an `imageUrl` has been specified." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 14 + "lineNumber": 43 }, "signature": [ "string | undefined" @@ -614,50 +968,58 @@ }, { "tags": [], - "id": "def-server.Space.disabledFeatures", - "type": "Array", - "label": "disabledFeatures", - "description": [], + "id": "def-server.Space.imageUrl", + "type": "string", + "label": "imageUrl", + "description": [ + "\nOptional base-64 encoded data image url to show as this space's avatar.\nThis setting takes precedence over any configured `color` or `initials`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 15 + "lineNumber": 49 }, "signature": [ - "string[]" + "string | undefined" ] }, { "tags": [], - "id": "def-server.Space._reserved", - "type": "CompoundType", - "label": "_reserved", - "description": [], + "id": "def-server.Space.disabledFeatures", + "type": "Array", + "label": "disabledFeatures", + "description": [ + "\nThe set of feature ids that should be hidden within this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 16 + "lineNumber": 54 }, "signature": [ - "boolean | undefined" + "string[]" ] }, { - "tags": [], - "id": "def-server.Space.imageUrl", - "type": "string", - "label": "imageUrl", - "description": [], + "tags": [ + "private" + ], + "id": "def-server.Space._reserved", + "type": "CompoundType", + "label": "_reserved", + "description": [ + "\nIndicates that this space is reserved (system controlled).\nReserved spaces cannot be created or deleted by end-users." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 17 + "lineNumber": 61 }, "signature": [ - "string | undefined" + "boolean | undefined" ] } ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 9 + "lineNumber": 12 }, "initialIsOpen": false }, @@ -665,7 +1027,9 @@ "id": "def-server.SpacesServiceSetup", "type": "Interface", "label": "SpacesServiceSetup", - "description": [], + "description": [ + "\nThe Spaces service setup contract." + ], "tags": [], "children": [ { @@ -702,20 +1066,23 @@ }, "" ], - "description": [], + "description": [ + "the request." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 23 + "lineNumber": 27 } } ], "tags": [ - "deprecated" + "deprecated", + "removeBy" ], "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 23 + "lineNumber": 27 } }, { @@ -737,20 +1104,23 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the space id to convert." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 31 + "lineNumber": 36 } } ], "tags": [ - "deprecated" + "deprecated", + "removeBy" ], "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 31 + "lineNumber": 36 } }, { @@ -772,26 +1142,29 @@ "signature": [ "string | undefined" ], - "description": [], + "description": [ + "the namespace to convert." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 39 + "lineNumber": 45 } } ], "tags": [ - "deprecated" + "deprecated", + "removeBy" ], "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 39 + "lineNumber": 45 } } ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 16 + "lineNumber": 19 }, "initialIsOpen": false }, @@ -799,7 +1172,9 @@ "id": "def-server.SpacesServiceStart", "type": "Interface", "label": "SpacesServiceStart", - "description": [], + "description": [ + "\nThe Spaces service start contract." + ], "tags": [], "children": [ { @@ -812,7 +1187,7 @@ ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 46 + "lineNumber": 56 }, "signature": [ "(request: ", @@ -823,9 +1198,14 @@ "section": "def-server.KibanaRequest", "text": "KibanaRequest" }, - ") => Pick<", - "SpacesClient", - ", \"get\" | \"delete\" | \"create\" | \"update\" | \"getAll\">" + ") => ", + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.ISpacesClient", + "text": "ISpacesClient" + } ] }, { @@ -862,10 +1242,12 @@ }, "" ], - "description": [], + "description": [ + "the request." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 52 + "lineNumber": 62 } } ], @@ -873,7 +1255,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 52 + "lineNumber": 62 } }, { @@ -910,10 +1292,12 @@ }, "" ], - "description": [], + "description": [ + "the request." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 58 + "lineNumber": 68 } } ], @@ -921,7 +1305,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 58 + "lineNumber": 68 } }, { @@ -966,10 +1350,12 @@ }, "" ], - "description": [], + "description": [ + "the request." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 64 + "lineNumber": 74 } } ], @@ -977,7 +1363,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 64 + "lineNumber": 74 } }, { @@ -999,10 +1385,12 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the space id to convert." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 70 + "lineNumber": 80 } } ], @@ -1010,7 +1398,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 70 + "lineNumber": 80 } }, { @@ -1032,10 +1420,12 @@ "signature": [ "string | undefined" ], - "description": [], + "description": [ + "the namespace to convert." + ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 76 + "lineNumber": 86 } } ], @@ -1043,13 +1433,13 @@ "returnComment": [], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 76 + "lineNumber": 86 } } ], "source": { "path": "x-pack/plugins/spaces/server/spaces_service/spaces_service.ts", - "lineNumber": 42 + "lineNumber": 51 }, "initialIsOpen": false } @@ -1061,10 +1451,12 @@ "type": "Type", "label": "GetAllSpacesPurpose", "tags": [], - "description": [], + "description": [ + "\nThe set of purposes to retrieve spaces:\n- `any`: retrieves all spaces the user is authorized to see.\n- `copySavedObjectsIntoSpace`: retrieves all spaces the user is authorized to copy saved objects into.\n- `findSavedObjects`: retrieves all spaces the user is authorized to search within.\n- `shareSavedObjectsIntoSpace`: retrieves all spaces the user is authorized to share saved objects into." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 15 + "lineNumber": 38 }, "signature": [ "\"any\" | \"copySavedObjectsIntoSpace\" | \"findSavedObjects\" | \"shareSavedObjectsIntoSpace\"" @@ -1072,17 +1464,87 @@ "initialIsOpen": false }, { - "id": "def-server.ISpacesClient", + "id": "def-server.SpacesClientRepositoryFactory", "type": "Type", - "label": "ISpacesClient", - "tags": [], - "description": [], + "label": "SpacesClientRepositoryFactory", + "tags": [ + "private" + ], + "description": [ + "\nFor consumption by the security plugin only." + ], "source": { - "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client.ts", - "lineNumber": 27 + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts", + "lineNumber": 34 }, "signature": [ - "{ get: (id: string) => Promise; delete: (id: string) => Promise; create: (space: Space) => Promise; update: (id: string, space: Space) => Promise; getAll: (options?: GetAllSpacesOptions) => Promise; }" + "(request: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + ", savedObjectsStart: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsServiceStart", + "text": "SavedObjectsServiceStart" + }, + ") => Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsRepository", + "text": "SavedObjectsRepository" + }, + ", \"get\" | \"delete\" | \"create\" | \"bulkCreate\" | \"checkConflicts\" | \"deleteByNamespace\" | \"find\" | \"bulkGet\" | \"resolve\" | \"update\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"bulkUpdate\" | \"removeReferencesTo\" | \"incrementCounter\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" + ], + "initialIsOpen": false + }, + { + "id": "def-server.SpacesClientWrapper", + "type": "Type", + "label": "SpacesClientWrapper", + "tags": [ + "private" + ], + "description": [ + "\nFor consumption by the security plugin only." + ], + "source": { + "path": "x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts", + "lineNumber": 25 + }, + "signature": [ + "(request: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + ", baseClient: ", + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.ISpacesClient", + "text": "ISpacesClient" + }, + ") => ", + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.ISpacesClient", + "text": "ISpacesClient" + } ], "initialIsOpen": false } @@ -1092,18 +1554,25 @@ "id": "def-server.SpacesPluginSetup", "type": "Interface", "label": "SpacesPluginSetup", - "description": [], + "description": [ + "\nSetup contract for the Spaces plugin." + ], "tags": [], "children": [ { - "tags": [], + "tags": [ + "deprecated", + "removeBy" + ], "id": "def-server.SpacesPluginSetup.spacesService", "type": "Object", "label": "spacesService", - "description": [], + "description": [ + "\nService for interacting with spaces.\n" + ], "source": { "path": "x-pack/plugins/spaces/server/plugin.ts", - "lineNumber": 55 + "lineNumber": 64 }, "signature": [ { @@ -1116,27 +1585,43 @@ ] }, { - "tags": [], + "tags": [ + "private" + ], "id": "def-server.SpacesPluginSetup.spacesClient", "type": "Object", "label": "spacesClient", - "description": [], + "description": [ + "\nRegistries exposed for the security plugin to transparently provide authorization and audit logging." + ], "source": { "path": "x-pack/plugins/spaces/server/plugin.ts", - "lineNumber": 56 + "lineNumber": 70 }, "signature": [ "{ setClientRepositoryFactory: (factory: ", - "SpacesClientRepositoryFactory", + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.SpacesClientRepositoryFactory", + "text": "SpacesClientRepositoryFactory" + }, ") => void; registerClientWrapper: (wrapper: ", - "SpacesClientWrapper", + { + "pluginId": "spaces", + "scope": "server", + "docId": "kibSpacesPluginApi", + "section": "def-server.SpacesClientWrapper", + "text": "SpacesClientWrapper" + }, ") => void; }" ] } ], "source": { "path": "x-pack/plugins/spaces/server/plugin.ts", - "lineNumber": 54 + "lineNumber": 57 }, "lifecycle": "setup", "initialIsOpen": true @@ -1145,7 +1630,9 @@ "id": "def-server.SpacesPluginStart", "type": "Interface", "label": "SpacesPluginStart", - "description": [], + "description": [ + "\nStart contract for the Spaces plugin." + ], "tags": [], "children": [ { @@ -1153,10 +1640,12 @@ "id": "def-server.SpacesPluginStart.spacesService", "type": "Object", "label": "spacesService", - "description": [], + "description": [ + "Service for interacting with spaces." + ], "source": { "path": "x-pack/plugins/spaces/server/plugin.ts", - "lineNumber": 63 + "lineNumber": 89 }, "signature": [ { @@ -1171,7 +1660,7 @@ ], "source": { "path": "x-pack/plugins/spaces/server/plugin.ts", - "lineNumber": 62 + "lineNumber": 87 }, "lifecycle": "start", "initialIsOpen": true @@ -1187,7 +1676,9 @@ "signature": [ "(basePath: string, spaceId: string, requestedPath: string) => string" ], - "description": [], + "description": [ + "\nGiven a server base path, space id, and requested resource, this will construct a space-aware path\nthat includes a URL identifier with the space id.\n" + ], "children": [ { "id": "def-common.addSpaceIdToPath.$1", @@ -1197,10 +1688,12 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the server's base path." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 44 + "lineNumber": 62 } }, { @@ -1211,10 +1704,12 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the space id." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 45 + "lineNumber": 63 } }, { @@ -1225,18 +1720,22 @@ "signature": [ "string" ], - "description": [], + "description": [ + "the requested path (e.g. `/app/dashboard`)." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 46 + "lineNumber": 64 } } ], "tags": [], - "returnComment": [], + "returnComment": [ + "the space-aware version of the requested path, inclusive of the server's base path." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 43 + "lineNumber": 61 }, "initialIsOpen": false }, @@ -1247,7 +1746,9 @@ "signature": [ "(requestBasePath: string | null | undefined, serverBasePath: string | null | undefined) => { spaceId: string; pathHasExplicitSpaceIdentifier: boolean; }" ], - "description": [], + "description": [ + "\nExtracts the space id from the given path.\n" + ], "children": [ { "id": "def-common.getSpaceIdFromPath.$1", @@ -1257,10 +1758,12 @@ "signature": [ "string | null | undefined" ], - "description": [], + "description": [ + "The base path of the current request." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 13 + "lineNumber": 22 } }, { @@ -1271,18 +1774,24 @@ "signature": [ "string | null | undefined" ], - "description": [], + "description": [ + "The server's base path." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 14 + "lineNumber": 23 } } ], - "tags": [], - "returnComment": [], + "tags": [ + "private" + ], + "returnComment": [ + "the space id." + ], "source": { "path": "x-pack/plugins/spaces/common/lib/spaces_url_parser.ts", - "lineNumber": 12 + "lineNumber": 21 }, "initialIsOpen": false }, @@ -1346,32 +1855,42 @@ "id": "def-common.GetAllSpacesOptions", "type": "Interface", "label": "GetAllSpacesOptions", - "description": [], + "description": [ + "\nControls how spaces are retrieved." + ], "tags": [], "children": [ { - "tags": [], + "tags": [ + "see" + ], "id": "def-common.GetAllSpacesOptions.purpose", "type": "CompoundType", "label": "purpose", - "description": [], + "description": [ + "\nAn optional purpose describing how the set of spaces will be used.\nThe default purpose (`any`) will retrieve all spaces the user is authorized to see,\nwhereas a more specific purpose will retrieve all spaces the user is authorized to perform a specific action within.\n" + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 11 + "lineNumber": 21 }, "signature": [ "\"any\" | \"copySavedObjectsIntoSpace\" | \"findSavedObjects\" | \"shareSavedObjectsIntoSpace\" | undefined" ] }, { - "tags": [], + "tags": [ + "see" + ], "id": "def-common.GetAllSpacesOptions.includeAuthorizedPurposes", "type": "CompoundType", "label": "includeAuthorizedPurposes", - "description": [], + "description": [ + "\nSet to true to return a set of flags indicating which purposes the user is authorized for.\n" + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 12 + "lineNumber": 28 }, "signature": [ "boolean | undefined" @@ -1380,7 +1899,7 @@ ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 10 + "lineNumber": 13 }, "initialIsOpen": false }, @@ -1405,7 +1924,9 @@ "text": "Space" } ], - "description": [], + "description": [ + "\nResponse format when querying for spaces." + ], "tags": [], "children": [ { @@ -1413,10 +1934,12 @@ "id": "def-common.GetSpaceResult.authorizedPurposes", "type": "Object", "label": "authorizedPurposes", - "description": [], + "description": [ + "\nA set of flags indicating which purposes the user is authorized for." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 22 + "lineNumber": 51 }, "signature": [ "Record<", @@ -1433,7 +1956,7 @@ ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 21 + "lineNumber": 47 }, "initialIsOpen": false } @@ -1462,10 +1985,12 @@ "type": "Type", "label": "GetAllSpacesPurpose", "tags": [], - "description": [], + "description": [ + "\nThe set of purposes to retrieve spaces:\n- `any`: retrieves all spaces the user is authorized to see.\n- `copySavedObjectsIntoSpace`: retrieves all spaces the user is authorized to copy saved objects into.\n- `findSavedObjects`: retrieves all spaces the user is authorized to search within.\n- `shareSavedObjectsIntoSpace`: retrieves all spaces the user is authorized to share saved objects into." + ], "source": { "path": "x-pack/plugins/spaces/common/types.ts", - "lineNumber": 15 + "lineNumber": 38 }, "signature": [ "\"any\" | \"copySavedObjectsIntoSpace\" | \"findSavedObjects\" | \"shareSavedObjectsIntoSpace\"" diff --git a/api_docs/spaces_oss.json b/api_docs/spaces_oss.json index a93aeefb3db6f..8442d6cb5d5be 100644 --- a/api_docs/spaces_oss.json +++ b/api_docs/spaces_oss.json @@ -8,10 +8,10 @@ "id": "def-public.LegacyUrlConflictProps", "type": "Interface", "label": "LegacyUrlConflictProps", - "description": [], - "tags": [ - "public" + "description": [ + "\nProperties for the LegacyUrlConflict component." ], + "tags": [], "children": [ { "tags": [], @@ -23,7 +23,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 252 + "lineNumber": 257 }, "signature": [ "string | undefined" @@ -39,7 +39,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 256 + "lineNumber": 261 } }, { @@ -52,7 +52,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 260 + "lineNumber": 265 } }, { @@ -65,13 +65,13 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 264 + "lineNumber": 269 } } ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 245 + "lineNumber": 250 }, "initialIsOpen": false }, @@ -79,10 +79,10 @@ "id": "def-public.ShareToSpaceFlyoutProps", "type": "Interface", "label": "ShareToSpaceFlyoutProps", - "description": [], - "tags": [ - "public" + "description": [ + "\nProperties for the ShareToSpaceFlyout." ], + "tags": [], "children": [ { "tags": [], @@ -94,7 +94,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 130 + "lineNumber": 135 }, "signature": [ { @@ -116,7 +116,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 136 + "lineNumber": 141 }, "signature": [ "string | undefined" @@ -132,7 +132,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 142 + "lineNumber": 147 }, "signature": [ "string | undefined" @@ -148,7 +148,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 149 + "lineNumber": 154 }, "signature": [ "boolean | undefined" @@ -164,7 +164,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 156 + "lineNumber": 161 }, "signature": [ "boolean | undefined" @@ -180,7 +180,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 164 + "lineNumber": 169 }, "signature": [ "\"within-space\" | \"outside-space\" | undefined" @@ -196,7 +196,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 170 + "lineNumber": 175 }, "signature": [ "((spacesToAdd: string[], spacesToRemove: string[]) => Promise) | undefined" @@ -212,7 +212,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 174 + "lineNumber": 179 }, "signature": [ "(() => void) | undefined" @@ -228,7 +228,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 178 + "lineNumber": 183 }, "signature": [ "(() => void) | undefined" @@ -237,7 +237,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 126 + "lineNumber": 131 }, "initialIsOpen": false }, @@ -245,10 +245,10 @@ "id": "def-public.ShareToSpaceSavedObjectTarget", "type": "Interface", "label": "ShareToSpaceSavedObjectTarget", - "description": [], - "tags": [ - "public" + "description": [ + "\nDescribes the target saved object during a share operation." ], + "tags": [], "children": [ { "tags": [], @@ -260,7 +260,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 188 + "lineNumber": 193 } }, { @@ -273,7 +273,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 192 + "lineNumber": 197 } }, { @@ -286,7 +286,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 196 + "lineNumber": 201 }, "signature": [ "string[]" @@ -302,7 +302,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 202 + "lineNumber": 207 }, "signature": [ "string | undefined" @@ -318,7 +318,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 208 + "lineNumber": 213 }, "signature": [ "string | undefined" @@ -334,7 +334,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 214 + "lineNumber": 219 }, "signature": [ "string | undefined" @@ -343,7 +343,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 184 + "lineNumber": 189 }, "initialIsOpen": false }, @@ -351,20 +351,22 @@ "id": "def-public.SpaceAvatarProps", "type": "Interface", "label": "SpaceAvatarProps", - "description": [], - "tags": [ - "public" + "description": [ + "\nProperties for the SpaceAvatar component." ], + "tags": [], "children": [ { "tags": [], "id": "def-public.SpaceAvatarProps.space", "type": "Object", "label": "space", - "description": [], + "description": [ + "The space to represent with an avatar." + ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 271 + "lineNumber": 277 }, "signature": [ "Partial<", @@ -383,10 +385,12 @@ "id": "def-public.SpaceAvatarProps.size", "type": "CompoundType", "label": "size", - "description": [], + "description": [ + "The size of the avatar." + ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 272 + "lineNumber": 280 }, "signature": [ "\"m\" | \"s\" | \"l\" | \"xl\" | undefined" @@ -397,10 +401,12 @@ "id": "def-public.SpaceAvatarProps.className", "type": "string", "label": "className", - "description": [], + "description": [ + "Optional CSS class(es) to apply." + ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 273 + "lineNumber": 283 }, "signature": [ "string | undefined" @@ -416,7 +422,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 279 + "lineNumber": 290 }, "signature": [ "boolean | undefined" @@ -425,7 +431,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 270 + "lineNumber": 275 }, "initialIsOpen": false }, @@ -433,10 +439,10 @@ "id": "def-public.SpaceListProps", "type": "Interface", "label": "SpaceListProps", - "description": [], - "tags": [ - "public" + "description": [ + "\nProperties for the SpaceList component." ], + "tags": [], "children": [ { "tags": [], @@ -448,7 +454,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 224 + "lineNumber": 229 }, "signature": [ "string[]" @@ -464,7 +470,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 231 + "lineNumber": 236 }, "signature": [ "number | undefined" @@ -480,7 +486,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 239 + "lineNumber": 244 }, "signature": [ "\"within-space\" | \"outside-space\" | undefined" @@ -489,7 +495,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 220 + "lineNumber": 225 }, "initialIsOpen": false }, @@ -497,20 +503,22 @@ "id": "def-public.SpacesApi", "type": "Interface", "label": "SpacesApi", - "description": [], - "tags": [ - "public" + "description": [ + "\nClient-side Spaces API." ], + "tags": [], "children": [ { "tags": [], "id": "def-public.SpacesApi.activeSpace$", "type": "Object", "label": "activeSpace$", - "description": [], + "description": [ + "\nObservable representing the currently active space.\nThe details of the space can change without a full page reload (such as display name, color, etc.)" + ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 18 + "lineNumber": 22 }, "signature": [ "Observable", @@ -540,13 +548,15 @@ }, ">" ], - "description": [], + "description": [ + "\nRetrieve the currently active space." + ], "children": [], "tags": [], "returnComment": [], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 19 + "lineNumber": 27 } }, { @@ -555,11 +565,11 @@ "type": "Object", "label": "ui", "description": [ - "\nUI API to use to add spaces capabilities to an application" + "\nUI components and services to add spaces capabilities to an application." ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 23 + "lineNumber": 32 }, "signature": [ { @@ -582,10 +592,10 @@ "id": "def-public.SpacesApiUi", "type": "Interface", "label": "SpacesApiUi", - "description": [], - "tags": [ - "public" + "description": [ + "\nUI components and services to add spaces capabilities to an application." ], + "tags": [], "children": [ { "tags": [], @@ -593,11 +603,11 @@ "type": "Object", "label": "components", "description": [ - "\nLazy-loadable {@link SpacesApiUiComponent | React components} to support the spaces feature." + "\nLazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature." ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 40 + "lineNumber": 47 }, "signature": [ { @@ -619,7 +629,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 61 + "lineNumber": 68 }, "signature": [ "(path: string, objectNoun?: string | undefined) => Promise" @@ -628,7 +638,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 36 + "lineNumber": 43 }, "initialIsOpen": false }, @@ -637,11 +647,9 @@ "type": "Interface", "label": "SpacesApiUiComponent", "description": [ - "\nReact UI components to be used to display the spaces feature in any application.\n" - ], - "tags": [ - "public" + "\nReact UI components to be used to display the Spaces feature in any application." ], + "tags": [], "children": [ { "tags": [], @@ -653,7 +661,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 73 + "lineNumber": 78 }, "signature": [ { @@ -684,7 +692,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 79 + "lineNumber": 84 }, "signature": [ { @@ -715,7 +723,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 88 + "lineNumber": 93 }, "signature": [ { @@ -746,7 +754,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 106 + "lineNumber": 111 }, "signature": [ { @@ -777,7 +785,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 110 + "lineNumber": 115 }, "signature": [ { @@ -801,7 +809,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 69 + "lineNumber": 74 }, "initialIsOpen": false }, @@ -826,7 +834,9 @@ "text": "SpacesApi" } ], - "description": [], + "description": [ + "\nOSS Spaces plugin start contract when the Spaces feature is enabled." + ], "tags": [], "children": [ { @@ -834,10 +844,12 @@ "id": "def-public.SpacesAvailableStartContract.isSpacesAvailable", "type": "boolean", "label": "isSpacesAvailable", - "description": [], + "description": [ + "Indicates if the Spaces feature is enabled." + ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 12 + "lineNumber": 16 }, "signature": [ "true" @@ -846,7 +858,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 11 + "lineNumber": 14 }, "initialIsOpen": false }, @@ -854,10 +866,10 @@ "id": "def-public.SpacesContextProps", "type": "Interface", "label": "SpacesContextProps", - "description": [], - "tags": [ - "public" + "description": [ + "\nProperties for the SpacesContext." ], + "tags": [], "children": [ { "tags": [], @@ -869,7 +881,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 120 + "lineNumber": 125 }, "signature": [ "string | undefined" @@ -878,7 +890,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 116 + "lineNumber": 121 }, "initialIsOpen": false }, @@ -886,18 +898,25 @@ "id": "def-public.SpacesUnavailableStartContract", "type": "Interface", "label": "SpacesUnavailableStartContract", - "description": [], - "tags": [], + "description": [ + "\nOSS Spaces plugin start contract when the Spaces feature is disabled." + ], + "tags": [ + "deprecated", + "removeBy" + ], "children": [ { "tags": [], "id": "def-public.SpacesUnavailableStartContract.isSpacesAvailable", "type": "boolean", "label": "isSpacesAvailable", - "description": [], + "description": [ + "Indicates if the Spaces feature is enabled." + ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 16 + "lineNumber": 26 }, "signature": [ "false" @@ -906,7 +925,7 @@ ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 15 + "lineNumber": 24 }, "initialIsOpen": false } @@ -917,15 +936,13 @@ "id": "def-public.LazyComponentFn", "type": "Type", "label": "LazyComponentFn", - "tags": [ - "public" - ], + "tags": [], "description": [ - "\nFunction that returns a promise for a lazy-loadable component.\n" + "\nFunction that returns a promise for a lazy-loadable component." ], "source": { "path": "src/plugins/spaces_oss/public/api.ts", - "lineNumber": 31 + "lineNumber": 38 }, "signature": [ "(props: T) => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" @@ -938,7 +955,9 @@ "id": "def-public.SpacesOssPluginSetup", "type": "Interface", "label": "SpacesOssPluginSetup", - "description": [], + "description": [ + "\nOSS Spaces plugin setup contract." + ], "tags": [], "children": [ { @@ -957,7 +976,7 @@ ") => void" ], "description": [ - "\nRegister a provider for the Spaces API.\n\nOnly one provider can be registered, subsequent calls to this method will fail." + "\nRegister a provider for the Spaces API.\n\nOnly one provider can be registered, subsequent calls to this method will fail.\n" ], "children": [ { @@ -974,24 +993,28 @@ "text": "SpacesApi" } ], - "description": [], + "description": [ + "the API provider." + ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 25 + "lineNumber": 42 } } ], - "tags": [], + "tags": [ + "private" + ], "returnComment": [], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 25 + "lineNumber": 42 } } ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 19 + "lineNumber": 32 }, "lifecycle": "setup", "initialIsOpen": true @@ -1001,10 +1024,12 @@ "type": "Type", "label": "SpacesOssPluginStart", "tags": [], - "description": [], + "description": [ + "\nOSS Spaces plugin start contract." + ], "source": { "path": "src/plugins/spaces_oss/public/types.ts", - "lineNumber": 28 + "lineNumber": 48 }, "signature": [ { @@ -1043,7 +1068,9 @@ "id": "def-common.Space", "type": "Interface", "label": "Space", - "description": [], + "description": [ + "\nA Kibana Space." + ], "tags": [], "children": [ { @@ -1051,10 +1078,12 @@ "id": "def-common.Space.id", "type": "string", "label": "id", - "description": [], + "description": [ + "\nThe unique identifier for this space.\nThe id becomes part of the \"URL Identifier\" of the space.\n\nExample: an id of `marketing` would result in the URL identifier of `/s/marketing`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 10 + "lineNumber": 19 } }, { @@ -1062,10 +1091,12 @@ "id": "def-common.Space.name", "type": "string", "label": "name", - "description": [], + "description": [ + "\nDisplay name for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 11 + "lineNumber": 24 } }, { @@ -1073,10 +1104,12 @@ "id": "def-common.Space.description", "type": "string", "label": "description", - "description": [], + "description": [ + "\nOptional description for this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 12 + "lineNumber": 29 }, "signature": [ "string | undefined" @@ -1087,10 +1120,12 @@ "id": "def-common.Space.color", "type": "string", "label": "color", - "description": [], + "description": [ + "\nOptional color (hex code) for this space.\nIf neither `color` nor `imageUrl` is specified, then a color will be automatically generated." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 13 + "lineNumber": 35 }, "signature": [ "string | undefined" @@ -1101,10 +1136,12 @@ "id": "def-common.Space.initials", "type": "string", "label": "initials", - "description": [], + "description": [ + "\nOptional display initials for this space's avatar. Supports a maximum of 2 characters.\nIf initials are not provided, then they will be derived from the space name automatically.\n\nInitials are not displayed if an `imageUrl` has been specified." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 14 + "lineNumber": 43 }, "signature": [ "string | undefined" @@ -1112,50 +1149,58 @@ }, { "tags": [], - "id": "def-common.Space.disabledFeatures", - "type": "Array", - "label": "disabledFeatures", - "description": [], + "id": "def-common.Space.imageUrl", + "type": "string", + "label": "imageUrl", + "description": [ + "\nOptional base-64 encoded data image url to show as this space's avatar.\nThis setting takes precedence over any configured `color` or `initials`." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 15 + "lineNumber": 49 }, "signature": [ - "string[]" + "string | undefined" ] }, { "tags": [], - "id": "def-common.Space._reserved", - "type": "CompoundType", - "label": "_reserved", - "description": [], + "id": "def-common.Space.disabledFeatures", + "type": "Array", + "label": "disabledFeatures", + "description": [ + "\nThe set of feature ids that should be hidden within this space." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 16 + "lineNumber": 54 }, "signature": [ - "boolean | undefined" + "string[]" ] }, { - "tags": [], - "id": "def-common.Space.imageUrl", - "type": "string", - "label": "imageUrl", - "description": [], + "tags": [ + "private" + ], + "id": "def-common.Space._reserved", + "type": "CompoundType", + "label": "_reserved", + "description": [ + "\nIndicates that this space is reserved (system controlled).\nReserved spaces cannot be created or deleted by end-users." + ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 17 + "lineNumber": 61 }, "signature": [ - "string | undefined" + "boolean | undefined" ] } ], "source": { "path": "src/plugins/spaces_oss/common/types.ts", - "lineNumber": 9 + "lineNumber": 12 }, "initialIsOpen": false } diff --git a/api_docs/usage_collection.json b/api_docs/usage_collection.json index a10e2e73f0098..440187d1083b0 100644 --- a/api_docs/usage_collection.json +++ b/api_docs/usage_collection.json @@ -14,7 +14,13 @@ "isRequired": true, "signature": [ "React.PropsWithChildren<", - "TrackApplicationViewProps", + { + "pluginId": "usageCollection", + "scope": "public", + "docId": "kibUsageCollectionPluginApi", + "section": "def-public.TrackApplicationViewProps", + "text": "TrackApplicationViewProps" + }, ">" ], "description": [], @@ -26,7 +32,13 @@ ], "signature": [ "(props: React.PropsWithChildren<", - "TrackApplicationViewProps", + { + "pluginId": "usageCollection", + "scope": "public", + "docId": "kibUsageCollectionPluginApi", + "section": "def-public.TrackApplicationViewProps", + "text": "TrackApplicationViewProps" + }, ">) => JSX.Element" ], "description": [ @@ -38,38 +50,75 @@ "lineNumber": 23 }, "tags": [ - "Link", "constructor" ], "returnComment": [], "initialIsOpen": false } ], - "interfaces": [], - "enums": [ + "interfaces": [ { - "id": "def-public.METRIC_TYPE", - "type": "Enum", - "label": "METRIC_TYPE", - "tags": [], - "description": [], + "id": "def-public.TrackApplicationViewProps", + "type": "Interface", + "label": "TrackApplicationViewProps", + "description": [ + "\nProps to provide to the {@link TrackApplicationView} component." + ], + "tags": [ + "public" + ], + "children": [ + { + "tags": [ + "public" + ], + "id": "def-public.TrackApplicationViewProps.viewId", + "type": "string", + "label": "viewId", + "description": [ + "\nThe name of the view to be tracked. The appId will be obtained automatically." + ], + "source": { + "path": "src/plugins/usage_collection/public/components/track_application_view/types.ts", + "lineNumber": 20 + } + }, + { + "tags": [ + "public" + ], + "id": "def-public.TrackApplicationViewProps.children", + "type": "CompoundType", + "label": "children", + "description": [ + "\nThe React component to be tracked." + ], + "source": { + "path": "src/plugins/usage_collection/public/components/track_application_view/types.ts", + "lineNumber": 25 + }, + "signature": [ + "React.ReactNode" + ] + } + ], "source": { - "path": "node_modules/@kbn/analytics/target/types/metrics/index.d.ts", - "lineNumber": 10 + "path": "src/plugins/usage_collection/public/components/track_application_view/types.ts", + "lineNumber": 15 }, - "signature": [ - "METRIC_TYPE" - ], "initialIsOpen": false } ], + "enums": [], "misc": [], "objects": [], "setup": { "id": "def-public.UsageCollectionSetup", "type": "Interface", "label": "UsageCollectionSetup", - "description": [], + "description": [ + "Public's setup APIs exposed by the UsageCollection Service" + ], "tags": [], "children": [ { @@ -77,10 +126,12 @@ "id": "def-public.UsageCollectionSetup.components", "type": "Object", "label": "components", - "description": [], + "description": [ + "Component helpers to track usage collection in the UI" + ], "source": { "path": "src/plugins/usage_collection/public/plugin.tsx", - "lineNumber": 35 + "lineNumber": 38 }, "signature": [ "{ ApplicationUsageTrackingProvider: React.FC<{}>; }" @@ -91,10 +142,12 @@ "id": "def-public.UsageCollectionSetup.reportUiCounter", "type": "Function", "label": "reportUiCounter", - "description": [], + "description": [ + "Report whenever a UI event occurs for UI counters to report it" + ], "source": { "path": "src/plugins/usage_collection/public/plugin.tsx", - "lineNumber": 38 + "lineNumber": 75 }, "signature": [ "(appName: string, type: ", @@ -105,7 +158,7 @@ ], "source": { "path": "src/plugins/usage_collection/public/plugin.tsx", - "lineNumber": 34 + "lineNumber": 36 }, "lifecycle": "setup", "initialIsOpen": true @@ -114,7 +167,9 @@ "id": "def-public.UsageCollectionStart", "type": "Interface", "label": "UsageCollectionStart", - "description": [], + "description": [ + "Public's start APIs exposed by the UsageCollection Service" + ], "tags": [], "children": [ { @@ -122,10 +177,12 @@ "id": "def-public.UsageCollectionStart.reportUiCounter", "type": "Function", "label": "reportUiCounter", - "description": [], + "description": [ + "Report whenever a UI event occurs for UI counters to report it" + ], "source": { "path": "src/plugins/usage_collection/public/plugin.tsx", - "lineNumber": 42 + "lineNumber": 86 }, "signature": [ "(appName: string, type: ", @@ -136,186 +193,310 @@ ], "source": { "path": "src/plugins/usage_collection/public/plugin.tsx", - "lineNumber": 41 + "lineNumber": 84 }, "lifecycle": "start", "initialIsOpen": true } }, "server": { - "classes": [ + "classes": [], + "functions": [], + "interfaces": [ { - "id": "def-server.Collector", - "type": "Class", - "tags": [], - "label": "Collector", - "description": [], + "id": "def-server.ICollector", + "type": "Interface", + "label": "ICollector", "signature": [ { "pluginId": "usageCollection", "scope": "server", "docId": "kibUsageCollectionPluginApi", - "section": "def-server.Collector", - "text": "Collector" + "section": "def-server.ICollector", + "text": "ICollector" }, "" ], + "description": [ + "\nCommon interface for Usage and Stats Collectors" + ], + "tags": [], "children": [ { "tags": [], - "id": "def-server.Collector.extendFetchContext", + "id": "def-server.ICollector.log", + "type": "Object", + "label": "log", + "description": [ + "Logger" + ], + "source": { + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 173 + }, + "signature": [ + "Logger" + ] + }, + { + "tags": [], + "id": "def-server.ICollector.extendFetchContext", "type": "CompoundType", "label": "extendFetchContext", - "description": [], + "description": [ + "\nThe options to extend the context provided to the `fetch` method: {@link CollectorOptionsFetchExtendedContext}." + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 144 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 178 }, "signature": [ - "CollectorOptionsFetchExtendedContext", - "" + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.CollectorOptionsFetchExtendedContext", + "text": "CollectorOptionsFetchExtendedContext" + }, + "" ] }, { "tags": [], - "id": "def-server.Collector.type", + "id": "def-server.ICollector.type", "type": "string", "label": "type", - "description": [], + "description": [ + "The registered type (aka name) of the collector" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 145 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 180 } }, { "tags": [], - "id": "def-server.Collector.init", - "type": "Object", - "label": "init", - "description": [], + "id": "def-server.ICollector.fetch", + "type": "Function", + "label": "fetch", + "description": [ + "\nThe actual logic that reports the Usage collection.\nIt will be called on every collection request.\nWhatever is returned in this method will be passed through as-is under\nthe {@link ICollector.type} key.\n" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 146 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 192 }, "signature": [ - "Function | undefined" + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.CollectorFetchMethod", + "text": "CollectorFetchMethod" + }, + "" ] }, { "tags": [], - "id": "def-server.Collector.fetch", + "id": "def-server.ICollector.isReady", "type": "Function", - "label": "fetch", - "description": [], + "label": "isReady", + "description": [ + "\nShould return `true` when it's safe to call the `fetch` method." + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 147 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 196 }, "signature": [ - "CollectorFetchMethod", - "" + "() => boolean | Promise" ] + } + ], + "source": { + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 171 + }, + "initialIsOpen": false + }, + { + "id": "def-server.IncrementCounterParams", + "type": "Interface", + "label": "IncrementCounterParams", + "description": [ + "\nDetails about the counter to be incremented" + ], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-server.IncrementCounterParams.counterName", + "type": "string", + "label": "counterName", + "description": [ + "The name of the counter" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 28 + } }, { "tags": [], - "id": "def-server.Collector.isReady", - "type": "Function", - "label": "isReady", - "description": [], + "id": "def-server.IncrementCounterParams.counterType", + "type": "string", + "label": "counterType", + "description": [ + "The counter type (\"count\" by default)" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 148 + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 30 }, "signature": [ - "() => boolean | Promise" + "string | undefined" ] }, { - "id": "def-server.Collector.Unnamed", - "type": "Function", - "label": "Constructor", + "tags": [], + "id": "def-server.IncrementCounterParams.incrementBy", + "type": "number", + "label": "incrementBy", + "description": [ + "Increment the counter by this number (1 if not specified)" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 32 + }, "signature": [ - "any" + "number | undefined" + ] + } + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 26 + }, + "initialIsOpen": false + }, + { + "id": "def-server.IUsageCounter", + "type": "Interface", + "label": "IUsageCounter", + "description": [ + "\nUsage Counter allows to keep track of any events that occur.\nBy calling {@link IUsageCounter.incrementCounter} devs can notify this\nAPI whenever the event happens." + ], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-server.IUsageCounter.incrementCounter", + "type": "Function", + "label": "incrementCounter", + "description": [ + "\nNotifies the counter about a new event happening so it can increase the count internally." ], - "description": [], - "children": [ + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 45 + }, + "signature": [ + "(params: ", { - "id": "def-server.Collector.Unnamed.$1", - "type": "Object", - "label": "log", - "isRequired": true, - "signature": [ - "Logger" - ], - "description": [], - "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 155 - } + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.IncrementCounterParams", + "text": "IncrementCounterParams" }, - { - "id": "def-server.Collector.Unnamed.$2", - "type": "CompoundType", - "label": "{\n type,\n init,\n fetch,\n isReady,\n extendFetchContext = {},\n ...options\n }", - "isRequired": true, - "signature": [ - { - "pluginId": "usageCollection", - "scope": "server", - "docId": "kibUsageCollectionPluginApi", - "section": "def-server.CollectorOptions", - "text": "CollectorOptions" - }, - "" - ], - "description": [], - "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 156 - } - } - ], - "tags": [ - "private" - ], - "returnComment": [], - "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 154 - } + ") => void" + ] } ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 143 + "path": "src/plugins/usage_collection/server/usage_counters/usage_counter.ts", + "lineNumber": 40 }, "initialIsOpen": false - } - ], - "functions": [], - "interfaces": [ + }, { - "id": "def-server.SchemaField", + "id": "def-server.UsageCountersSavedObjectAttributes", "type": "Interface", - "label": "SchemaField", - "description": [], + "label": "UsageCountersSavedObjectAttributes", + "signature": [ + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.UsageCountersSavedObjectAttributes", + "text": "UsageCountersSavedObjectAttributes" + }, + " extends ", + "SavedObjectAttributes" + ], + "description": [ + "\nThe attributes stored in the UsageCounters' SavedObjects" + ], "tags": [], "children": [ { "tags": [], - "id": "def-server.SchemaField.type", + "id": "def-server.UsageCountersSavedObjectAttributes.domainId", "type": "string", - "label": "type", - "description": [], + "label": "domainId", + "description": [ + "The domain ID registered in the Usage Counter" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 23 + } + }, + { + "tags": [], + "id": "def-server.UsageCountersSavedObjectAttributes.counterName", + "type": "string", + "label": "counterName", + "description": [ + "The counter name" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 25 + } + }, + { + "tags": [], + "id": "def-server.UsageCountersSavedObjectAttributes.counterType", + "type": "string", + "label": "counterType", + "description": [ + "The counter type" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 27 + } + }, + { + "tags": [], + "id": "def-server.UsageCountersSavedObjectAttributes.count", + "type": "number", + "label": "count", + "description": [ + "Number of times the event has occurred" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 33 + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 29 } } ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 32 + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 21 }, "initialIsOpen": false } @@ -327,10 +508,12 @@ "type": "Type", "label": "AllowedSchemaTypes", "tags": [], - "description": [], + "description": [ + "\nPossible type values in the schema" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 27 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 33 }, "signature": [ "\"boolean\" | \"date\" | \"text\" | \"keyword\" | \"long\" | \"double\" | \"short\" | \"integer\" | \"byte\" | \"float\"" @@ -346,26 +529,70 @@ "\nThe context for the `fetch` method: It includes the most commonly used clients in the collectors (ES and SO clients).\nBoth are scoped based on the request and the context:\n- When users are requesting a sample of data, it is scoped to their role to avoid exposing data they shouldn't read\n- When building the telemetry data payload to report to the remote cluster, the requests are scoped to the `kibana` internal user\n" ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 64 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 76 }, "signature": [ "{ esClient: ElasticsearchClient; soClient: SavedObjectsClientContract; } & (WithKibanaRequest extends true ? { kibanaRequest?: KibanaRequest | undefined; } : {})" ], "initialIsOpen": false }, + { + "id": "def-server.CollectorFetchMethod", + "type": "Type", + "label": "CollectorFetchMethod", + "tags": [], + "description": [ + "\nThe fetch method has the context of the Collector itself\n(this has access to all the properties of the collector like the logger)\nand the the first parameter is {@link CollectorFetchContext}." + ], + "source": { + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 104 + }, + "signature": [ + "(context: ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.CollectorFetchContext", + "text": "CollectorFetchContext" + }, + ") => TReturn | Promise" + ], + "initialIsOpen": false + }, { "id": "def-server.CollectorOptions", "type": "Type", "label": "CollectorOptions", "tags": [], - "description": [], + "description": [ + "\nOptions to instantiate a collector" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 111 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 135 }, "signature": [ - "{ type: string; init?: Function | undefined; isReady: () => Promise | boolean; schema?: MakeSchemaFrom | undefined; fetch: CollectorFetchMethod; } & ExtraOptions & (WithKibanaRequest extends true ? { extendFetchContext: CollectorOptionsFetchExtendedContext; } : { extendFetchContext?: CollectorOptionsFetchExtendedContext | undefined; })" + "{ type: string; isReady: () => Promise | boolean; schema?: MakeSchemaFrom | undefined; fetch: CollectorFetchMethod; } & ExtraOptions & (WithKibanaRequest extends true ? { extendFetchContext: CollectorOptionsFetchExtendedContext; } : { extendFetchContext?: CollectorOptionsFetchExtendedContext | undefined; })" + ], + "initialIsOpen": false + }, + { + "id": "def-server.CollectorOptionsFetchExtendedContext", + "type": "Type", + "label": "CollectorOptionsFetchExtendedContext", + "tags": [], + "description": [ + "\nThe options to extend the context provided to the `fetch` method." + ], + "source": { + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 125 + }, + "signature": [ + "ICollectorOptionsFetchExtendedContext & (WithKibanaRequest extends true ? Required, \"kibanaRequest\">> : {})" ], "initialIsOpen": false }, @@ -374,10 +601,12 @@ "type": "Type", "label": "MakeSchemaFrom", "tags": [], - "description": [], + "description": [ + "\nThe `schema` property in {@link CollectorOptions} must match the output of\nthe `fetch` method. This type helps ensure that is correct" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/collector.ts", - "lineNumber": 50 + "path": "src/plugins/usage_collection/server/collector/types.ts", + "lineNumber": 61 }, "signature": [ "{ [Key in keyof Required]: Required[Key] extends (infer U)[] ? { type: 'array'; items: RecursiveMakeSchemaFrom; } : RecursiveMakeSchemaFrom[Key]>; }" @@ -385,84 +614,234 @@ "initialIsOpen": false }, { - "id": "def-server.UsageCollectionSetup", + "tags": [], + "id": "def-server.USAGE_COUNTERS_SAVED_OBJECT_TYPE", + "type": "string", + "label": "USAGE_COUNTERS_SAVED_OBJECT_TYPE", + "description": [ + "The Saved Objects type for Usage Counters" + ], + "source": { + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 38 + }, + "signature": [ + "\"usage-counters\"" + ], + "initialIsOpen": false + }, + { + "id": "def-server.UsageCollectorOptions", "type": "Type", - "label": "UsageCollectionSetup", + "label": "UsageCollectorOptions", "tags": [], - "description": [], + "description": [ + "\nSame as {@link CollectorOptions} but with the `schema` property enforced" + ], "source": { - "path": "src/plugins/usage_collection/server/plugin.ts", - "lineNumber": 21 + "path": "src/plugins/usage_collection/server/collector/usage_collector.ts", + "lineNumber": 16 }, "signature": [ - "{ makeStatsCollector: (options: ", + "{ type: string; isReady: () => boolean | Promise; schema?: ", { "pluginId": "usageCollection", "scope": "server", "docId": "kibUsageCollectionPluginApi", - "section": "def-server.CollectorOptions", - "text": "CollectorOptions" + "section": "def-server.MakeSchemaFrom", + "text": "MakeSchemaFrom" }, - ") => ", + " | undefined; fetch: ", { "pluginId": "usageCollection", "scope": "server", "docId": "kibUsageCollectionPluginApi", - "section": "def-server.Collector", - "text": "Collector" + "section": "def-server.CollectorFetchMethod", + "text": "CollectorFetchMethod" }, - "; makeUsageCollector: (options: ", + "; } & ExtraOptions & (WithKibanaRequest extends true ? { extendFetchContext: ", { "pluginId": "usageCollection", "scope": "server", "docId": "kibUsageCollectionPluginApi", - "section": "def-server.UsageCollectorOptions", - "text": "UsageCollectorOptions" + "section": "def-server.CollectorOptionsFetchExtendedContext", + "text": "CollectorOptionsFetchExtendedContext" }, - ") => ", - "UsageCollector", - "; registerCollector: (collector: ", + "; } : { extendFetchContext?: ", { "pluginId": "usageCollection", "scope": "server", "docId": "kibUsageCollectionPluginApi", - "section": "def-server.Collector", - "text": "Collector" - } + "section": "def-server.CollectorOptionsFetchExtendedContext", + "text": "CollectorOptionsFetchExtendedContext" + }, + " | undefined; }) & Required, \"schema\">>" ], "initialIsOpen": false }, { - "id": "def-server.UsageCollectorOptions", + "id": "def-server.UsageCountersSavedObject", "type": "Type", - "label": "UsageCollectorOptions", + "label": "UsageCountersSavedObject", "tags": [], - "description": [], + "description": [ + "\nThe structure of the SavedObjects of type \"usage-counters\"" + ], "source": { - "path": "src/plugins/usage_collection/server/collector/usage_collector.ts", - "lineNumber": 13 + "path": "src/plugins/usage_collection/server/usage_counters/saved_objects.ts", + "lineNumber": 35 }, "signature": [ - "{ type: string; init?: Function | undefined; isReady: () => boolean | Promise; schema?: ", - { - "pluginId": "usageCollection", - "scope": "server", - "docId": "kibUsageCollectionPluginApi", - "section": "def-server.MakeSchemaFrom", - "text": "MakeSchemaFrom" - }, - " | undefined; fetch: ", - "CollectorFetchMethod", - "; } & ExtraOptions & (WithKibanaRequest extends true ? { extendFetchContext: ", - "CollectorOptionsFetchExtendedContext", - "; } : { extendFetchContext?: ", - "CollectorOptionsFetchExtendedContext", - " | undefined; }) & Required, \"schema\">>" + "SavedObject" ], "initialIsOpen": false } ], - "objects": [] + "objects": [], + "setup": { + "id": "def-server.UsageCollectionSetup", + "type": "Interface", + "label": "UsageCollectionSetup", + "description": [ + "Server's setup APIs exposed by the UsageCollection Service" + ], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-server.UsageCollectionSetup.createUsageCounter", + "type": "Function", + "label": "createUsageCounter", + "description": [ + "\nCreates and registers a usage counter to collect daily aggregated plugin counter events" + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 33 + }, + "signature": [ + "(type: string) => ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.IUsageCounter", + "text": "IUsageCounter" + } + ] + }, + { + "tags": [], + "id": "def-server.UsageCollectionSetup.getUsageCounterByType", + "type": "Function", + "label": "getUsageCounterByType", + "description": [ + "\nReturns a usage counter by type" + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 37 + }, + "signature": [ + "(type: string) => ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.IUsageCounter", + "text": "IUsageCounter" + }, + " | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UsageCollectionSetup.makeUsageCollector", + "type": "Function", + "label": "makeUsageCollector", + "description": [ + "\nCreates a usage collector to collect plugin telemetry data.\nregisterCollector must be called to connect the created collector with the service." + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 42 + }, + "signature": [ + "(options: ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.UsageCollectorOptions", + "text": "UsageCollectorOptions" + }, + ") => ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.ICollector", + "text": "ICollector" + }, + "" + ] + }, + { + "tags": [], + "id": "def-server.UsageCollectionSetup.registerCollector", + "type": "Function", + "label": "registerCollector", + "description": [ + "\nRegister a usage collector or a stats collector.\nUsed to connect the created collector to telemetry." + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 53 + }, + "signature": [ + "(collector: ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.ICollector", + "text": "ICollector" + }, + ") => void" + ] + }, + { + "tags": [], + "id": "def-server.UsageCollectionSetup.getCollectorByType", + "type": "Function", + "label": "getCollectorByType", + "description": [ + "\nReturns a usage collector by type" + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 59 + }, + "signature": [ + "(type: string) => ", + { + "pluginId": "usageCollection", + "scope": "server", + "docId": "kibUsageCollectionPluginApi", + "section": "def-server.ICollector", + "text": "ICollector" + }, + " | undefined" + ] + } + ], + "source": { + "path": "src/plugins/usage_collection/server/plugin.ts", + "lineNumber": 29 + }, + "lifecycle": "setup", + "initialIsOpen": true + } }, "common": { "classes": [], diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 5f2801990d4b4..62db0389c2d5c 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -22,13 +22,13 @@ import usageCollectionObj from './usage_collection.json'; ### Functions -### Enums - +### Interfaces + ## Server -### Classes - +### Setup + ### Interfaces diff --git a/docs/developer/getting-started/building-kibana.asciidoc b/docs/developer/getting-started/building-kibana.asciidoc index 04771b34bf69f..b015be79abca2 100644 --- a/docs/developer/getting-started/building-kibana.asciidoc +++ b/docs/developer/getting-started/building-kibana.asciidoc @@ -18,12 +18,12 @@ yarn build --help [discrete] === Building OS packages -Packages are built using fpm, dpkg, and rpm. Package building has only been tested on Linux and is not supported on any other platform. - +Packages are built using fpm, dpkg, and rpm, and Docker. Package building has only been tested on Linux and is not supported on any other platform. +Docker installation instructions can be found at https://docs.docker.com/engine/install/[Install Docker Engine]. [source,bash] ---- -apt-get install ruby-dev rpm +apt-get install ruby ruby-dev rpm dpkg build-essential gem install fpm -v 1.5.0 yarn build --skip-archives ---- diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc index 969226df53cb7..f4f226ec8728f 100644 --- a/docs/developer/getting-started/monorepo-packages.asciidoc +++ b/docs/developer/getting-started/monorepo-packages.asciidoc @@ -64,17 +64,24 @@ yarn kbn watch-bazel - @elastic/datemath - @elastic/eslint-config-kibana - @elastic/safer-lodash-set +- @kbn/analytics - @kbn/apm-config-loader - @kbn/apm-utils - @kbn/babel-code-parser - @kbn/babel-preset +- @kbn/config - @kbn/config-schema +- @kbn/crypto - @kbn/dev-utils +- @kbn/es - @kbn/eslint-import-resolver-kibana - @kbn/eslint-plugin-eslint - @kbn/expect - @kbn/legacy-logging - @kbn/logging +- @kbn/securitysolution-constants +- @kbn/securitysolution-utils +- @kbn/securitysolution-io-ts-utils - @kbn/std - @kbn/tinymath - @kbn/utility-types diff --git a/docs/developer/plugin/migrating-legacy-plugins-examples.asciidoc b/docs/developer/plugin/migrating-legacy-plugins-examples.asciidoc index 6361b3c921128..acc42ec91bb71 100644 --- a/docs/developer/plugin/migrating-legacy-plugins-examples.asciidoc +++ b/docs/developer/plugin/migrating-legacy-plugins-examples.asciidoc @@ -522,7 +522,7 @@ platform, these were referred to as `hidden` applications and were set via the `hidden` property in a {kib} plugin. Chromeless applications are also not displayed in the left navbar. -To mark an application as chromeless, specify `chromeless: false` when +To mark an application as chromeless, specify `chromeless: true` when registering your application to hide the chrome UI when the application is mounted: @@ -813,7 +813,7 @@ been renamed: `SavedObjectsType.management.importableAndExportable`. ---- `(doc: SavedObjectUnsanitizedDoc, log: SavedObjectsMigrationLogger) => SavedObjectUnsanitizedDoc;` ---- -In {kib} Platform, it is +In {kib} Platform, it is [source,typescript] ---- `(doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc;` @@ -1092,7 +1092,7 @@ the current user. [source,typescript] ---- -const request = client.asCurrentUser.ping({}, { +const request = client.asCurrentUser.ping({}, { headers: { authorization: 'foo', custom: 'bar', @@ -1143,9 +1143,9 @@ router.get( ==== Accessing the client from a collector's `fetch` method -At the moment, the `fetch` method's context receives preconfigured -<> for Elasticsearch and SavedObjects. -To help in the transition, both, the legacy (`callCluster`) and new clients are provided, +At the moment, the `fetch` method's context receives preconfigured +<> for Elasticsearch and SavedObjects. +To help in the transition, both, the legacy (`callCluster`) and new clients are provided, but we strongly discourage using the deprecated legacy ones for any new implementation. [source,typescript] @@ -1156,19 +1156,19 @@ usageCollection.makeUsageCollector({ schema: {...}, async fetch(context) { const { callCluster, esClient, soClient } = context; - + // Before: const result = callCluster('search', options) // After: const { body: result } = esClient.search(options); - + return result; } }); ---- -Regarding the `soClient`, it is encouraged to use it instead of the plugin's owned SavedObject's repository +Regarding the `soClient`, it is encouraged to use it instead of the plugin's owned SavedObject's repository as we used to do in the past. Before: @@ -1176,7 +1176,7 @@ Before: [source,typescript] ---- function getUsageCollector( - usageCollection: UsageCollectionSetup, + usageCollection: UsageCollectionSetup, getSavedObjectsRepository: () => ISavedObjectsRepository | undefined ) { usageCollection.makeUsageCollector({ @@ -1185,12 +1185,12 @@ function getUsageCollector( schema: {...}, async fetch() { const savedObjectsRepository = getSavedObjectsRepository(); - + const { attributes: result } = await savedObjectsRepository.get('my-so-type', 'my-so-id'); - + return result; } - }); + }); } ---- @@ -1205,10 +1205,10 @@ function getUsageCollector(usageCollection: UsageCollectionSetup) { schema: {...}, async fetch({ soClient }) { const { attributes: result } = await soClient.get('my-so-type', 'my-so-id'); - + return result; } - }); + }); } ---- diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 1830e8f140e60..180d376ceaf51 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -104,6 +104,9 @@ readonly links: { readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; + readonly search: { + readonly sessions: string; + }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; diff --git a/docs/index.asciidoc b/docs/index.asciidoc index eb6f794434f8a..70e40576fdd71 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -9,10 +9,12 @@ include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] -:docker-repo: docker.elastic.co/kibana/kibana -:docker-image: docker.elastic.co/kibana/kibana:{version} -:blob: {kib-repo}blob/{branch}/ -:security-ref: https://www.elastic.co/community/security/ +:docker-repo: docker.elastic.co/kibana/kibana +:docker-image: {docker-repo}:{version} +:es-docker-repo: docker.elastic.co/elasticsearch/elasticsearch +:es-docker-image: {es-docker-repo}:{version} +:blob: {kib-repo}blob/{branch}/ +:security-ref: https://www.elastic.co/community/security/ include::{docs-root}/shared/attributes.asciidoc[] diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 4d6dcb631792e..6cdb1dbfa712e 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -106,6 +106,12 @@ New connectors can be created by clicking the *Create connector* button, which w [role="screenshot"] image::images/connector-select-type.png[Connector select type] +[float] +[[importing-and-exporting-connectors]] +=== Importing and exporting connectors + +To import and export rules, use the <>. + [float] [[create-connectors]] === Preconfigured connectors diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 084ac633e9bca..70f3e272fa5a9 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -276,14 +276,14 @@ For information about {kib} memory limits, see <> to try out all of the features. [float] -[[pull-image]] -=== Pull the image - -Obtaining Kibana for Docker is as simple as issuing a +docker pull+ command -against the Elastic Docker registry. +[[run-kibana-on-docker-for-dev]] +=== Run {kib} on Docker for development ifeval::["{release-state}"=="unreleased"] -However, version {version} of Kibana has not yet been released, so no Docker -image is currently available for this version. +NOTE: No Docker images are currently available for {kib} {version}. endif::[] ifeval::["{release-state}"!="unreleased"] -["source","txt",subs="attributes"] --------------------------------------------- -docker pull {docker-repo}:{version} --------------------------------------------- +To start an {es} container for development or testing, run: -[float] -=== Run Kibana on Docker for development -Kibana can be quickly started and connected to a local Elasticsearch container for development -or testing use with the following command: +[source,sh,subs="attributes"] +---- +docker network create elastic +docker pull {es-docker-image} +docker run --name es01-test --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" {es-docker-image} +---- + +To start {kib} and connect it to your {es} container, run the following commands +in a new terminal session: [source,sh,subs="attributes"] ---- -docker run --link YOUR_ELASTICSEARCH_CONTAINER_NAME_OR_ID:elasticsearch -p 5601:5601 {docker-repo}:{version} +docker pull {docker-image} +docker run --name kib01-test --net elastic -p 5601:5601 -e "ELASTICSEARCH_HOSTS=http://es01-test:9200" {docker-image} +---- + +To access {kib}, go to http://localhost:5601[http://localhost:5601]. + +[float] +=== Stop Docker containers + +To stop your containers, run: + +[source,sh] +---- +docker stop es01-test +docker stop kib01-test +---- + +To remove the containers and their network, run: + +[source,sh] +---- +docker network rm elastic +docker rm es01-test +docker rm kib01-test ---- endif::[] diff --git a/docs/user/alerting/alerting-troubleshooting.asciidoc b/docs/user/alerting/alerting-troubleshooting.asciidoc index 6d4a0e9375678..b7fd98d1c674e 100644 --- a/docs/user/alerting/alerting-troubleshooting.asciidoc +++ b/docs/user/alerting/alerting-troubleshooting.asciidoc @@ -69,3 +69,167 @@ Configuration options are available to specialize connections to TLS servers, including ignoring server certificate validation, and providing certificate authority data to verify servers using custom certificates. For more details, see <>. + +[float] +[[rules-long-execution-time]] +=== Identify long-running rules + +The following query can help you identify rules that are taking a long time to execute and might impact the overall health of your deployment. + +[IMPORTANT] +============================================== +By default, only users with a `superuser` role can query the {kib} event log because it is a system index. To enable additional users to execute this query, assign `read` privileges to the `.kibana-event-log*` index. +============================================== + +Query for a list of rule ids, bucketed by their execution times: + +[source,console] +-------------------------------------------------- +GET /.kibana-event-log*/_search +{ + "size": 0, + "query": { + "bool": { + "filter": [ + { + "range": { + "@timestamp": { + "gte": "now-1d", <1> + "lte": "now" + } + } + }, + { + "term": { + "event.action": { + "value": "execute" + } + } + }, + { + "term": { + "event.provider": { + "value": "alerting" <2> + } + } + } + ] + } + }, + "runtime_mappings": { <3> + "event.duration_in_seconds": { + "type": "double", + "script": { + "source": "emit(doc['event.duration'].value / 1E9)" + } + } + }, + "aggs": { + "ruleIdsByExecutionDuration": { + "histogram": { + "field": "event.duration_in_seconds", + "min_doc_count": 1, + "interval": 1 <4> + }, + "aggs": { + "ruleId": { + "nested": { + "path": "kibana.saved_objects" + }, + "aggs": { + "ruleId": { + "terms": { + "field": "kibana.saved_objects.id", + "size": 10 <5> + } + } + } + } + } + } + } +} +-------------------------------------------------- +// TEST + +<1> This queries for rules executed in the last day. Update the values of `lte` and `gte` to query over a different time range. +<2> Use `event.provider: actions` to query for long-running action executions. +<3> Execution durations are stored as nanoseconds. This adds a runtime field to convert that duration into seconds. +<4> This interval buckets the event.duration_in_seconds runtime field into 1 second intervals. Update this value to change the granularity of the buckets. If you are unable to use runtime fields, make sure this aggregation targets `event.duration` and use nanoseconds for the interval. +<5> This retrieves the top 10 rule ids for this duration interval. Update this value to retrieve more rule ids. + +This query returns the following: + +[source,json] +-------------------------------------------------- +{ + "took" : 322, + "timed_out" : false, + "_shards" : { + "total" : 1, + "successful" : 1, + "skipped" : 0, + "failed" : 0 + }, + "hits" : { + "total" : { + "value" : 326, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ ] + }, + "aggregations" : { + "ruleIdsByExecutionDuration" : { + "buckets" : [ + { + "key" : 0.0, <1> + "doc_count" : 320, + "ruleId" : { + "doc_count" : 320, + "ruleId" : { + "doc_count_error_upper_bound" : 0, + "sum_other_doc_count" : 0, + "buckets" : [ + { + "key" : "1923ada0-a8f3-11eb-a04b-13d723cdfdc5", + "doc_count" : 140 + }, + { + "key" : "15415ecf-cdb0-4fef-950a-f824bd277fe4", + "doc_count" : 130 + }, + { + "key" : "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2", + "doc_count" : 50 + } + ] + } + } + }, + { + "key" : 30.0, <2> + "doc_count" : 6, + "ruleId" : { + "doc_count" : 6, + "ruleId" : { + "doc_count_error_upper_bound" : 0, + "sum_other_doc_count" : 0, + "buckets" : [ + { + "key" : "41893910-6bca-11eb-9e0d-85d233e3ee35", + "doc_count" : 6 + } + ] + } + } + } + ] + } + } +} +-------------------------------------------------- +<1> Most rule execution durations fall within the first bucket (0 - 1 seconds). +<2> A single rule with id `41893910-6bca-11eb-9e0d-85d233e3ee35` took between 30 and 31 seconds to execute. + +Use the <> to retrieve additional information about rules that take a long time to execute. \ No newline at end of file diff --git a/docs/user/alerting/rule-management.asciidoc b/docs/user/alerting/rule-management.asciidoc index b908bd03b0992..b15c46254b770 100644 --- a/docs/user/alerting/rule-management.asciidoc +++ b/docs/user/alerting/rule-management.asciidoc @@ -57,6 +57,12 @@ These operations can also be performed in bulk by multi-selecting rules and clic [role="screenshot"] image:images/bulk-mute-disable.png[The Manage rules button lets you mute/unmute, enable/disable, and delete in bulk] +[float] +[[importing-and-exporting-rules]] +=== Importing and exporting rules + +To import and export rules, use the <>. + [float] === Required permissions diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 144ed1ea28c93..9e4271b3659bc 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -32,7 +32,7 @@ for different operating systems. == Roles and privileges When security is enabled, access to the {report-features} is controlled by security privileges. In versions 7.12 and earlier, you can grant access to the {report-features} -by assigning users the `reporting_user` role in {es}. In 7.13 and later, you can configure *Reporting* to use +by assigning users the `reporting_user` role in {es}. In 7.14 and later, you can configure *Reporting* to use <>. It is recommended that *Reporting* is configured to use {kib} privileges by setting <> to `false`. By using {kib} privileges, you can define custom roles that grant *Reporting* privileges as sub-features of {kib} applications in *Role Management*. diff --git a/docs/user/security/reporting.asciidoc b/docs/user/security/reporting.asciidoc index 2f331e252c492..ab25dddd04694 100644 --- a/docs/user/security/reporting.asciidoc +++ b/docs/user/security/reporting.asciidoc @@ -17,7 +17,7 @@ For more information, see [[reporting-app-users]] Access to reporting features is limited to privileged users. In older versions of Kibana, you could only grant -users the privilege by assigning them the `reporting_user` role in Elasticsearch. In 7.13 and above, you have +users the privilege by assigning them the `reporting_user` role in Elasticsearch. In 7.14 and above, you have the option to create your own roles that grant access to reporting features using <>. It is recommended that you set `xpack.reporting.roles.enabled: false` in your kibana.yml to begin using Kibana diff --git a/package.json b/package.json index 04bce960ab1a2..dc0521e03deaa 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,8 @@ "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/request": "^2.88.2", "**/trim": "0.0.3", - "**/typescript": "4.1.3" + "**/typescript": "4.1.3", + "**/underscore": "^1.13.1" }, "engines": { "node": "14.16.1", @@ -122,18 +123,21 @@ "@hapi/podium": "^4.1.1", "@hapi/wreck": "^17.1.0", "@kbn/ace": "link:packages/kbn-ace", - "@kbn/analytics": "link:packages/kbn-analytics", + "@kbn/analytics": "link:bazel-bin/packages/kbn-analytics/npm_module", "@kbn/apm-config-loader": "link:bazel-bin/packages/kbn-apm-config-loader/npm_module", "@kbn/apm-utils": "link:bazel-bin/packages/kbn-apm-utils/npm_module", - "@kbn/config": "link:packages/kbn-config", + "@kbn/config": "link:bazel-bin/packages/kbn-config/npm_module", "@kbn/config-schema": "link:bazel-bin/packages/kbn-config-schema/npm_module", - "@kbn/crypto": "link:packages/kbn-crypto", + "@kbn/crypto": "link:bazel-bin/packages/kbn-crypto/npm_module", "@kbn/i18n": "link:packages/kbn-i18n", "@kbn/interpreter": "link:packages/kbn-interpreter", "@kbn/io-ts-utils": "link:packages/kbn-io-ts-utils", "@kbn/legacy-logging": "link:bazel-bin/packages/kbn-legacy-logging/npm_module", "@kbn/logging": "link:bazel-bin/packages/kbn-logging/npm_module", "@kbn/monaco": "link:packages/kbn-monaco", + "@kbn/securitysolution-constants": "link:bazel-bin/packages/kbn-securitysolution-constants/npm_module", + "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils/npm_module", + "@kbn/securitysolution-io-ts-utils": "link:bazel-bin/packages/kbn-securitysolution-io-ts-utils/npm_module", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", "@kbn/std": "link:bazel-bin/packages/kbn-std/npm_module", @@ -394,6 +398,7 @@ "utility-types": "^3.10.0", "uuid": "3.3.2", "vega": "^5.19.1", + "vega-interpreter": "^1.0.4", "vega-lite": "^5.0.0", "vega-schema-url-parser": "^2.1.0", "vega-spec-injector": "^0.0.2", @@ -442,7 +447,7 @@ "@kbn/cli-dev-mode": "link:packages/kbn-cli-dev-mode", "@kbn/dev-utils": "link:bazel-bin/packages/kbn-dev-utils/npm_module", "@kbn/docs-utils": "link:packages/kbn-docs-utils", - "@kbn/es": "link:packages/kbn-es", + "@kbn/es": "link:bazel-bin/packages/kbn-es/npm_module", "@kbn/es-archiver": "link:packages/kbn-es-archiver", "@kbn/eslint-import-resolver-kibana": "link:bazel-bin/packages/kbn-eslint-import-resolver-kibana/npm_module", "@kbn/eslint-plugin-eslint": "link:bazel-bin/packages/kbn-eslint-plugin-eslint/npm_module", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 39285fb9ea66a..10600e514cfb6 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -6,17 +6,24 @@ filegroup( "//packages/elastic-datemath:build", "//packages/elastic-eslint-config-kibana:build", "//packages/elastic-safer-lodash-set:build", + "//packages/kbn-analytics:build", "//packages/kbn-apm-config-loader:build", "//packages/kbn-apm-utils:build", "//packages/kbn-babel-code-parser:build", "//packages/kbn-babel-preset:build", + "//packages/kbn-config:build", "//packages/kbn-config-schema:build", + "//packages/kbn-crypto:build", "//packages/kbn-dev-utils:build", + "//packages/kbn-es:build", "//packages/kbn-eslint-import-resolver-kibana:build", "//packages/kbn-eslint-plugin-eslint:build", "//packages/kbn-expect:build", "//packages/kbn-legacy-logging:build", "//packages/kbn-logging:build", + "//packages/kbn-securitysolution-constants:build", + "//packages/kbn-securitysolution-io-ts-utils:build", + "//packages/kbn-securitysolution-utils:build", "//packages/kbn-std:build", "//packages/kbn-tinymath:build", "//packages/kbn-utility-types:build", diff --git a/packages/kbn-analytics/BUILD.bazel b/packages/kbn-analytics/BUILD.bazel new file mode 100644 index 0000000000000..a5506598baeac --- /dev/null +++ b/packages/kbn-analytics/BUILD.bazel @@ -0,0 +1,124 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-analytics" +PKG_REQUIRE_NAME = "@kbn/analytics" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json" +] + +SRC_DEPS = [ + "@npm//moment-timezone", + "@npm//tslib", +] + +TYPES_DEPS = [ + "@npm//@types/moment-timezone", + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_config( + name = "tsconfig_browser", + src = "tsconfig.browser.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.browser.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_dir = "types", + declaration_map = True, + incremental = True, + out_dir = "node", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +ts_project( + name = "tsc_browser", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = False, + incremental = True, + out_dir = "web", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig_browser", +) + +filegroup( + name = "tsc_types", + srcs = [":tsc"], + output_group = "types", +) + +filegroup( + name = "target_files", + srcs = [ + ":tsc", + ":tsc_browser", + ":tsc_types", + ], +) + +pkg_npm( + name = "target", + deps = [ + ":target_files", + ], +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = [":target"] + DEPS, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index 2195de578081e..726b62e1c61b8 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -4,13 +4,8 @@ "version": "1.0.0", "description": "Kibana Analytics tool", "main": "target/node/index.js", - "browser": "target/web/index.js", "types": "target/types/index.d.ts", + "browser": "target/web/index.js", "author": "Ahmad Bamieh ", - "license": "SSPL-1.0 OR Elastic License 2.0", - "scripts": { - "build": "node scripts/build", - "kbn:bootstrap": "node scripts/build --source-maps", - "kbn:watch": "node scripts/build --source-maps --watch" - } + "license": "SSPL-1.0 OR Elastic License 2.0" } \ No newline at end of file diff --git a/packages/kbn-analytics/scripts/build.js b/packages/kbn-analytics/scripts/build.js deleted file mode 100644 index b9677d6a07a88..0000000000000 --- a/packages/kbn-analytics/scripts/build.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const { resolve } = require('path'); - -const del = require('del'); -const supportsColor = require('supports-color'); -const { run, withProcRunner } = require('@kbn/dev-utils'); - -const ROOT_DIR = resolve(__dirname, '..'); -const BUILD_DIR = resolve(ROOT_DIR, 'target'); - -const padRight = (width, str) => - str.length >= width ? str : `${str}${' '.repeat(width - str.length)}`; - -run( - async ({ log, flags }) => { - await withProcRunner(log, async (proc) => { - log.info('Deleting old output'); - await del(BUILD_DIR); - - const cwd = ROOT_DIR; - const env = { ...process.env }; - if (supportsColor.stdout) { - env.FORCE_COLOR = 'true'; - } - - log.info(`Starting babel and typescript${flags.watch ? ' in watch mode' : ''}`); - await Promise.all([ - ...['web', 'node'].map((subTask) => - proc.run(padRight(10, `babel:${subTask}`), { - cmd: 'babel', - args: [ - 'src', - '--config-file', - require.resolve('../babel.config.js'), - '--out-dir', - resolve(BUILD_DIR, subTask), - '--extensions', - '.ts,.js,.tsx', - ...(flags.watch ? ['--watch'] : ['--quiet']), - ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE - ? [] - : ['--source-maps', 'inline']), - ], - wait: true, - env: { - ...env, - BABEL_ENV: subTask, - }, - cwd, - }) - ), - - proc.run(padRight(10, 'tsc'), { - cmd: 'tsc', - args: [ - ...(flags.watch ? ['--watch', '--preserveWatchOutput', 'true'] : []), - ...(flags['source-maps'] ? ['--declarationMap', 'true'] : []), - ], - wait: true, - env, - cwd, - }), - ]); - - log.success('Complete'); - }); - }, - { - description: 'Simple build tool for @kbn/analytics package', - flags: { - boolean: ['watch', 'source-maps'], - help: ` - --watch Run in watch mode - --source-maps Include sourcemaps - `, - }, - } -); diff --git a/packages/kbn-analytics/tsconfig.browser.json b/packages/kbn-analytics/tsconfig.browser.json new file mode 100644 index 0000000000000..12f70b77008e7 --- /dev/null +++ b/packages/kbn-analytics/tsconfig.browser.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.browser.json", + "compilerOptions": { + "incremental": true, + "outDir": "./target/web", + "stripInternal": true, + "declaration": false, + "isolatedModules": true, + "sourceMap": true, + "sourceRoot": "../../../../../packages/kbn-analytics/src", + "types": [ + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-analytics/tsconfig.json b/packages/kbn-analytics/tsconfig.json index 80a2255d71805..165a8b695db57 100644 --- a/packages/kbn-analytics/tsconfig.json +++ b/packages/kbn-analytics/tsconfig.json @@ -1,10 +1,10 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": false, - "outDir": "./target/types", + "incremental": true, + "declarationDir": "./target/types", + "outDir": "./target/node", "stripInternal": true, - "emitDeclarationOnly": true, "declaration": true, "declarationMap": true, "isolatedModules": true, diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json index 9def59623c938..0401e6a82e11a 100644 --- a/packages/kbn-cli-dev-mode/package.json +++ b/packages/kbn-cli-dev-mode/package.json @@ -14,7 +14,6 @@ "devOnly": true }, "dependencies": { - "@kbn/config": "link:../kbn-config", "@kbn/server-http-tools": "link:../kbn-server-http-tools", "@kbn/optimizer": "link:../kbn-optimizer" } diff --git a/packages/kbn-config/BUILD.bazel b/packages/kbn-config/BUILD.bazel new file mode 100644 index 0000000000000..a079d9c8f6413 --- /dev/null +++ b/packages/kbn-config/BUILD.bazel @@ -0,0 +1,97 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-config" +PKG_REQUIRE_NAME = "@kbn/config" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/__fixtures__", + "**/__mocks__", + "**/__snapshots__" + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "//packages/elastic-safer-lodash-set", + "//packages/kbn-config-schema", + "//packages/kbn-logging", + "//packages/kbn-std", + "//packages/kbn-utility-types", + "@npm//js-yaml", + "@npm//load-json-file", + "@npm//lodash", + "@npm//rxjs", + "@npm//type-detect", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/js-yaml", + "@npm//@types/lodash", + "@npm//@types/node", + "@npm//@types/type-detect", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = [":tsc"] + DEPS, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index b114cb13933d1..56f1855bebbce 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -4,9 +4,5 @@ "types": "./target/index.d.ts", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true, - "scripts": { - "build": "../../node_modules/.bin/tsc", - "kbn:bootstrap": "yarn build" - } + "private": true } \ No newline at end of file diff --git a/packages/kbn-config/tsconfig.json b/packages/kbn-config/tsconfig.json index 4e1bf573f488a..115af2c46b549 100644 --- a/packages/kbn-config/tsconfig.json +++ b/packages/kbn-config/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": false, + "incremental": true, "outDir": "./target", "stripInternal": false, "declaration": true, diff --git a/packages/kbn-crypto/BUILD.bazel b/packages/kbn-crypto/BUILD.bazel new file mode 100644 index 0000000000000..3e23f17acaea0 --- /dev/null +++ b/packages/kbn-crypto/BUILD.bazel @@ -0,0 +1,87 @@ + +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-cypto" +PKG_REQUIRE_NAME = "@kbn/crypto" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "@npm//jest-styled-components", + "@npm//node-forge", +] + +TYPES_DEPS = [ + "@npm//@types/flot", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/node-forge", + "@npm//@types/testing-library__jest-dom", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = [":tsc"] + DEPS, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/packages/kbn-crypto/package.json b/packages/kbn-crypto/package.json index 0787427c60b10..bbeb57e5b7cca 100644 --- a/packages/kbn-crypto/package.json +++ b/packages/kbn-crypto/package.json @@ -4,10 +4,5 @@ "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", "main": "./target/index.js", - "types": "./target/index.d.ts", - "scripts": { - "build": "../../node_modules/.bin/tsc", - "kbn:bootstrap": "yarn build", - "kbn:watch": "yarn build --watch" - } -} \ No newline at end of file + "types": "./target/index.d.ts" +} diff --git a/packages/kbn-crypto/tsconfig.json b/packages/kbn-crypto/tsconfig.json index 5005152cac754..a9f18fde1bba0 100644 --- a/packages/kbn-crypto/tsconfig.json +++ b/packages/kbn-crypto/tsconfig.json @@ -1,14 +1,13 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": false, + "incremental": true, "outDir": "./target", "declaration": true, "declarationMap": true, + "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-crypto/src" }, - "include": [ - "src/**/*" - ] + "include": ["src/**/*"] } diff --git a/packages/kbn-docs-utils/package.json b/packages/kbn-docs-utils/package.json index 6aca554f0f945..27d38d2d8ed4f 100644 --- a/packages/kbn-docs-utils/package.json +++ b/packages/kbn-docs-utils/package.json @@ -11,8 +11,5 @@ "scripts": { "kbn:bootstrap": "../../node_modules/.bin/tsc", "kbn:watch": "../../node_modules/.bin/tsc --watch" - }, - "dependencies": { - "@kbn/config": "link:../kbn-config" } } \ No newline at end of file diff --git a/packages/kbn-es/.babelrc b/packages/kbn-es/.babelrc new file mode 100644 index 0000000000000..7da72d1779128 --- /dev/null +++ b/packages/kbn-es/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-es/BUILD.bazel b/packages/kbn-es/BUILD.bazel new file mode 100644 index 0000000000000..bc8986e527fc5 --- /dev/null +++ b/packages/kbn-es/BUILD.bazel @@ -0,0 +1,90 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("@npm//@babel/cli:index.bzl", "babel") + +PKG_BASE_NAME = "kbn-es" +PKG_REQUIRE_NAME = "@kbn/es" + +SOURCE_FILES = glob( + [ + "src/**/*", + ], + exclude = [ + "**/*.test.*", + "**/integration_tests/**", + "**/__fixtures__/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +DEPS = [ + "//packages/kbn-babel-preset", + "@npm//@elastic/elasticsearch", + "@npm//abort-controller", + "@npm//chalk", + "@npm//dedent", + "@npm//del", + "@npm//execa", + "@npm//getopts", + "@npm//glob", + "@npm//node-fetch", + "@npm//simple-git", + "@npm//tar-fs", + "@npm//tree-kill", + "@npm//yauzl", + "@npm//zlib" +] + +babel( + name = "target", + data = [ + ":srcs", + ".babelrc", + ] + DEPS, + output_dir = True, + # the following arg paths includes $(execpath) as babel runs on the sandbox root + args = [ + "./%s/src" % package_name(), + "--config-file", + "./%s/.babelrc" % package_name(), + "--out-dir", + "$(@D)", + "--extensions", + ".ts,.js", + "--copy-files", + "--quiet" + ], +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = [":target"] + DEPS, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index e7356794b6113..ed3f29feaef98 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -6,10 +6,5 @@ "private": true, "kibana": { "devOnly": true - }, - "scripts": { - "build": "node scripts/build", - "kbn:bootstrap": "node scripts/build", - "kbn:watch": "node scripts/build --watch" } } \ No newline at end of file diff --git a/packages/kbn-es/scripts/build.js b/packages/kbn-es/scripts/build.js deleted file mode 100644 index d8d8d0e69ee27..0000000000000 --- a/packages/kbn-es/scripts/build.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const { resolve } = require('path'); - -const del = require('del'); -const { run, withProcRunner } = require('@kbn/dev-utils'); - -const ROOT_DIR = resolve(__dirname, '..'); -const BUILD_DIR = resolve(ROOT_DIR, 'target'); - -run( - async ({ log, flags }) => { - await withProcRunner(log, async (proc) => { - log.info('Deleting old output'); - await del(BUILD_DIR); - - const cwd = ROOT_DIR; - - log.info(`Starting babel${flags.watch ? ' in watch mode' : ''}`); - await proc.run(`babel`, { - cmd: 'babel', - args: [ - 'src', - '--no-babelrc', - '--presets', - require.resolve('@kbn/babel-preset/node_preset'), - '--extensions', - '.ts,.js', - '--copy-files', - '--out-dir', - BUILD_DIR, - ...(flags.watch ? ['--watch'] : ['--quiet']), - ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE - ? [] - : ['--source-maps', 'inline']), - ], - wait: true, - cwd, - }); - - log.success('Complete'); - }); - }, - { - description: 'Simple build tool for @kbn/es package', - flags: { - boolean: ['watch', 'source-maps'], - help: ` - --watch Run in watch mode - --source-maps Include sourcemaps - `, - }, - } -); diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index f193fcf898a3d..54fcdc3bb130f 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -11,7 +11,6 @@ "kbn:watch": "yarn build --watch" }, "dependencies": { - "@kbn/config": "link:../kbn-config", "@kbn/ui-shared-deps": "link:../kbn-ui-shared-deps" } } \ No newline at end of file diff --git a/packages/kbn-securitysolution-constants/BUILD.bazel b/packages/kbn-securitysolution-constants/BUILD.bazel new file mode 100644 index 0000000000000..8b2d8c9103f3e --- /dev/null +++ b/packages/kbn-securitysolution-constants/BUILD.bazel @@ -0,0 +1,84 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-securitysolution-constants" + +PKG_REQUIRE_NAME = "@kbn/securitysolution-constants" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.mock.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "@npm//tslib", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + srcs = SRCS, + args = ["--pretty"], + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + root_dir = "src", + source_map = True, + tsconfig = ":tsconfig", + deps = DEPS, +) + +js_library( + name = PKG_BASE_NAME, + package_name = PKG_REQUIRE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + visibility = ["//visibility:public"], + deps = [":tsc"] + DEPS, +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ], +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-securitysolution-constants/README.md b/packages/kbn-securitysolution-constants/README.md new file mode 100644 index 0000000000000..dd1ab8da6a2a8 --- /dev/null +++ b/packages/kbn-securitysolution-constants/README.md @@ -0,0 +1,6 @@ +# kbn-securitysolution-constants + +This is where shared constants for security solution should go that are going to be shared among plugins. +This was originally created to remove the dependencies between security_solution and other projects such as lists. + + diff --git a/src/plugins/discover/public/application/components/context_app/constants.ts b/packages/kbn-securitysolution-constants/jest.config.js similarity index 60% rename from src/plugins/discover/public/application/components/context_app/constants.ts rename to packages/kbn-securitysolution-constants/jest.config.js index a22aa69477ee9..f0bb13e39417c 100644 --- a/src/plugins/discover/public/application/components/context_app/constants.ts +++ b/packages/kbn-securitysolution-constants/jest.config.js @@ -6,14 +6,8 @@ * Side Public License, v 1. */ -export const FAILURE_REASONS = { - UNKNOWN: 'unknown', - INVALID_TIEBREAKER: 'invalid_tiebreaker', -}; - -export const LOADING_STATUS = { - FAILED: 'failed', - LOADED: 'loaded', - LOADING: 'loading', - UNINITIALIZED: 'uninitialized', +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-constants'], }; diff --git a/packages/kbn-securitysolution-constants/package.json b/packages/kbn-securitysolution-constants/package.json new file mode 100644 index 0000000000000..bb60f79aa03e5 --- /dev/null +++ b/packages/kbn-securitysolution-constants/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-constants", + "version": "1.0.0", + "description": "security solution constants to use across plugins such lists, security_solution, cases, etc...", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-constants/src/index.ts b/packages/kbn-securitysolution-constants/src/index.ts new file mode 100644 index 0000000000000..06b741d761367 --- /dev/null +++ b/packages/kbn-securitysolution-constants/src/index.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Value list routes + */ +export const LIST_URL = '/api/lists'; +export const LIST_INDEX = `${LIST_URL}/index`; +export const LIST_ITEM_URL = `${LIST_URL}/items`; +export const LIST_PRIVILEGES_URL = `${LIST_URL}/privileges`; + +/** + * Exception list routes + */ +export const EXCEPTION_LIST_URL = '/api/exception_lists'; +export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; + +/** + * Exception list spaces + */ +export const EXCEPTION_LIST_NAMESPACE_AGNOSTIC = 'exception-list-agnostic'; +export const EXCEPTION_LIST_NAMESPACE = 'exception-list'; + +/** + * Specific routes for the single global space agnostic endpoint list + */ +export const ENDPOINT_LIST_URL = '/api/endpoint_list'; + +/** + * Specific routes for the single global space agnostic endpoint list. These are convenience + * routes where they are going to try and create the global space agnostic endpoint list if it + * does not exist yet or if it was deleted at some point and re-create it before adding items to + * the list + */ +export const ENDPOINT_LIST_ITEM_URL = '/api/endpoint_list/items'; + +/** + * This ID is used for _both_ the Saved Object ID and for the list_id + * for the single global space agnostic endpoint list + */ +export const ENDPOINT_LIST_ID = 'endpoint_list'; + +/** The name of the single global space agnostic endpoint list */ +export const ENDPOINT_LIST_NAME = 'Endpoint Security Exception List'; + +/** The description of the single global space agnostic endpoint list */ +export const ENDPOINT_LIST_DESCRIPTION = 'Endpoint Security Exception List'; + +export const MAX_EXCEPTION_LIST_SIZE = 10000; + +/** ID of trusted apps agnostic list */ +export const ENDPOINT_TRUSTED_APPS_LIST_ID = 'endpoint_trusted_apps'; + +/** Name of trusted apps agnostic list */ +export const ENDPOINT_TRUSTED_APPS_LIST_NAME = 'Endpoint Security Trusted Apps List'; + +/** Description of trusted apps agnostic list */ +export const ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION = 'Endpoint Security Trusted Apps List'; + +/** ID of event filters agnostic list */ +export const ENDPOINT_EVENT_FILTERS_LIST_ID = 'endpoint_event_filters'; + +/** Name of event filters agnostic list */ +export const ENDPOINT_EVENT_FILTERS_LIST_NAME = 'Endpoint Security Event Filters List'; + +/** Description of event filters agnostic list */ +export const ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION = 'Endpoint Security Event Filters List'; + +/** The default max signals without any additional configuration */ +export const DEFAULT_MAX_SIGNALS = 100; diff --git a/packages/kbn-securitysolution-constants/tsconfig.json b/packages/kbn-securitysolution-constants/tsconfig.json new file mode 100644 index 0000000000000..cf06f4ced4b9f --- /dev/null +++ b/packages/kbn-securitysolution-constants/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": true, + "outDir": "target", + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-securitysolution-constants/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel new file mode 100644 index 0000000000000..66c5674067ecd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel @@ -0,0 +1,92 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-securitysolution-io-ts-utils" +PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-utils" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.mock.*" + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "//packages/elastic-datemath", + "@npm//fp-ts", + "@npm//io-ts", + "@npm//lodash", + "@npm//moment", + "@npm//tslib", + "@npm//uuid", +] + +TYPES_DEPS = [ + "@npm//@types/flot", + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/node", + "@npm//@types/uuid" +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = [":tsc"] + DEPS, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-securitysolution-io-ts-utils/README.md b/packages/kbn-securitysolution-io-ts-utils/README.md new file mode 100644 index 0000000000000..908651b50b80a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/README.md @@ -0,0 +1,10 @@ +# kbn-securitysolution-io-ts-utils + +Temporary location for all the io-ts-utils from security solutions. This is a lift-and-shift, where +we are moving them here for phase 1. + +Phase 2 is deprecating across plugins any copied code or sharing of io-ts utils that are now in here. + +Phase 3 is replacing those deprecated types with the ones in here. + +Phase 4+ is (potentially) consolidating any duplication or everything altogether with the `kbn-io-ts-utils` project \ No newline at end of file diff --git a/packages/kbn-analytics/babel.config.js b/packages/kbn-securitysolution-io-ts-utils/jest.config.js similarity index 52% rename from packages/kbn-analytics/babel.config.js rename to packages/kbn-securitysolution-io-ts-utils/jest.config.js index cdbc4feb60176..bd4e22ed3caaa 100644 --- a/packages/kbn-analytics/babel.config.js +++ b/packages/kbn-securitysolution-io-ts-utils/jest.config.js @@ -6,16 +6,8 @@ * Side Public License, v 1. */ -// We can't use common Kibana presets here because of babel versions incompatibility module.exports = { - plugins: ['@babel/plugin-proposal-class-properties'], - env: { - web: { - presets: ['@kbn/babel-preset/webpack_preset'], - }, - node: { - presets: ['@kbn/babel-preset/node_preset'], - }, - }, - ignore: ['**/*.test.ts', '**/*.test.tsx'], + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-io-ts-utils'], }; diff --git a/packages/kbn-securitysolution-io-ts-utils/package.json b/packages/kbn-securitysolution-io-ts-utils/package.json new file mode 100644 index 0000000000000..0912fbfcb6e8d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-io-ts-utils", + "version": "1.0.0", + "description": "io ts utilities and types to be shared with plugins from the security solution project", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-io-ts-utils/src/actions/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/actions/index.ts new file mode 100644 index 0000000000000..7503bcc330e0e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/actions/index.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { saved_object_attributes } from '../saved_object_attributes'; + +/** + * Params is an "object", since it is a type of AlertActionParams which is action templates. + * @see x-pack/plugins/alerting/common/alert.ts + */ +export const action_group = t.string; +export const action_id = t.string; +export const action_action_type_id = t.string; +export const action_params = saved_object_attributes; + +export const action = t.exact( + t.type({ + group: action_group, + id: action_id, + action_type_id: action_action_type_id, + params: action_params, + }) +); + +export const actions = t.array(action); +export type Actions = t.TypeOf; + +export const actionsCamel = t.array( + t.exact( + t.type({ + group: action_group, + id: action_id, + actionTypeId: action_action_type_id, + params: action_params, + }) + ) +); +export type ActionsCamel = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.ts new file mode 100644 index 0000000000000..d2107ae864f15 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export const ENTRY_VALUE = 'some host name'; +export const FIELD = 'host.name'; +export const MATCH = 'match'; +export const MATCH_ANY = 'match_any'; +export const OPERATOR = 'included'; +export const NESTED = 'nested'; +export const NESTED_FIELD = 'parent.field'; +export const LIST_ID = 'some-list-id'; +export const LIST = 'list'; +export const TYPE = 'ip'; +export const EXISTS = 'exists'; +export const WILDCARD = 'wildcard'; +export const USER = 'some user'; +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; + +// Exception List specific +export const ID = 'uuid_here'; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts new file mode 100644 index 0000000000000..91a3951ef11fc --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * This ID is used for _both_ the Saved Object ID and for the list_id + * for the single global space agnostic endpoint list + * TODO: Create a kbn-securitysolution-constants and add this to it. + * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. + */ +export const ENDPOINT_LIST_ID = 'endpoint_list'; + +/** + * TODO: Create a kbn-securitysolution-constants and add this to it. + * @deprecated Use the DEFAULT_MAX_SIGNALS from the kbn-securitysolution-constants. + */ +export const DEFAULT_MAX_SIGNALS = 100; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts new file mode 100644 index 0000000000000..9051f68855818 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const created_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts new file mode 100644 index 0000000000000..fbcf8da0cf458 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const created_by = t.string; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts new file mode 100644 index 0000000000000..f77903d2d030d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultVersionNumber } from '../default_version_number'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_version_number', () => { + test('it should validate a version number', () => { + const payload = 5; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a 0', () => { + const payload = 0; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a -1', () => { + const payload = -1; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a string', () => { + const payload = '5'; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 1', () => { + const payload = null; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(1); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts new file mode 100644 index 0000000000000..245ff9d0db7dd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { version, Version } from '../version'; + +/** + * Types the DefaultVersionNumber as: + * - If null or undefined, then a default of the number 1 will be used + */ +export const DefaultVersionNumber = new t.Type( + 'DefaultVersionNumber', + version.is, + (input, context): Either => + input == null ? t.success(1) : version.validate(input, context), + t.identity +); + +export type DefaultVersionNumberDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts new file mode 100644 index 0000000000000..9d741aa65e079 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { actions, Actions } from '../actions'; + +export const DefaultActionsArray = new t.Type( + 'DefaultActionsArray', + actions.is, + (input, context): Either => + input == null ? t.success([]) : actions.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts new file mode 100644 index 0000000000000..82fa884b1c577 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultArray } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +const testSchema = t.keyof({ + valid: true, + also_valid: true, +}); +type TestSchema = t.TypeOf; + +const defaultArraySchema = DefaultArray(testSchema); + +describe('default_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of testSchema', () => { + const payload: TestSchema[] = ['valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of valid testSchema strings', () => { + const payload = ['valid', 'also_valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['valid', 123]; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "123" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an array with an invalid string', () => { + const payload = ['valid', 'invalid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts new file mode 100644 index 0000000000000..7a39f484e601c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultArray as: + * - If undefined, then a default array will be set + * - If an array is sent in, then the array will be validated to ensure all elements are type C + */ +export const DefaultArray = (codec: C) => { + const arrType = t.array(codec); + type ArrType = t.TypeOf; + return new t.Type( + 'DefaultArray', + arrType.is, + (input, context): Either => + input == null ? t.success([]) : arrType.validate(input, context), + t.identity + ); +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts new file mode 100644 index 0000000000000..bddf9cc0747ea --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultBooleanFalse } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_boolean_false', () => { + test('it should validate a boolean false', () => { + const payload = false; + const decoded = DefaultBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a boolean true', () => { + const payload = true; + const decoded = DefaultBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultBooleanFalse"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default false', () => { + const payload = null; + const decoded = DefaultBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(false); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts new file mode 100644 index 0000000000000..f0b51afd2dc4d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultBooleanFalse as: + * - If null or undefined, then a default false will be set + */ +export const DefaultBooleanFalse = new t.Type( + 'DefaultBooleanFalse', + t.boolean.is, + (input, context): Either => + input == null ? t.success(false) : t.boolean.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts new file mode 100644 index 0000000000000..a05fb586c2e92 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultBooleanTrue } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_boolean_true', () => { + test('it should validate a boolean false', () => { + const payload = false; + const decoded = DefaultBooleanTrue.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a boolean true', () => { + const payload = true; + const decoded = DefaultBooleanTrue.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultBooleanTrue.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultBooleanTrue"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default true', () => { + const payload = null; + const decoded = DefaultBooleanTrue.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(true); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts new file mode 100644 index 0000000000000..888417a6fe043 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultBooleanTrue as: + * - If null or undefined, then a default true will be set + */ +export const DefaultBooleanTrue = new t.Type( + 'DefaultBooleanTrue', + t.boolean.is, + (input, context): Either => + input == null ? t.success(true) : t.boolean.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts new file mode 100644 index 0000000000000..5bdc9b298649e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultEmptyString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_empty_string', () => { + test('it should validate a regular string', () => { + const payload = 'some string'; + const decoded = DefaultEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultEmptyString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of ""', () => { + const payload = null; + const decoded = DefaultEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(''); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts new file mode 100644 index 0000000000000..aed0d11a75684 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultEmptyString as: + * - If null or undefined, then a default of an empty string "" will be used + */ +export const DefaultEmptyString = new t.Type( + 'DefaultEmptyString', + t.string.is, + (input, context): Either => + input == null ? t.success('') : t.string.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.ts new file mode 100644 index 0000000000000..1f81f056386d7 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultExportFileName } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_export_file_name', () => { + test('it should validate a regular string', () => { + const payload = 'some string'; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultExportFileName"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "export.ndjson"', () => { + const payload = null; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('export.ndjson'); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts new file mode 100644 index 0000000000000..df6afd800c1d6 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultExportFileName as: + * - If null or undefined, then a default of "export.ndjson" will be used + */ +export const DefaultExportFileName = new t.Type( + 'DefaultExportFileName', + t.string.is, + (input, context): Either => + input == null ? t.success('export.ndjson') : t.string.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.ts new file mode 100644 index 0000000000000..c1261f514540b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultFromString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_from_string', () => { + test('it should validate a from string', () => { + const payload = 'now-20m'; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultFromString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "now-6m"', () => { + const payload = null; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('now-6m'); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts new file mode 100644 index 0000000000000..55b76ab7c1a4e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { from } from '../from'; + +/** + * Types the DefaultFromString as: + * - If null or undefined, then a default of the string "now-6m" will be used + */ +export const DefaultFromString = new t.Type( + 'DefaultFromString', + t.string.is, + (input, context): Either => { + if (input == null) { + return t.success('now-6m'); + } + return from.validate(input, context); + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.ts new file mode 100644 index 0000000000000..c4a0dc3664d0e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultIntervalString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_interval_string', () => { + test('it should validate a interval string', () => { + const payload = '20m'; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultIntervalString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "5m"', () => { + const payload = null; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('5m'); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts new file mode 100644 index 0000000000000..7e42d32e42b13 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultIntervalString as: + * - If null or undefined, then a default of the string "5m" will be used + */ +export const DefaultIntervalString = new t.Type( + 'DefaultIntervalString', + t.string.is, + (input, context): Either => + input == null ? t.success('5m') : t.string.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts new file mode 100644 index 0000000000000..072c541a808a3 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Language } from '../language'; +import { DefaultLanguageString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_language_string', () => { + test('it should validate a string', () => { + const payload: Language = 'lucene'; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultLanguageString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "kuery"', () => { + const payload = null; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('kuery'); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts new file mode 100644 index 0000000000000..c7cdd02802bec --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { language } from '../language'; + +/** + * Types the DefaultLanguageString as: + * - If null or undefined, then a default of the string "kuery" will be used + */ +export const DefaultLanguageString = new t.Type( + 'DefaultLanguageString', + t.string.is, + (input, context): Either => + input == null ? t.success('kuery') : language.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts new file mode 100644 index 0000000000000..bf703fa52d844 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultMaxSignalsNumber } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; +import { DEFAULT_MAX_SIGNALS } from '../constants'; + +describe('default_from_string', () => { + test('it should validate a max signal number', () => { + const payload = 5; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a string', () => { + const payload = '5'; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of DEFAULT_MAX_SIGNALS', () => { + const payload = null; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(DEFAULT_MAX_SIGNALS); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts new file mode 100644 index 0000000000000..09bc45f589e11 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { max_signals } from '../max_signals'; +import { DEFAULT_MAX_SIGNALS } from '../constants'; + +/** + * Types the default max signal: + * - Natural Number (positive integer and not a float), + * - greater than 1 + * - If undefined then it will use DEFAULT_MAX_SIGNALS (100) as the default + */ +export const DefaultMaxSignalsNumber = new t.Type( + 'DefaultMaxSignals', + t.number.is, + (input, context): Either => { + return input == null ? t.success(DEFAULT_MAX_SIGNALS) : max_signals.validate(input, context); + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts new file mode 100644 index 0000000000000..3bcad15a7ebb8 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultPage } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_page', () => { + test('it should validate a regular number greater than zero', () => { + const payload = 5; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a string of a number', () => { + const payload = '5'; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(5); + }); + + test('it should not validate a junk string', () => { + const payload = 'invalid-string'; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 20', () => { + const payload = null; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(1); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts new file mode 100644 index 0000000000000..056005b452a03 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero'; + +/** + * Types the DefaultPerPage as: + * - If a string this will convert the string to a number + * - If null or undefined, then a default of 1 will be used + * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + */ +export const DefaultPage = new t.Type( + 'DefaultPerPage', + t.number.is, + (input, context): Either => { + if (input == null) { + return t.success(1); + } else if (typeof input === 'string') { + return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); + } else { + return PositiveIntegerGreaterThanZero.validate(input, context); + } + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts new file mode 100644 index 0000000000000..f7361ba12a570 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultPerPage } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_per_page', () => { + test('it should validate a regular number greater than zero', () => { + const payload = 5; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a string of a number', () => { + const payload = '5'; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(5); + }); + + test('it should not validate a junk string', () => { + const payload = 'invalid-string'; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 20', () => { + const payload = null; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(20); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts new file mode 100644 index 0000000000000..026642f91c08a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero'; + +/** + * Types the DefaultPerPage as: + * - If a string this will convert the string to a number + * - If null or undefined, then a default of 20 will be used + * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + */ +export const DefaultPerPage = new t.Type( + 'DefaultPerPage', + t.number.is, + (input, context): Either => { + if (input == null) { + return t.success(20); + } else if (typeof input === 'string') { + return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); + } else { + return PositiveIntegerGreaterThanZero.validate(input, context); + } + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts new file mode 100644 index 0000000000000..8bd913af9255b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { RiskScoreMapping, risk_score_mapping } from '../risk_score_mapping'; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default risk_score_mapping array will be set + */ +export const DefaultRiskScoreMappingArray = new t.Type< + RiskScoreMapping, + RiskScoreMapping | undefined, + unknown +>( + 'DefaultRiskScoreMappingArray', + risk_score_mapping.is, + (input, context): Either => + input == null ? t.success([]) : risk_score_mapping.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts new file mode 100644 index 0000000000000..58a96eef5a14f --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { SeverityMapping, severity_mapping } from '../severity_mapping'; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default severity_mapping array will be set + */ +export const DefaultSeverityMappingArray = new t.Type< + SeverityMapping, + SeverityMapping | undefined, + unknown +>( + 'DefaultSeverityMappingArray', + severity_mapping.is, + (input, context): Either => + input == null ? t.success([]) : severity_mapping.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts new file mode 100644 index 0000000000000..c7137d9c56b0d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultStringArray } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_string_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of strings', () => { + const payload = ['value 1', 'value 2']; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['value 1', 5]; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts new file mode 100644 index 0000000000000..02f1831cfde29 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultStringArray as: + * - If undefined, then a default array will be set + * - If an array is sent in, then the array will be validated to ensure all elements are a string + */ +export const DefaultStringArray = new t.Type( + 'DefaultStringArray', + t.array(t.string).is, + (input, context): Either => + input == null ? t.success([]) : t.array(t.string).validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts new file mode 100644 index 0000000000000..2443e8f71fecd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultStringBooleanFalse } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_string_boolean_false', () => { + test('it should validate a boolean false', () => { + const payload = false; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a boolean true', () => { + const payload = true; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultStringBooleanFalse"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default false', () => { + const payload = null; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(false); + }); + + test('it should return a default false when given a string of "false"', () => { + const payload = 'false'; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(false); + }); + + test('it should return a default true when given a string of "true"', () => { + const payload = 'true'; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(true); + }); + + test('it should return a default true when given a string of "TruE"', () => { + const payload = 'TruE'; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(true); + }); + + test('it should not work with a string of junk "junk"', () => { + const payload = 'junk'; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "junk" supplied to "DefaultStringBooleanFalse"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not work with an empty string', () => { + const payload = ''; + const decoded = DefaultStringBooleanFalse.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "" supplied to "DefaultStringBooleanFalse"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts new file mode 100644 index 0000000000000..f5f74f90e6b30 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultStringBooleanFalse as: + * - If a string this will convert the string to a boolean + * - If null or undefined, then a default false will be set + */ +export const DefaultStringBooleanFalse = new t.Type( + 'DefaultStringBooleanFalse', + t.boolean.is, + (input, context): Either => { + if (input == null) { + return t.success(false); + } else if (typeof input === 'string' && input.toLowerCase() === 'true') { + return t.success(true); + } else if (typeof input === 'string' && input.toLowerCase() === 'false') { + return t.success(false); + } else { + return t.boolean.validate(input, context); + } + }, + t.identity +); + +export type DefaultStringBooleanFalseC = typeof DefaultStringBooleanFalse; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts new file mode 100644 index 0000000000000..ac86b5508ff14 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Threats } from '../threat'; +import { DefaultThreatArray } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_threat_null', () => { + test('it should validate an empty array', () => { + const payload: Threats = []; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of threats', () => { + const payload: Threats = [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + ]; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + 5, + ]; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultThreatArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default empty array if not provided a value', () => { + const payload = null; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts new file mode 100644 index 0000000000000..8905369d270eb --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { threats, Threats } from '../threat'; + +/** + * Types the DefaultThreatArray as: + * - If null or undefined, then an empty array will be set + */ +export const DefaultThreatArray = new t.Type( + 'DefaultThreatArray', + threats.is, + (input, context): Either => + input == null ? t.success([]) : threats.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts new file mode 100644 index 0000000000000..4b8877bd532c2 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Throttle } from '../throttle'; +import { DefaultThrottleNull } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_throttle_null', () => { + test('it should validate a throttle string', () => { + const payload: Throttle = 'some string'; + const decoded = DefaultThrottleNull.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = 5; + const decoded = DefaultThrottleNull.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultThreatNull"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default "null" if not provided a value', () => { + const payload = undefined; + const decoded = DefaultThrottleNull.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(null); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts new file mode 100644 index 0000000000000..e9b9ec27ea418 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { throttle, ThrottleOrNull } from '../throttle'; + +/** + * Types the DefaultThrottleNull as: + * - If null or undefined, then a null will be set + */ +export const DefaultThrottleNull = new t.Type( + 'DefaultThreatNull', + throttle.is, + (input, context): Either => + input == null ? t.success(null) : throttle.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.ts new file mode 100644 index 0000000000000..bcab8ebd5f17c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultToString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_to_string', () => { + test('it should validate a to string', () => { + const payload = 'now-5m'; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultToString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "now"', () => { + const payload = null; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('now'); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts new file mode 100644 index 0000000000000..9ef50783c08c1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultToString as: + * - If null or undefined, then a default of the string "now" will be used + */ +export const DefaultToString = new t.Type( + 'DefaultToString', + t.string.is, + (input, context): Either => + input == null ? t.success('now') : t.string.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.ts new file mode 100644 index 0000000000000..d8cdff416037c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultUuid } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_uuid', () => { + test('it should validate a regular string', () => { + const payload = '1'; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "DefaultUuid"']); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of a uuid', () => { + const payload = null; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts new file mode 100644 index 0000000000000..01bd58d11e96d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import uuid from 'uuid'; +import { NonEmptyString } from '../non_empty_string'; + +/** + * Types the DefaultUuid as: + * - If null or undefined, then a default string uuid.v4() will be + * created otherwise it will be checked just against an empty string + */ +export const DefaultUuid = new t.Type( + 'DefaultUuid', + t.string.is, + (input, context): Either => + input == null ? t.success(uuid.v4()) : NonEmptyString.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts new file mode 100644 index 0000000000000..b9e9a3ff367e4 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultVersionNumber } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_version_number', () => { + test('it should validate a version number', () => { + const payload = 5; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a 0', () => { + const payload = 0; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a -1', () => { + const payload = -1; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a string', () => { + const payload = '5'; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultVersionNumber"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 1', () => { + const payload = null; + const decoded = DefaultVersionNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(1); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts new file mode 100644 index 0000000000000..245ff9d0db7dd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { version, Version } from '../version'; + +/** + * Types the DefaultVersionNumber as: + * - If null or undefined, then a default of the number 1 will be used + */ +export const DefaultVersionNumber = new t.Type( + 'DefaultVersionNumber', + version.is, + (input, context): Either => + input == null ? t.success(1) : version.validate(input, context), + t.identity +); + +export type DefaultVersionNumberDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/description/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/description/index.ts new file mode 100644 index 0000000000000..eda821f4e54ec --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/description/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const description = t.string; +export type Description = t.TypeOf; +export const descriptionOrUndefined = t.union([description, t.undefined]); +export type DescriptionOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts new file mode 100644 index 0000000000000..86ffba6eeb60a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EmptyStringArray, EmptyStringArrayEncoded } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('empty_string_array', () => { + test('it should validate "null" and create an empty array', () => { + const payload: EmptyStringArrayEncoded = null; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should validate "undefined" and create an empty array', () => { + const payload: EmptyStringArrayEncoded = undefined; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should validate a single value of "a" into an array of size 1 of ["a"]', () => { + const payload: EmptyStringArrayEncoded = 'a'; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['a']); + }); + + test('it should validate 2 values of "a,b" into an array of size 2 of ["a", "b"]', () => { + const payload: EmptyStringArrayEncoded = 'a,b'; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['a', 'b']); + }); + + test('it should validate 3 values of "a,b,c" into an array of size 3 of ["a", "b", "c"]', () => { + const payload: EmptyStringArrayEncoded = 'a,b,c'; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['a', 'b', 'c']); + }); + + test('it should FAIL validation of number', () => { + const payload: number = 5; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "EmptyStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate 3 values of " a, b, c " into an array of size 3 of ["a", "b", "c"] even though they have spaces', () => { + const payload: EmptyStringArrayEncoded = ' a, b, c '; + const decoded = EmptyStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['a', 'b', 'c']); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts new file mode 100644 index 0000000000000..c9c0c7b4113a5 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the EmptyStringArray as: + * - A value that can be undefined, or null (which will be turned into an empty array) + * - A comma separated string that can turn into an array by splitting on it + * - Example input converted to output: undefined -> [] + * - Example input converted to output: null -> [] + * - Example input converted to output: "a,b,c" -> ["a", "b", "c"] + */ +export const EmptyStringArray = new t.Type( + 'EmptyStringArray', + t.array(t.string).is, + (input, context): Either => { + if (input == null) { + return t.success([]); + } else if (typeof input === 'string' && input.trim() !== '') { + const arrayValues = input + .trim() + .split(',') + .map((value) => value.trim()); + const emptyValueFound = arrayValues.some((value) => value === ''); + if (emptyValueFound) { + return t.failure(input, context); + } else { + return t.success(arrayValues); + } + } else { + return t.failure(input, context); + } + }, + String +); + +export type EmptyStringArrayEncoded = t.OutputOf; +export type EmptyStringArrayDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts new file mode 100644 index 0000000000000..512075b319739 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { left, right, Either } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, findDifferencesRecursive } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('exact_check', () => { + test('it returns an error if given extra object properties', () => { + const someType = t.exact( + t.type({ + a: t.string, + }) + ); + const payload = { a: 'test', b: 'test' }; + const decoded = someType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "b"']); + expect(message.schema).toEqual({}); + }); + + test('it returns an error if the data type is not as expected', () => { + type UnsafeCastForTest = Either< + t.Errors, + { + a: number; + } + >; + + const someType = t.exact( + t.type({ + a: t.string, + }) + ); + + const payload = { a: 1 }; + const decoded = someType.decode(payload); + const checked = exactCheck(payload, decoded as UnsafeCastForTest); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "a"']); + expect(message.schema).toEqual({}); + }); + + test('it does NOT return an error if given normal object properties', () => { + const someType = t.exact( + t.type({ + a: t.string, + }) + ); + const payload = { a: 'test' }; + const decoded = someType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will return an existing error and not validate', () => { + const payload = { a: 'test' }; + const validationError: t.ValidationError = { + value: 'Some existing error', + context: [], + message: 'some error', + }; + const error: t.Errors = [validationError]; + const leftValue = left(error); + const checked = exactCheck(payload, leftValue); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['some error']); + expect(message.schema).toEqual({}); + }); + + test('it will work with a regular "right" payload without any decoding', () => { + const payload = { a: 'test' }; + const rightValue = right(payload); + const checked = exactCheck(payload, rightValue); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ a: 'test' }); + }); + + test('it will work with decoding a null payload when the schema expects a null', () => { + const someType = t.union([ + t.exact( + t.type({ + a: t.string, + }) + ), + t.null, + ]); + const payload = null; + const decoded = someType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(null); + }); + + test('it should find no differences recursively with two empty objects', () => { + const difference = findDifferencesRecursive({}, {}); + expect(difference).toEqual([]); + }); + + test('it should find a single difference with two objects with different keys', () => { + const difference = findDifferencesRecursive({ a: 1 }, { b: 1 }); + expect(difference).toEqual(['a']); + }); + + test('it should find a two differences with two objects with multiple different keys', () => { + const difference = findDifferencesRecursive({ a: 1, c: 1 }, { b: 1 }); + expect(difference).toEqual(['a', 'c']); + }); + + test('it should find no differences with two objects with the same keys', () => { + const difference = findDifferencesRecursive({ a: 1, b: 1 }, { a: 1, b: 1 }); + expect(difference).toEqual([]); + }); + + test('it should find a difference with two deep objects with different same keys', () => { + const difference = findDifferencesRecursive({ a: 1, b: { c: 1 } }, { a: 1, b: { d: 1 } }); + expect(difference).toEqual(['c']); + }); + + test('it should find a difference within an array', () => { + const difference = findDifferencesRecursive({ a: 1, b: [{ c: 1 }] }, { a: 1, b: [{ a: 1 }] }); + expect(difference).toEqual(['c']); + }); + + test('it should find a no difference when using arrays that are identical', () => { + const difference = findDifferencesRecursive({ a: 1, b: [{ c: 1 }] }, { a: 1, b: [{ c: 1 }] }); + expect(difference).toEqual([]); + }); + + test('it should find differences when one has an array and the other does not', () => { + const difference = findDifferencesRecursive({ a: 1, b: [{ c: 1 }] }, { a: 1 }); + expect(difference).toEqual(['b', '[{"c":1}]']); + }); + + test('it should find differences when one has an deep object and the other does not', () => { + const difference = findDifferencesRecursive({ a: 1, b: { c: 1 } }, { a: 1 }); + expect(difference).toEqual(['b', '{"c":1}']); + }); + + test('it should find differences when one has a deep object with multiple levels and the other does not', () => { + const difference = findDifferencesRecursive({ a: 1, b: { c: { d: 1 } } }, { a: 1 }); + expect(difference).toEqual(['b', '{"c":{"d":1}}']); + }); + + test('it tests two deep objects as the same with no key differences', () => { + const difference = findDifferencesRecursive( + { a: 1, b: { c: { d: 1 } } }, + { a: 1, b: { c: { d: 1 } } } + ); + expect(difference).toEqual([]); + }); + + test('it tests two deep objects with just one deep key difference', () => { + const difference = findDifferencesRecursive( + { a: 1, b: { c: { d: 1 } } }, + { a: 1, b: { c: { e: 1 } } } + ); + expect(difference).toEqual(['d']); + }); + + test('it should not find any differences when the original and decoded are both null', () => { + const difference = findDifferencesRecursive(null, null); + expect(difference).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts new file mode 100644 index 0000000000000..23931b393990a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { left, Either, fold, right } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { isObject, get } from 'lodash/fp'; + +/** + * Given an original object and a decoded object this will return an error + * if and only if the original object has additional keys that the decoded + * object does not have. If the original decoded already has an error, then + * this will return the error as is and not continue. + * + * NOTE: You MUST use t.exact(...) for this to operate correctly as your schema + * needs to remove additional keys before the compare + * + * You might not need this in the future if the below issue is solved: + * https://github.com/gcanti/io-ts/issues/322 + * + * @param original The original to check if it has additional keys + * @param decoded The decoded either which has either an existing error or the + * decoded object which could have additional keys stripped from it. + */ +export const exactCheck = ( + original: unknown, + decoded: Either +): Either => { + const onLeft = (errors: t.Errors): Either => left(errors); + const onRight = (decodedValue: T): Either => { + const differences = findDifferencesRecursive(original, decodedValue); + if (differences.length !== 0) { + const validationError: t.ValidationError = { + value: differences, + context: [], + message: `invalid keys "${differences.join(',')}"`, + }; + const error: t.Errors = [validationError]; + return left(error); + } else { + return right(decodedValue); + } + }; + return pipe(decoded, fold(onLeft, onRight)); +}; + +export const findDifferencesRecursive = (original: unknown, decodedValue: T): string[] => { + if (decodedValue === null && original === null) { + // both the decodedValue and the original are null which indicates that they are equal + // so do not report differences + return []; + } else if (decodedValue == null) { + try { + // It is null and painful when the original contains an object or an array + // the the decoded value does not have. + return [JSON.stringify(original)]; + } catch (err) { + return ['circular reference']; + } + } else if (typeof original !== 'object' || original == null) { + // We are not an object or null so do not report differences + return []; + } else { + const decodedKeys = Object.keys(decodedValue); + const differences = Object.keys(original).flatMap((originalKey) => { + const foundKey = decodedKeys.some((key) => key === originalKey); + const topLevelKey = foundKey ? [] : [originalKey]; + // I use lodash to cheat and get an any (not going to lie ;-)) + const valueObjectOrArrayOriginal = get(originalKey, original); + const valueObjectOrArrayDecoded = get(originalKey, decodedValue); + if (isObject(valueObjectOrArrayOriginal)) { + return [ + ...topLevelKey, + ...findDifferencesRecursive(valueObjectOrArrayOriginal, valueObjectOrArrayDecoded), + ]; + } else if (Array.isArray(valueObjectOrArrayOriginal)) { + return [ + ...topLevelKey, + ...valueObjectOrArrayOriginal.flatMap((arrayElement, index) => + findDifferencesRecursive(arrayElement, get(index, valueObjectOrArrayDecoded)) + ), + ]; + } else { + return topLevelKey; + } + }); + return differences; + } +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts new file mode 100644 index 0000000000000..f47f8f87f6a7e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { formatErrors } from '.'; + +describe('utils', () => { + test('returns an empty error message string if there are no errors', () => { + const errors: t.Errors = []; + const output = formatErrors(errors); + expect(output).toEqual([]); + }); + + test('returns a single error message if given one', () => { + const validationError: t.ValidationError = { + value: 'Some existing error', + context: [], + message: 'some error', + }; + const errors: t.Errors = [validationError]; + const output = formatErrors(errors); + expect(output).toEqual(['some error']); + }); + + test('returns a two error messages if given two', () => { + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context: [], + message: 'some error 1', + }; + const validationError2: t.ValidationError = { + value: 'Some existing error 2', + context: [], + message: 'some error 2', + }; + const errors: t.Errors = [validationError1, validationError2]; + const output = formatErrors(errors); + expect(output).toEqual(['some error 1', 'some error 2']); + }); + + test('it filters out duplicate error messages', () => { + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context: [], + message: 'some error 1', + }; + const validationError2: t.ValidationError = { + value: 'Some existing error 1', + context: [], + message: 'some error 1', + }; + const errors: t.Errors = [validationError1, validationError2]; + const output = formatErrors(errors); + expect(output).toEqual(['some error 1']); + }); + + test('will use message before context if it is set', () => { + const context: t.Context = ([{ key: 'some string key' }] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + message: 'I should be used first', + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual(['I should be used first']); + }); + + test('will use context entry of a single string', () => { + const context: t.Context = ([{ key: 'some string key' }] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual(['Invalid value "Some existing error 1" supplied to "some string key"']); + }); + + test('will use two context entries of two strings', () => { + const context: t.Context = ([ + { key: 'some string key 1' }, + { key: 'some string key 2' }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual([ + 'Invalid value "Some existing error 1" supplied to "some string key 1,some string key 2"', + ]); + }); + + test('will filter out and not use any strings of numbers', () => { + const context: t.Context = ([ + { key: '5' }, + { key: 'some string key 2' }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual([ + 'Invalid value "Some existing error 1" supplied to "some string key 2"', + ]); + }); + + test('will filter out and not use null', () => { + const context: t.Context = ([ + { key: null }, + { key: 'some string key 2' }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual([ + 'Invalid value "Some existing error 1" supplied to "some string key 2"', + ]); + }); + + test('will filter out and not use empty strings', () => { + const context: t.Context = ([ + { key: '' }, + { key: 'some string key 2' }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual([ + 'Invalid value "Some existing error 1" supplied to "some string key 2"', + ]); + }); + + test('will use a name context if it cannot find a keyContext', () => { + const context: t.Context = ([ + { key: '' }, + { key: '', type: { name: 'someName' } }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual(['Invalid value "Some existing error 1" supplied to "someName"']); + }); + + test('will return an empty string if name does not exist but type does', () => { + const context: t.Context = ([{ key: '' }, { key: '', type: {} }] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: 'Some existing error 1', + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual(['Invalid value "Some existing error 1" supplied to ""']); + }); + + test('will stringify an error value', () => { + const context: t.Context = ([ + { key: '' }, + { key: 'some string key 2' }, + ] as unknown) as t.Context; + const validationError1: t.ValidationError = { + value: { foo: 'some error' }, + context, + }; + const errors: t.Errors = [validationError1]; + const output = formatErrors(errors); + expect(output).toEqual([ + 'Invalid value "{"foo":"some error"}" supplied to "some string key 2"', + ]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts new file mode 100644 index 0000000000000..7df66dcd13596 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { isObject } from 'lodash/fp'; + +export const formatErrors = (errors: t.Errors): string[] => { + const err = errors.map((error) => { + if (error.message != null) { + return error.message; + } else { + const keyContext = error.context + .filter( + (entry) => entry.key != null && !Number.isInteger(+entry.key) && entry.key.trim() !== '' + ) + .map((entry) => entry.key) + .join(','); + + const nameContext = error.context.find((entry) => entry.type?.name?.length > 0); + const suppliedValue = + keyContext !== '' ? keyContext : nameContext != null ? nameContext.type.name : ''; + const value = isObject(error.value) ? JSON.stringify(error.value) : error.value; + return `Invalid value "${value}" supplied to "${suppliedValue}"`; + } + }); + + return [...new Set(err)]; +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/from/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/from/index.ts new file mode 100644 index 0000000000000..963e2fa0444f0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/from/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Either } from 'fp-ts/lib/Either'; +import * as t from 'io-ts'; +import { parseScheduleDates } from '../parse_schedule_dates'; + +const stringValidator = (input: unknown): input is string => typeof input === 'string'; + +export const from = new t.Type( + 'From', + t.string.is, + (input, context): Either => { + if (stringValidator(input) && parseScheduleDates(input) == null) { + return t.failure(input, context, 'Failed to parse "from" on rule param'); + } + return t.string.validate(input, context); + }, + t.identity +); +export type From = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/id/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/id/index.ts new file mode 100644 index 0000000000000..7b187d7730f73 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/id/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../non_empty_string'; + +export const id = NonEmptyString; +export type Id = t.TypeOf; +export const idOrUndefined = t.union([id, t.undefined]); +export type IdOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/index.ts new file mode 100644 index 0000000000000..bae90fed29dea --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/index.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './format_errors'; +export * from './actions'; +export * from './constants'; +export * from './created_at'; +export * from './created_by'; +export * from './default_version_number'; +export * from './default_actions_array'; +export * from './default_array'; +export * from './default_boolean_false'; +export * from './default_boolean_true'; +export * from './default_empty_string'; +export * from './default_export_file_name'; +export * from './default_from_string'; +export * from './default_interval_string'; +export * from './default_language_string'; +export * from './default_max_signals_number'; +export * from './default_page'; +export * from './default_per_page'; +export * from './default_risk_score_mapping_array'; +export * from './default_severity_mapping_array'; +export * from './default_string_array'; +export * from './default_string_boolean_false'; +export * from './default_threat_array'; +export * from './default_throttle_null'; +export * from './default_to_string'; +export * from './default_uuid'; +export * from './default_version_number'; +export * from './description'; +export * from './empty_string_array'; +export * from './exact_check'; +export * from './format_errors'; +export * from './from'; +export * from './id'; +export * from './iso_date_string'; +export * from './language'; +export * from './max_signals'; +export * from './meta'; +export * from './name'; +export * from './non_empty_array'; +export * from './non_empty_or_nullable_string_array'; +export * from './non_empty_string'; +export * from './normalized_ml_job_id'; +export * from './only_false_allowed'; +export * from './operator'; +export * from './parse_schedule_dates'; +export * from './positive_integer'; +export * from './positive_integer_greater_than_zero'; +export * from './references_default_array'; +export * from './risk_score'; +export * from './risk_score_mapping'; +export * from './saved_object_attributes'; +export * from './severity'; +export * from './severity_mapping'; +export * from './string_to_positive_number'; +export * from './tags'; +export * from './threat'; +export * from './threat_mapping'; +export * from './threat_subtechnique'; +export * from './threat_tactic'; +export * from './threat_technique'; +export * from './throttle'; +export * from './updated_at'; +export * from './updated_by'; +export * from './uuid'; +export * from './validate'; +export * from './version'; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts new file mode 100644 index 0000000000000..4b73ed1b136dc --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { IsoDateString } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('ios_date_string', () => { + test('it should validate a iso string', () => { + const payload = '2020-02-26T00:32:34.541Z'; + const decoded = IsoDateString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an epoch number', () => { + const payload = '1582677283067'; + const decoded = IsoDateString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1582677283067" supplied to "IsoDateString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a number such as 2000', () => { + const payload = '2000'; + const decoded = IsoDateString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "2000" supplied to "IsoDateString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a UTC', () => { + const payload = 'Wed, 26 Feb 2020 00:36:20 GMT'; + const decoded = IsoDateString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "Wed, 26 Feb 2020 00:36:20 GMT" supplied to "IsoDateString"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts new file mode 100644 index 0000000000000..f59b07c554b65 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the IsoDateString as: + * - A string that is an ISOString + */ +export const IsoDateString = new t.Type( + 'IsoDateString', + t.string.is, + (input, context): Either => { + if (typeof input === 'string') { + try { + const parsed = new Date(input); + if (parsed.toISOString() === input) { + return t.success(input); + } else { + return t.failure(input, context); + } + } catch (err) { + return t.failure(input, context); + } + } else { + return t.failure(input, context); + } + }, + t.identity +); + +export type IsoDateStringC = typeof IsoDateString; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/language/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/language/index.ts new file mode 100644 index 0000000000000..fc3f70f1f2d88 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/language/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const language = t.keyof({ eql: null, kuery: null, lucene: null }); +export type Language = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts new file mode 100644 index 0000000000000..56440d628e4aa --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Comment, CommentsArray } from '.'; +import { DATE_NOW, ID, USER } from '../../constants/index.mock'; + +export const getCommentsMock = (): Comment => ({ + comment: 'some old comment', + created_at: DATE_NOW, + created_by: USER, + id: ID, +}); + +export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts new file mode 100644 index 0000000000000..0f0bfac5e2068 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getCommentsArrayMock, getCommentsMock } from './index.mock'; +import { + Comment, + comment, + CommentsArray, + commentsArray, + CommentsArrayOrUndefined, + commentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { DATE_NOW } from '../../constants/index.mock'; + +describe('Comment', () => { + describe('comment', () => { + test('it fails validation when "id" is undefined', () => { + const payload = { ...getCommentsMock(), id: undefined }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it passes validation with a typical comment', () => { + const payload = getCommentsMock(); + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation with "updated_at" and "updated_by" fields included', () => { + const payload = getCommentsMock(); + payload.updated_at = DATE_NOW; + payload.updated_by = 'someone'; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is an empty string', () => { + const payload: Omit & { comment: string } = { + ...getCommentsMock(), + comment: '', + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is not a string', () => { + const payload: Omit & { comment: string[] } = { + ...getCommentsMock(), + comment: ['some value'], + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "created_at" is not a string', () => { + const payload: Omit & { created_at: number } = { + ...getCommentsMock(), + created_at: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "created_by" is not a string', () => { + const payload: Omit & { created_by: number } = { + ...getCommentsMock(), + created_by: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "updated_at" is not a string', () => { + const payload: Omit & { updated_at: number } = { + ...getCommentsMock(), + updated_at: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "updated_by" is not a string', () => { + const payload: Omit & { updated_by: number } = { + ...getCommentsMock(), + updated_by: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: Comment & { + extraKey?: string; + } = getCommentsMock(); + payload.extraKey = 'some value'; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getCommentsMock()); + }); + }); + + describe('commentsArray', () => { + test('it passes validation an array of Comment', () => { + const payload = getCommentsArrayMock(); + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when a Comment includes "updated_at" and "updated_by"', () => { + const commentsPayload = getCommentsMock(); + commentsPayload.updated_at = DATE_NOW; + commentsPayload.updated_by = 'someone'; + const payload = [{ ...commentsPayload }, ...getCommentsArrayMock()]; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when array includes non Comment types', () => { + const payload = ([1] as unknown) as CommentsArray; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('commentsArrayOrUndefined', () => { + test('it passes validation an array of Comment', () => { + const payload = getCommentsArrayMock(); + const decoded = commentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when undefined', () => { + const payload = undefined; + const decoded = commentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when array includes non Comment types', () => { + const payload = ([1] as unknown) as CommentsArrayOrUndefined; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts new file mode 100644 index 0000000000000..783d8606b8a96 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { NonEmptyString } from '../../non_empty_string'; +import { created_at } from '../../created_at'; +import { created_by } from '../../created_by'; +import { id } from '../../id'; +import { updated_at } from '../../updated_at'; +import { updated_by } from '../../updated_by'; + +export const comment = t.intersection([ + t.exact( + t.type({ + comment: NonEmptyString, + created_at, + created_by, + id, + }) + ), + t.exact( + t.partial({ + updated_at, + updated_by, + }) + ), +]); + +export const commentsArray = t.array(comment); +export type CommentsArray = t.TypeOf; +export type Comment = t.TypeOf; +export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]); +export type CommentsArrayOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts new file mode 100644 index 0000000000000..0d0e2b51d003c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CreateComment, CreateCommentsArray } from '.'; + +export const getCreateCommentsMock = (): CreateComment => ({ + comment: 'some comments', +}); + +export const getCreateCommentsArrayMock = (): CreateCommentsArray => [getCreateCommentsMock()]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts new file mode 100644 index 0000000000000..1ac605e232ea1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getCreateCommentsArrayMock, getCreateCommentsMock } from './index.mock'; +import { + CreateComment, + createComment, + CreateCommentsArray, + createCommentsArray, + CreateCommentsArrayOrUndefined, + createCommentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('CreateComment', () => { + describe('createComment', () => { + test('it passes validation with a default comment', () => { + const payload = getCreateCommentsMock(); + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "{| comment: NonEmptyString |}"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is not a string', () => { + const payload: Omit & { comment: string[] } = { + ...getCreateCommentsMock(), + comment: ['some value'], + }; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: CreateComment & { + extraKey?: string; + } = getCreateCommentsMock(); + payload.extraKey = 'some value'; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getCreateCommentsMock()); + }); + }); + + describe('createCommentsArray', () => { + test('it passes validation an array of comments', () => { + const payload = getCreateCommentsArrayMock(); + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when array includes non comments types', () => { + const payload = ([1] as unknown) as CreateCommentsArray; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('createCommentsArrayOrUndefined', () => { + test('it passes validation an array of comments', () => { + const payload = getCreateCommentsArrayMock(); + const decoded = createCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when undefined', () => { + const payload = undefined; + const decoded = createCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when array includes non comments types', () => { + const payload = ([1] as unknown) as CreateCommentsArrayOrUndefined; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts new file mode 100644 index 0000000000000..438f946e796d6 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; + +export const createComment = t.exact( + t.type({ + comment: NonEmptyString, + }) +); + +export type CreateComment = t.TypeOf; +export const createCommentsArray = t.array(createComment); +export type CreateCommentsArray = t.TypeOf; +export type CreateComments = t.TypeOf; +export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]); +export type CreateCommentsArrayOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts new file mode 100644 index 0000000000000..5e667380e2adf --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { CommentsArray } from '../comment'; +import { DefaultCommentsArray } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getCommentsArrayMock } from '../comment/index.mock'; + +describe('default_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: CommentsArray = []; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: CommentsArray = getCommentsArrayMock(); + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts new file mode 100644 index 0000000000000..c2f81b82a0c0a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { comment, CommentsArray } from '../comment'; + +/** + * Types the DefaultCommentsArray as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultCommentsArray = new t.Type( + 'DefaultCommentsArray', + t.array(comment).is, + (input): Either => + input == null ? t.success([]) : t.array(comment).decode(input), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts new file mode 100644 index 0000000000000..a4581fabbf6a9 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { CommentsArray } from '../comment'; +import { DefaultCommentsArray } from '../default_comments_array'; +import { getCommentsArrayMock } from '../comment/index.mock'; + +describe('default_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: CommentsArray = []; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: CommentsArray = getCommentsArrayMock(); + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts new file mode 100644 index 0000000000000..ba3babdabf267 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { createComment, CreateCommentsArray } from '../create_comment'; + +/** + * Types the DefaultCreateComments as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultCreateCommentsArray = new t.Type< + CreateCommentsArray, + CreateCommentsArray, + unknown +>( + 'DefaultCreateComments', + t.array(createComment).is, + (input): Either => + input == null ? t.success([]) : t.array(createComment).decode(input), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts new file mode 100644 index 0000000000000..1decca0de6c50 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultNamespace } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('default_namespace', () => { + test('it should validate "single"', () => { + const payload = 'single'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate "agnostic"', () => { + const payload = 'agnostic'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it defaults to "single" if "undefined"', () => { + const payload = undefined; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('single'); + }); + + test('it defaults to "single" if "null"', () => { + const payload = null; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('single'); + }); + + test('it should FAIL validation if not "single" or "agnostic"', () => { + const payload = 'something else'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + `Invalid value "something else" supplied to "DefaultNamespace"`, + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts new file mode 100644 index 0000000000000..14c516da0b6f0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +export const namespaceType = t.keyof({ agnostic: null, single: null }); +export type NamespaceType = t.TypeOf; + +/** + * Types the DefaultNamespace as: + * - If null or undefined, then a default string/enumeration of "single" will be used. + */ +export const DefaultNamespace = new t.Type( + 'DefaultNamespace', + namespaceType.is, + (input, context): Either => + input == null ? t.success('single') : namespaceType.validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts new file mode 100644 index 0000000000000..8bc7a16b96097 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultNamespaceArray, DefaultNamespaceArrayType } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('default_namespace_array', () => { + test('it should validate "null" single item as an array with a "single" value', () => { + const payload: DefaultNamespaceArrayType = null; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single']); + }); + + test('it should FAIL validation of numeric value', () => { + const payload = 5; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultNamespaceArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate "undefined" item as an array with a "single" value', () => { + const payload: DefaultNamespaceArrayType = undefined; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single']); + }); + + test('it should validate "single" as an array of a "single" value', () => { + const payload: DefaultNamespaceArrayType = 'single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([payload]); + }); + + test('it should validate "agnostic" as an array of a "agnostic" value', () => { + const payload: DefaultNamespaceArrayType = 'agnostic'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([payload]); + }); + + test('it should validate "single,agnostic" as an array of 2 values of ["single", "agnostic"] values', () => { + const payload: DefaultNamespaceArrayType = 'agnostic,single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['agnostic', 'single']); + }); + + test('it should validate 3 elements of "single,agnostic,single" as an array of 3 values of ["single", "agnostic", "single"] values', () => { + const payload: DefaultNamespaceArrayType = 'single,agnostic,single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single', 'agnostic', 'single']); + }); + + test('it should validate 3 elements of "single,agnostic, single" as an array of 3 values of ["single", "agnostic", "single"] values when there are spaces', () => { + const payload: DefaultNamespaceArrayType = ' single, agnostic, single '; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single', 'agnostic', 'single']); + }); + + test('it should FAIL validation when given 3 elements of "single,agnostic,junk" since the 3rd value is junk', () => { + const payload: DefaultNamespaceArrayType = 'single,agnostic,junk'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "junk" supplied to "DefaultNamespaceArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts new file mode 100644 index 0000000000000..75f0cb459119b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { namespaceType } from '../default_namespace'; + +export const namespaceTypeArray = t.array(namespaceType); +export type NamespaceTypeArray = t.TypeOf; + +/** + * Types the DefaultNamespaceArray as: + * - If null or undefined, then a default string array of "single" will be used. + * - If it contains a string, then it is split along the commas and puts them into an array and validates it + */ +export const DefaultNamespaceArray = new t.Type< + NamespaceTypeArray, + string | undefined | null, + unknown +>( + 'DefaultNamespaceArray', + namespaceTypeArray.is, + (input, context): Either => { + if (input == null) { + return t.success(['single']); + } else if (typeof input === 'string') { + const commaSeparatedValues = input + .trim() + .split(',') + .map((value) => value.trim()); + return namespaceTypeArray.validate(commaSeparatedValues, context); + } + return t.failure(input, context); + }, + String +); + +export type DefaultNamespaceArrayType = t.OutputOf; +export type DefaultNamespaceArrayTypeDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts new file mode 100644 index 0000000000000..f52baa49530ec --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { UpdateCommentsArray } from '../update_comment'; +import { DefaultUpdateCommentsArray } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getUpdateCommentsArrayMock } from '../update_comment/index.mock'; + +describe('default_update_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: UpdateCommentsArray = []; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: UpdateCommentsArray = getUpdateCommentsArrayMock(); + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts new file mode 100644 index 0000000000000..26bfdad597100 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { updateCommentsArray, UpdateCommentsArray } from '../update_comment'; + +/** + * Types the DefaultCommentsUpdate as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultUpdateCommentsArray = new t.Type< + UpdateCommentsArray, + UpdateCommentsArray, + unknown +>( + 'DefaultCreateComments', + updateCommentsArray.is, + (input): Either => + input == null ? t.success([]) : updateCommentsArray.decode(input), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts new file mode 100644 index 0000000000000..e491b50b0f9c8 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EndpointEntriesArray } from '.'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; + +export const getEndpointEntriesArrayMock = (): EndpointEntriesArray => [ + getEndpointEntryMatchMock(), + getEndpointEntryMatchAnyMock(), + getEndpointEntryNestedMock(), +]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts new file mode 100644 index 0000000000000..f5cb89ee79607 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { + endpointEntriesArray, + nonEmptyEndpointEntriesArray, + NonEmptyEndpointEntriesArray, +} from '.'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; +import { getEndpointEntriesArrayMock } from './index.mock'; +import { getEntryListMock } from '../../entries_list/index.mock'; +import { getEntryExistsMock } from '../../entries_exist/index.mock'; + +describe('Endpoint', () => { + describe('entriesArray', () => { + test('it should validate an array with match entry', () => { + const payload = [getEndpointEntryMatchMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with match_any entry', () => { + const payload = [getEndpointEntryMatchAnyMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate an empty array', () => { + const payload: NonEmptyEndpointEntriesArray = []; + const decoded = nonEmptyEndpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyEndpointEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { + const payload: NonEmptyEndpointEntriesArray = [getEndpointEntryMatchAnyMock()]; + const guarded = nonEmptyEndpointEntriesArray.is(payload); + expect(guarded).toBeTruthy(); + }); + + test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { + const payload: NonEmptyEndpointEntriesArray = []; + const guarded = nonEmptyEndpointEntriesArray.is(payload); + expect(guarded).toBeFalsy(); + }); + + test('it should NOT validate an array with exists entry', () => { + const payload = [getEntryExistsMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "exists" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate an array with list entry', () => { + const payload = [getEntryListMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "list" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEndpointEntryNestedMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with all types of entries', () => { + const payload = getEndpointEntriesArrayMock(); + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.ts new file mode 100644 index 0000000000000..451131dafc459 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { endpointEntryMatch } from '../entry_match'; +import { endpointEntryMatchAny } from '../entry_match_any'; +import { endpointEntryNested } from '../entry_nested'; + +export const endpointEntriesArray = t.array( + t.union([endpointEntryMatch, endpointEntryMatchAny, endpointEntryNested]) +); +export type EndpointEntriesArray = t.TypeOf; + +/** + * Types the nonEmptyEndpointEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEndpointEntriesArray = new t.Type< + EndpointEntriesArray, + EndpointEntriesArray, + unknown +>( + 'NonEmptyEndpointEntriesArray', + (u: unknown): u is EndpointEntriesArray => endpointEntriesArray.is(u) && u.length > 0, + (input, context): Either => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return endpointEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEndpointEntriesArray = t.OutputOf; +export type NonEmptyEndpointEntriesArrayDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts new file mode 100644 index 0000000000000..7104406c4869c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EndpointEntryMatch } from '.'; +import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../../constants/index.mock'; + +export const getEndpointEntryMatchMock = (): EndpointEntryMatch => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH, + value: ENTRY_VALUE, +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts new file mode 100644 index 0000000000000..cc0423fc119c7 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchMock } from './index.mock'; +import { EndpointEntryMatch, endpointEntryMatch } from '.'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { getEntryMatchMock } from '../../entry_match/index.mock'; + +describe('endpointEntryMatch', () => { + test('it should validate an entry', () => { + const payload = getEndpointEntryMatchMock(); + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate when "operator" is "excluded"', () => { + // Use the generic entry mock so we can test operator: excluded + const payload = getEntryMatchMock(); + payload.operator = 'excluded'; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "excluded" supplied to "operator"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { field: string } = { + ...getEndpointEntryMatchMock(), + field: '', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit & { value: string[] } = { + ...getEndpointEntryMatchMock(), + value: ['some value'], + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit & { value: string } = { + ...getEndpointEntryMatchMock(), + value: '', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match"', () => { + const payload: Omit & { type: string } = { + ...getEndpointEntryMatchMock(), + type: 'match_any', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryMatch & { + extraKey?: string; + } = getEndpointEntryMatchMock(); + payload.extraKey = 'some value'; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts new file mode 100644 index 0000000000000..83e2a0f61bb4a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { operatorIncluded } from '../../../operator'; +import { NonEmptyString } from '../../../non_empty_string'; + +export const endpointEntryMatch = t.exact( + t.type({ + field: NonEmptyString, + operator: operatorIncluded, + type: t.keyof({ match: null }), + value: NonEmptyString, + }) +); +export type EndpointEntryMatch = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts new file mode 100644 index 0000000000000..95bd6008f1d7c --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../../constants/index.mock'; +import { EndpointEntryMatchAny } from '.'; + +export const getEndpointEntryMatchAnyMock = (): EndpointEntryMatchAny => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH_ANY, + value: [ENTRY_VALUE], +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts new file mode 100644 index 0000000000000..0fd878986d5a2 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchAnyMock } from './index.mock'; +import { EndpointEntryMatchAny, endpointEntryMatchAny } from '.'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { getEntryMatchAnyMock } from '../../entry_match_any/index.mock'; + +describe('endpointEntryMatchAny', () => { + test('it should validate an entry', () => { + const payload = getEndpointEntryMatchAnyMock(); + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate when operator is "excluded"', () => { + // Use the generic entry mock so we can test operator: excluded + const payload = getEntryMatchAnyMock(); + payload.operator = 'excluded'; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "excluded" supplied to "operator"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when field is empty string', () => { + const payload: Omit & { field: string } = { + ...getEndpointEntryMatchAnyMock(), + field: '', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is empty array', () => { + const payload: Omit & { value: string[] } = { + ...getEndpointEntryMatchAnyMock(), + value: [], + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is not string array', () => { + const payload: Omit & { value: string } = { + ...getEndpointEntryMatchAnyMock(), + value: 'some string', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match_any"', () => { + const payload: Omit & { type: string } = { + ...getEndpointEntryMatchAnyMock(), + type: 'match', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryMatchAny & { + extraKey?: string; + } = getEndpointEntryMatchAnyMock(); + payload.extraKey = 'some extra key'; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchAnyMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts new file mode 100644 index 0000000000000..b39a428bb49dd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { nonEmptyOrNullableStringArray } from '../../../non_empty_or_nullable_string_array'; +import { operatorIncluded } from '../../../operator'; +import { NonEmptyString } from '../../../non_empty_string'; + +export const endpointEntryMatchAny = t.exact( + t.type({ + field: NonEmptyString, + operator: operatorIncluded, + type: t.keyof({ match_any: null }), + value: nonEmptyOrNullableStringArray, + }) +); +export type EndpointEntryMatchAny = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts new file mode 100644 index 0000000000000..b66c5a2588eef --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { operatorIncluded } from '../../../operator'; +import { NonEmptyString } from '../../../non_empty_string'; + +export const endpointEntryMatchWildcard = t.exact( + t.type({ + field: NonEmptyString, + operator: operatorIncluded, + type: t.keyof({ wildcard: null }), + value: NonEmptyString, + }) +); +export type EndpointEntryMatchWildcard = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts new file mode 100644 index 0000000000000..f59e29c8ce526 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EndpointEntryNested } from '.'; +import { FIELD, NESTED } from '../../../constants/index.mock'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; + +export const getEndpointEntryNestedMock = (): EndpointEntryNested => ({ + entries: [getEndpointEntryMatchMock(), getEndpointEntryMatchAnyMock()], + field: FIELD, + type: NESTED, +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts new file mode 100644 index 0000000000000..03c02f67b71ad --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EndpointEntryNested, endpointEntryNested } from '.'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { getEndpointEntryNestedMock } from './index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { + nonEmptyEndpointNestedEntriesArray, + NonEmptyEndpointNestedEntriesArray, +} from '../non_empty_nested_entries_array'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryExistsMock } from '../../entries_exist/index.mock'; + +describe('endpointEntryNested', () => { + test('it should validate a nested entry', () => { + const payload = getEndpointEntryNestedMock(); + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "type" is not "nested"', () => { + const payload: Omit & { type: 'match' } = { + ...getEndpointEntryNestedMock(), + type: 'match', + }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { + field: string; + } = { ...getEndpointEntryNestedMock(), field: '' }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is not a string', () => { + const payload: Omit & { + field: number; + } = { ...getEndpointEntryNestedMock(), field: 1 }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "entries" is not an array', () => { + const payload: Omit & { + entries: string; + } = { ...getEndpointEntryNestedMock(), entries: 'im a string' }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "im a string" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate when "entries" contains an entry item that is type "match"', () => { + const payload = { ...getEndpointEntryNestedMock(), entries: [getEndpointEntryMatchAnyMock()] }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host name'], + }, + ], + field: 'host.name', + type: 'nested', + }); + }); + + test('it should NOT validate when "entries" contains an entry item that is type "exists"', () => { + const payload = { ...getEndpointEntryNestedMock(), entries: [getEntryExistsMock()] }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "exists" supplied to "entries,type"', + 'Invalid value "undefined" supplied to "entries,value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryNested & { + extraKey?: string; + } = getEndpointEntryNestedMock(); + payload.extraKey = 'some extra key'; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEndpointEntryNestedMock()); + }); + + test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { + const payload: NonEmptyEndpointNestedEntriesArray = [ + getEndpointEntryMatchMock(), + getEndpointEntryMatchAnyMock(), + ]; + const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); + expect(guarded).toBeTruthy(); + }); + + test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { + const payload: NonEmptyEndpointNestedEntriesArray = []; + const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); + expect(guarded).toBeFalsy(); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts new file mode 100644 index 0000000000000..249dcc9077b34 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../../non_empty_string'; +import { nonEmptyEndpointNestedEntriesArray } from '../non_empty_nested_entries_array'; + +export const endpointEntryNested = t.exact( + t.type({ + entries: nonEmptyEndpointNestedEntriesArray, + field: NonEmptyString, + type: t.keyof({ nested: null }), + }) +); +export type EndpointEntryNested = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts new file mode 100644 index 0000000000000..212b5de1470ff --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './entries'; +export * from './entry_match'; +export * from './entry_match_any'; +export * from './entry_match_wildcard'; +export * from './entry_nested'; +export * from './non_empty_nested_entries_array'; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts new file mode 100644 index 0000000000000..7714476921e68 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { endpointEntryMatch } from '../entry_match'; +import { endpointEntryMatchAny } from '../entry_match_any'; + +export const endpointNestedEntriesArray = t.array( + t.union([endpointEntryMatch, endpointEntryMatchAny]) +); +export type EndpointNestedEntriesArray = t.TypeOf; + +/** + * Types the nonEmptyNestedEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEndpointNestedEntriesArray = new t.Type< + EndpointNestedEntriesArray, + EndpointNestedEntriesArray, + unknown +>( + 'NonEmptyEndpointNestedEntriesArray', + (u: unknown): u is EndpointNestedEntriesArray => endpointNestedEntriesArray.is(u) && u.length > 0, + (input, context): Either => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return endpointNestedEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEndpointNestedEntriesArray = t.OutputOf< + typeof nonEmptyEndpointNestedEntriesArray +>; +export type NonEmptyEndpointNestedEntriesArrayDecoded = t.TypeOf< + typeof nonEmptyEndpointNestedEntriesArray +>; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts new file mode 100644 index 0000000000000..77d577b7aed96 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntriesArray } from '.'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryListMock } from '../entries_list/index.mock'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +export const getListAndNonListEntriesArrayMock = (): EntriesArray => [ + getEntryMatchMock(), + getEntryMatchAnyMock(), + getEntryListMock(), + getEntryExistsMock(), + getEntryNestedMock(), +]; + +export const getListEntriesArrayMock = (): EntriesArray => [getEntryListMock(), getEntryListMock()]; + +export const getEntriesArrayMock = (): EntriesArray => [ + getEntryMatchMock(), + getEntryMatchAnyMock(), + getEntryExistsMock(), + getEntryNestedMock(), +]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts new file mode 100644 index 0000000000000..b6e448f94ce6a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { entriesArray, entriesArrayOrUndefined, entry } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryListMock } from '../entries_list/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; +import { getEntriesArrayMock } from './index.mock'; + +describe('Entries', () => { + describe('entry', () => { + test('it should validate a match entry', () => { + const payload = getEntryMatchMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a match_any entry', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a exists entry', () => { + const payload = getEntryExistsMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a list entry', () => { + const payload = getEntryListMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation of nested entry', () => { + const payload = getEntryNestedMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "operator"', + 'Invalid value "nested" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "list"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('entriesArray', () => { + test('it should validate an array with match entry', () => { + const payload = [getEntryMatchMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with match_any entry', () => { + const payload = [getEntryMatchAnyMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with exists entry', () => { + const payload = [getEntryExistsMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with list entry', () => { + const payload = [getEntryListMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEntryNestedMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with all types of entries', () => { + const payload = [...getEntriesArrayMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); + + describe('entriesArrayOrUndefined', () => { + test('it should validate undefined', () => { + const payload = undefined; + const decoded = entriesArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEntryNestedMock()]; + const decoded = entriesArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts new file mode 100644 index 0000000000000..5164c62e3f5e0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { entriesExists } from '../entries_exist'; +import { entriesList } from '../entries_list'; +import { entriesMatch } from '../entry_match'; +import { entriesMatchAny } from '../entry_match_any'; +import { entriesMatchWildcard } from '../entry_match_wildcard'; +import { entriesNested } from '../entry_nested'; + +// NOTE: Type nested is not included here to denote it's non-recursive nature. +// So a nested entry is really just a collection of `Entry` types. +export const entry = t.union([ + entriesMatch, + entriesMatchAny, + entriesList, + entriesExists, + entriesMatchWildcard, +]); +export type Entry = t.TypeOf; + +export const entriesArray = t.array( + t.union([ + entriesMatch, + entriesMatchAny, + entriesList, + entriesExists, + entriesNested, + entriesMatchWildcard, + ]) +); +export type EntriesArray = t.TypeOf; + +export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); +export type EntriesArrayOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts new file mode 100644 index 0000000000000..0882883f4d239 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryExists } from '.'; +import { EXISTS, FIELD, OPERATOR } from '../../constants/index.mock'; + +export const getEntryExistsMock = (): EntryExists => ({ + field: FIELD, + operator: OPERATOR, + type: EXISTS, +}); + +export const getEntryExistsExcludedMock = (): EntryExists => ({ + ...getEntryExistsMock(), + operator: 'excluded', +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts new file mode 100644 index 0000000000000..db4edb54dfc29 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryExistsMock } from './index.mock'; +import { entriesExists, EntryExists } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('entriesExists', () => { + test('it should validate an entry', () => { + const payload = getEntryExistsMock(); + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "included"', () => { + const payload = getEntryExistsMock(); + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryExistsMock(); + payload.operator = 'excluded'; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { field: string } = { + ...getEntryExistsMock(), + field: '', + }; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryExists & { + extraKey?: string; + } = getEntryExistsMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryExistsMock()); + }); + + test('it should FAIL validation when "type" is not "exists"', () => { + const payload: Omit & { type: string } = { + ...getEntryExistsMock(), + type: 'match', + }; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts new file mode 100644 index 0000000000000..79c58944ea3f5 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { operator } from '../operator'; +import { NonEmptyString } from '../../non_empty_string'; + +export const entriesExists = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ exists: null }), + }) +); +export type EntryExists = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts new file mode 100644 index 0000000000000..c4afb28f5ac54 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryList } from '.'; +import { FIELD, LIST, LIST_ID, OPERATOR, TYPE } from '../../constants/index.mock'; + +export const getEntryListMock = (): EntryList => ({ + field: FIELD, + list: { id: LIST_ID, type: TYPE }, + operator: OPERATOR, + type: LIST, +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts new file mode 100644 index 0000000000000..2be3803c356de --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryListMock } from './index.mock'; +import { entriesList, EntryList } from '.'; + +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('entriesList', () => { + test('it should validate an entry', () => { + const payload = getEntryListMock(); + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryListMock(); + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryListMock(); + payload.operator = 'excluded'; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "list" is not expected value', () => { + const payload: Omit & { list: string } = { + ...getEntryListMock(), + list: 'someListId', + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "someListId" supplied to "list"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "list.id" is empty string', () => { + const payload: Omit & { list: { id: string; type: 'ip' } } = { + ...getEntryListMock(), + list: { id: '', type: 'ip' }, + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "list,id"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "lists"', () => { + const payload: Omit & { type: 'match_any' } = { + ...getEntryListMock(), + type: 'match_any', + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryList & { + extraKey?: string; + } = getEntryListMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryListMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts new file mode 100644 index 0000000000000..f5c662a67158b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; + +import { type } from '../type'; +import { operator } from '../operator'; + +export const entriesList = t.exact( + t.type({ + field: NonEmptyString, + list: t.exact(t.type({ id: NonEmptyString, type })), + operator, + type: t.keyof({ list: null }), + }) +); +export type EntryList = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts new file mode 100644 index 0000000000000..4fdd8d915fe04 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryMatch } from '.'; +import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock'; + +export const getEntryMatchMock = (): EntryMatch => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH, + value: ENTRY_VALUE, +}); + +export const getEntryMatchExcludeMock = (): EntryMatch => ({ + ...getEntryMatchMock(), + operator: 'excluded', +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts new file mode 100644 index 0000000000000..744c74c1223df --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchMock } from './index.mock'; +import { entriesMatch, EntryMatch } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('entriesMatch', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchMock(); + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchMock(); + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryMatchMock(); + payload.operator = 'excluded'; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { field: string } = { + ...getEntryMatchMock(), + field: '', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit & { value: string[] } = { + ...getEntryMatchMock(), + value: ['some value'], + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit & { value: string } = { + ...getEntryMatchMock(), + value: '', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match"', () => { + const payload: Omit & { type: string } = { + ...getEntryMatchMock(), + type: 'match_any', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatch & { + extraKey?: string; + } = getEntryMatchMock(); + payload.extraKey = 'some value'; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts new file mode 100644 index 0000000000000..668da1a398093 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; +import { operator } from '../operator'; + +export const entriesMatch = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ match: null }), + value: NonEmptyString, + }) +); +export type EntryMatch = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts new file mode 100644 index 0000000000000..0022b00c604b0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryMatchAny } from '.'; +import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock'; + +export const getEntryMatchAnyMock = (): EntryMatchAny => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH_ANY, + value: [ENTRY_VALUE], +}); + +export const getEntryMatchAnyExcludeMock = (): EntryMatchAny => ({ + ...getEntryMatchAnyMock(), + operator: 'excluded', + value: [ENTRY_VALUE, 'some other host name'], +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts new file mode 100644 index 0000000000000..60fc4cdc26005 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchAnyMock } from './index.mock'; +import { entriesMatchAny, EntryMatchAny } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('entriesMatchAny', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "excluded"', () => { + const payload = getEntryMatchAnyMock(); + payload.operator = 'excluded'; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when field is empty string', () => { + const payload: Omit & { field: string } = { + ...getEntryMatchAnyMock(), + field: '', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is empty array', () => { + const payload: Omit & { value: string[] } = { + ...getEntryMatchAnyMock(), + value: [], + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is not string array', () => { + const payload: Omit & { value: string } = { + ...getEntryMatchAnyMock(), + value: 'some string', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match_any"', () => { + const payload: Omit & { type: string } = { + ...getEntryMatchAnyMock(), + type: 'match', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatchAny & { + extraKey?: string; + } = getEntryMatchAnyMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchAnyMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts new file mode 100644 index 0000000000000..4e7690a80f470 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { operator } from '../operator'; +import { nonEmptyOrNullableStringArray } from '../../non_empty_or_nullable_string_array'; +import { NonEmptyString } from '../../non_empty_string'; + +export const entriesMatchAny = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ match_any: null }), + value: nonEmptyOrNullableStringArray, + }) +); +export type EntryMatchAny = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts new file mode 100644 index 0000000000000..9810fe5e9875b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryMatchWildcard } from '.'; +import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../constants/index.mock'; + +export const getEntryMatchWildcardMock = (): EntryMatchWildcard => ({ + field: FIELD, + operator: OPERATOR, + type: WILDCARD, + value: ENTRY_VALUE, +}); + +export const getEntryMatchWildcardExcludeMock = (): EntryMatchWildcard => ({ + ...getEntryMatchWildcardMock(), + operator: 'excluded', +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts new file mode 100644 index 0000000000000..d9170dd60ab40 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchWildcardMock } from './index.mock'; +import { entriesMatchWildcard, EntryMatchWildcard } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('entriesMatchWildcard', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchWildcardMock(); + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchWildcardMock(); + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryMatchWildcardMock(); + payload.operator = 'excluded'; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { field: string } = { + ...getEntryMatchWildcardMock(), + field: '', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit & { value: string[] } = { + ...getEntryMatchWildcardMock(), + value: ['some value'], + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit & { value: string } = { + ...getEntryMatchWildcardMock(), + value: '', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "wildcard"', () => { + const payload: Omit & { type: string } = { + ...getEntryMatchWildcardMock(), + type: 'match', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatchWildcard & { + extraKey?: string; + } = getEntryMatchWildcardMock(); + payload.extraKey = 'some value'; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchWildcardMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts new file mode 100644 index 0000000000000..100b0c665d91b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; +import { operator } from '../operator'; + +export const entriesMatchWildcard = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ wildcard: null }), + value: NonEmptyString, + }) +); +export type EntryMatchWildcard = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts new file mode 100644 index 0000000000000..acde4443cccb7 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EntryNested } from '.'; +import { NESTED, NESTED_FIELD } from '../../constants/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryMatchExcludeMock, getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyExcludeMock, getEntryMatchAnyMock } from '../entry_match_any/index.mock'; + +export const getEntryNestedMock = (): EntryNested => ({ + entries: [getEntryMatchMock(), getEntryMatchAnyMock()], + field: NESTED_FIELD, + type: NESTED, +}); + +export const getEntryNestedExcludeMock = (): EntryNested => ({ + ...getEntryNestedMock(), + entries: [getEntryMatchExcludeMock(), getEntryMatchAnyExcludeMock()], +}); + +export const getEntryNestedMixedEntries = (): EntryNested => ({ + ...getEntryNestedMock(), + entries: [getEntryMatchMock(), getEntryMatchAnyExcludeMock(), getEntryExistsMock()], +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts new file mode 100644 index 0000000000000..b6bbc4dbef4a3 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryNestedMock } from './index.mock'; +import { entriesNested, EntryNested } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; + +describe('entriesNested', () => { + test('it should validate a nested entry', () => { + const payload = getEntryNestedMock(); + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "type" is not "nested"', () => { + const payload: Omit & { type: 'match' } = { + ...getEntryNestedMock(), + type: 'match', + }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit & { + field: string; + } = { ...getEntryNestedMock(), field: '' }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is not a string', () => { + const payload: Omit & { + field: number; + } = { ...getEntryNestedMock(), field: 1 }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "entries" is not a an array', () => { + const payload: Omit & { + entries: string; + } = { ...getEntryNestedMock(), entries: 'im a string' }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "im a string" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate when "entries" contains an entry item that is type "match"', () => { + const payload = { ...getEntryNestedMock(), entries: [getEntryMatchAnyMock()] }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host name'], + }, + ], + field: 'parent.field', + type: 'nested', + }); + }); + + test('it should validate when "entries" contains an entry item that is type "exists"', () => { + const payload = { ...getEntryNestedMock(), entries: [getEntryExistsMock()] }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'exists', + }, + ], + field: 'parent.field', + type: 'nested', + }); + }); + + test('it should strip out extra keys', () => { + const payload: EntryNested & { + extraKey?: string; + } = getEntryNestedMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryNestedMock()); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts new file mode 100644 index 0000000000000..ff224dd836a19 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; +import { nonEmptyNestedEntriesArray } from '../non_empty_nested_entries_array'; + +export const entriesNested = t.exact( + t.type({ + entries: nonEmptyNestedEntriesArray, + field: NonEmptyString, + type: t.keyof({ nested: null }), + }) +); +export type EntryNested = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts new file mode 100644 index 0000000000000..a68dc2fc76a96 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const exceptionListType = t.keyof({ + detection: null, + endpoint: null, + endpoint_events: null, +}); +export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); +export type ExceptionListType = t.TypeOf; +export type ExceptionListTypeOrUndefined = t.TypeOf; +export enum ExceptionListTypeEnum { + DETECTION = 'detection', + ENDPOINT = 'endpoint', + ENDPOINT_EVENTS = 'endpoint_events', +} diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts new file mode 100644 index 0000000000000..d527f01825f4b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const exceptionListItemType = t.keyof({ simple: null }); +export const exceptionListItemTypeOrUndefined = t.union([exceptionListItemType, t.undefined]); +export type ExceptionListItemType = t.TypeOf; +export type ExceptionListItemTypeOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts new file mode 100644 index 0000000000000..652395fa651ea --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './comment'; +export * from './create_comment'; +export * from './default_comments_array'; +export * from './default_comments_array'; +export * from './default_namespace'; +export * from './default_namespace_array'; +export * from './default_update_comments_array'; +export * from './entries'; +export * from './entries_exist'; +export * from './entries_list'; +export * from './entry_match'; +export * from './entry_match_any'; +export * from './entry_match_wildcard'; +export * from './entry_nested'; +export * from './exception_list'; +export * from './exception_list_item_type'; +export * from './item_id'; +export * from './lists'; +export * from './lists_default_array'; +export * from './non_empty_entries_array'; +export * from './non_empty_nested_entries_array'; +export * from './operator'; +export * from './os_type'; +export * from './type'; +export * from './update_comment'; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts new file mode 100644 index 0000000000000..171db8fd60fd1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; + +export const item_id = NonEmptyString; +export type ItemId = t.TypeOf; +export const itemIdOrUndefined = t.union([item_id, t.undefined]); +export type ItemIdOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts new file mode 100644 index 0000000000000..c6f54b57d937b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { List, ListArray } from '.'; +import { ENDPOINT_LIST_ID } from '../../constants'; + +export const getListMock = (): List => ({ + id: 'some_uuid', + list_id: 'list_id_single', + namespace_type: 'single', + type: 'detection', +}); + +export const getEndpointListMock = (): List => ({ + id: ENDPOINT_LIST_ID, + list_id: ENDPOINT_LIST_ID, + namespace_type: 'agnostic', + type: 'endpoint', +}); + +export const getListArrayMock = (): ListArray => [getListMock(), getEndpointListMock()]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts new file mode 100644 index 0000000000000..77d5e72ef8bc8 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointListMock, getListArrayMock, getListMock } from './index.mock'; +import { List, list, ListArray, listArray, ListArrayOrUndefined, listArrayOrUndefined } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('Lists', () => { + describe('list', () => { + test('it should validate a list', () => { + const payload = getListMock(); + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a list with "namespace_type" of "agnostic"', () => { + const payload = getEndpointListMock(); + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a list without an "id"', () => { + const payload = getListMock(); + // @ts-expect-error + delete payload.id; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a list without "namespace_type"', () => { + const payload = getListMock(); + // @ts-expect-error + delete payload.namespace_type; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "namespace_type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: List & { + extraKey?: string; + } = getListMock(); + payload.extraKey = 'some value'; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getListMock()); + }); + }); + + describe('listArray', () => { + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = listArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate when unexpected type found in array', () => { + const payload = ([1] as unknown) as ListArray; + const decoded = listArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('listArrayOrUndefined', () => { + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when undefined', () => { + const payload = undefined; + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an item that is not of type "list" in array', () => { + const payload = ([1] as unknown) as ListArrayOrUndefined; + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}> | undefined)"', + 'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_events", namespace_type: "agnostic" | "single" |}> | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts new file mode 100644 index 0000000000000..1bd1806564856 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { exceptionListType } from '../exception_list'; +import { namespaceType } from '../default_namespace'; +import { NonEmptyString } from '../../non_empty_string'; + +export const list = t.exact( + t.type({ + id: NonEmptyString, + list_id: NonEmptyString, + type: exceptionListType, + namespace_type: namespaceType, + }) +); + +export type List = t.TypeOf; +export const listArray = t.array(list); +export type ListArray = t.TypeOf; +export const listArrayOrUndefined = t.union([listArray, t.undefined]); +export type ListArrayOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts new file mode 100644 index 0000000000000..03d16d8e1b5ca --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultListArray } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getListArrayMock } from '../lists/index.mock'; + +describe('lists_default_array', () => { + test('it should return a default array when null', () => { + const payload = null; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should return a default array when undefined', () => { + const payload = undefined; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array of non accepted types', () => { + // Terrible casting for purpose of tests + const payload = [1] as unknown; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "DefaultListArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts new file mode 100644 index 0000000000000..ac0174db1eed1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { list, ListArray } from '../lists'; + +/** + * Types the DefaultListArray as: + * - If null or undefined, then a default array of type list will be set + */ +export const DefaultListArray = new t.Type( + 'DefaultListArray', + t.array(list).is, + (input, context): Either => + input == null ? t.success([]) : t.array(list).validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts new file mode 100644 index 0000000000000..11e6e54b344a9 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EntriesArray } from '../entries'; +import { nonEmptyEntriesArray } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { + getEntriesArrayMock, + getListAndNonListEntriesArrayMock, + getListEntriesArrayMock, +} from '../entries/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +describe('non_empty_entries_array', () => { + test('it should FAIL validation when given an empty array', () => { + const payload: EntriesArray = []; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "undefined"', () => { + const payload = undefined; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "null"', () => { + const payload = null; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of "match" entries', () => { + const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "match_any" entries', () => { + const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "exists" entries', () => { + const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "list" entries', () => { + const payload: EntriesArray = [...getListEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "nested" entries', () => { + const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of entries', () => { + const payload: EntriesArray = [...getEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of entries of value list and non-value list entries', () => { + const payload: EntriesArray = [...getListAndNonListEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Cannot have entry of type list and other']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given an array of non entries', () => { + const payload = [1]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts new file mode 100644 index 0000000000000..1b1ea8806471d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { entriesArray, EntriesArray } from '../entries'; +import { entriesList } from '../entries_list'; + +/** + * Types the nonEmptyEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEntriesArray = new t.Type( + 'NonEmptyEntriesArray', + entriesArray.is, + (input, context): Either => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + if ( + Array.isArray(input) && + input.some((entry) => entriesList.is(entry)) && + input.some((entry) => !entriesList.is(entry)) + ) { + // fail when an exception item contains both a value list entry and a non-value list entry + return t.failure(input, context, 'Cannot have entry of type list and other'); + } + return entriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEntriesArray = t.OutputOf; +export type NonEmptyEntriesArrayDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts new file mode 100644 index 0000000000000..95b74a6d4fe43 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EntriesArray } from '../entries'; +import { nonEmptyNestedEntriesArray } from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +describe('non_empty_nested_entries_array', () => { + test('it should FAIL validation when given an empty array', () => { + const payload: EntriesArray = []; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "undefined"', () => { + const payload = undefined; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "null"', () => { + const payload = null; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of "match" entries', () => { + const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "match_any" entries', () => { + const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "exists" entries', () => { + const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of "nested" entries', () => { + const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "operator"', + 'Invalid value "nested" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of entries', () => { + const payload: EntriesArray = [ + getEntryExistsMock(), + getEntryMatchAnyMock(), + getEntryMatchMock(), + ]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of non entries', () => { + const payload = [1]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts new file mode 100644 index 0000000000000..2f18697d36d81 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { entriesMatch } from '../entry_match'; +import { entriesMatchAny } from '../entry_match_any'; +import { entriesExists } from '../entries_exist'; + +export const nestedEntryItem = t.union([entriesMatch, entriesMatchAny, entriesExists]); +export const nestedEntriesArray = t.array(nestedEntryItem); +export type NestedEntriesArray = t.TypeOf; + +/** + * Types the nonEmptyNestedEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyNestedEntriesArray = new t.Type< + NestedEntriesArray, + NestedEntriesArray, + unknown +>( + 'NonEmptyNestedEntriesArray', + nestedEntriesArray.is, + (input, context): Either => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return nestedEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyNestedEntriesArray = t.OutputOf; +export type NonEmptyNestedEntriesArrayDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/operator/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/operator/index.ts new file mode 100644 index 0000000000000..7fe2b6e4d8ba7 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/operator/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const operator = t.keyof({ excluded: null, included: null }); +export type Operator = t.TypeOf; +export enum OperatorEnum { + INCLUDED = 'included', + EXCLUDED = 'excluded', +} + +export enum OperatorTypeEnum { + NESTED = 'nested', + MATCH = 'match', + MATCH_ANY = 'match_any', + WILDCARD = 'wildcard', + EXISTS = 'exists', + LIST = 'list', +} diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts new file mode 100644 index 0000000000000..5ff60e05817d5 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { DefaultArray } from '../../default_array'; + +export const osType = t.keyof({ + linux: null, + macos: null, + windows: null, +}); +export type OsType = t.TypeOf; + +export const osTypeArray = DefaultArray(osType); +export type OsTypeArray = t.TypeOf; + +export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); +export type OsTypeArrayOrUndefined = t.OutputOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts new file mode 100644 index 0000000000000..0eebf2eeaace1 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +/** + * Types of all the regular single value list items but not exception list + * or exception list types. Those types are in the list_types folder. + */ +export const type = t.keyof({ + binary: null, + boolean: null, + byte: null, + date: null, + date_nanos: null, + date_range: null, + double: null, + double_range: null, + float: null, + float_range: null, + geo_point: null, + geo_shape: null, + half_float: null, + integer: null, + integer_range: null, + ip: null, + ip_range: null, + keyword: null, + long: null, + long_range: null, + shape: null, + short: null, + text: null, +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts new file mode 100644 index 0000000000000..3b5cb256b28bf --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UpdateComment, UpdateCommentsArray } from '.'; +import { ID } from '../../constants/index.mock'; + +export const getUpdateCommentMock = (): UpdateComment => ({ + comment: 'some comment', + id: ID, +}); + +export const getUpdateCommentsArrayMock = (): UpdateCommentsArray => [ + getUpdateCommentMock(), + getUpdateCommentMock(), +]; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts new file mode 100644 index 0000000000000..a6fc285f05465 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getUpdateCommentMock, getUpdateCommentsArrayMock } from './index.mock'; +import { + UpdateComment, + updateComment, + UpdateCommentsArray, + updateCommentsArray, + UpdateCommentsArrayOrUndefined, + updateCommentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '../../test_utils'; + +describe('CommentsUpdate', () => { + describe('updateComment', () => { + test('it should pass validation when supplied typical comment update', () => { + const payload = getUpdateCommentMock(); + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an undefined for "comment"', () => { + const payload = getUpdateCommentMock(); + // @ts-expect-error + delete payload.comment; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an empty string for "comment"', () => { + const payload = { ...getUpdateCommentMock(), comment: '' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "id"', () => { + const payload = getUpdateCommentMock(); + delete payload.id; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an empty string for "id"', () => { + const payload = { ...getUpdateCommentMock(), id: '' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra key passed in', () => { + const payload: UpdateComment & { + extraKey?: string; + } = { ...getUpdateCommentMock(), extraKey: 'some new value' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getUpdateCommentMock()); + }); + }); + + describe('updateCommentsArray', () => { + test('it should pass validation when supplied an array of comments', () => { + const payload = getUpdateCommentsArrayMock(); + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when undefined', () => { + const payload = undefined; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when array includes non comments types', () => { + const payload = ([1] as unknown) as UpdateCommentsArray; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('updateCommentsArrayOrUndefined', () => { + test('it should pass validation when supplied an array of comments', () => { + const payload = getUpdateCommentsArrayMock(); + const decoded = updateCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied when undefined', () => { + const payload = undefined; + const decoded = updateCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when array includes non comments types', () => { + const payload = ([1] as unknown) as UpdateCommentsArrayOrUndefined; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts new file mode 100644 index 0000000000000..496ff07c5616f --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '../../non_empty_string'; +import { id } from '../../id'; + +export const updateComment = t.intersection([ + t.exact( + t.type({ + comment: NonEmptyString, + }) + ), + t.exact( + t.partial({ + id, + }) + ), +]); + +export type UpdateComment = t.TypeOf; +export const updateCommentsArray = t.array(updateComment); +export type UpdateCommentsArray = t.TypeOf; +export const updateCommentsArrayOrUndefined = t.union([updateCommentsArray, t.undefined]); +export type UpdateCommentsArrayOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts new file mode 100644 index 0000000000000..4c68cb01cf00f --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero'; + +export const max_signals = PositiveIntegerGreaterThanZero; +export type MaxSignals = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts new file mode 100644 index 0000000000000..93c38facd8f4e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const meta = t.object; +export type Meta = t.TypeOf; +export const metaOrUndefined = t.union([meta, t.undefined]); +export type MetaOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/name/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/name/index.ts new file mode 100644 index 0000000000000..585d903c861c0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/name/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const name = t.string; +export type Name = t.TypeOf; +export const nameOrUndefined = t.union([name, t.undefined]); +export type NameOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts new file mode 100644 index 0000000000000..0ea7eb5539ba9 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { NonEmptyArray } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +const testSchema = t.keyof({ + valid: true, + also_valid: true, +}); +type TestSchema = t.TypeOf; + +const nonEmptyArraySchema = NonEmptyArray(testSchema, 'TestSchemaArray'); + +describe('non empty array', () => { + test('it should generate the correct name for non empty array', () => { + const newTestSchema = NonEmptyArray(testSchema); + expect(newTestSchema.name).toEqual('NonEmptyArray<"valid" | "also_valid">'); + }); + + test('it should use a supplied name override', () => { + const newTestSchema = NonEmptyArray(testSchema, 'someName'); + expect(newTestSchema.name).toEqual('someName'); + }); + + test('it should NOT validate an empty array', () => { + const payload: string[] = []; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "TestSchemaArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of testSchema', () => { + const payload: TestSchema[] = ['valid']; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of valid testSchema strings', () => { + const payload: TestSchema[] = ['valid', 'also_valid']; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['valid', 123]; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "123" supplied to "TestSchemaArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an array with an invalid string', () => { + const payload = ['valid', 'invalid']; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "TestSchemaArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a null value', () => { + const payload = null; + const decoded = nonEmptyArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "TestSchemaArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts new file mode 100644 index 0000000000000..5bdcbcc049683 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +export const NonEmptyArray = ( + codec: C, + name: string = `NonEmptyArray<${codec.name}>` +) => { + const arrType = t.array(codec); + type ArrType = t.TypeOf; + return new t.Type( + name, + arrType.is, + (input, context): Either => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return arrType.validate(input, context); + } + }, + t.identity + ); +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts new file mode 100644 index 0000000000000..fb2e91862d91e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { nonEmptyOrNullableStringArray } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('nonEmptyOrNullableStringArray', () => { + test('it should FAIL validation when given an empty array', () => { + const payload: string[] = []; + const decoded = nonEmptyOrNullableStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyOrNullableStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "undefined"', () => { + const payload = undefined; + const decoded = nonEmptyOrNullableStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "NonEmptyOrNullableStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "null"', () => { + const payload = null; + const decoded = nonEmptyOrNullableStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "NonEmptyOrNullableStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given an array of with an empty string', () => { + const payload: string[] = ['im good', '']; + const decoded = nonEmptyOrNullableStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["im good",""]" supplied to "NonEmptyOrNullableStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given an array of non strings', () => { + const payload = [1]; + const decoded = nonEmptyOrNullableStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[1]" supplied to "NonEmptyOrNullableStringArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts new file mode 100644 index 0000000000000..d6de08789b1df --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the nonEmptyOrNullableStringArray as: + * - An array of non empty strings of length 1 or greater + * - This differs from NonEmptyStringArray in that both input and output are type array + * + */ +export const nonEmptyOrNullableStringArray = new t.Type( + 'NonEmptyOrNullableStringArray', + t.array(t.string).is, + (input, context): Either => { + const emptyValueFound = Array.isArray(input) && input.some((value) => value === ''); + const nonStringValueFound = + Array.isArray(input) && input.some((value) => typeof value !== 'string'); + + if (Array.isArray(input) && (emptyValueFound || nonStringValueFound || input.length === 0)) { + return t.failure(input, context); + } else { + return t.array(t.string).validate(input, context); + } + }, + t.identity +); + +export type NonEmptyOrNullableStringArray = t.OutputOf; +export type NonEmptyOrNullableStringArrayDecoded = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts new file mode 100644 index 0000000000000..15c8ced8c915f --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../test_utils'; +import { NonEmptyString } from '.'; + +describe('non_empty_string', () => { + test('it should validate a regular string', () => { + const payload = '1'; + const decoded = NonEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = NonEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "NonEmptyString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = NonEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "" supplied to "NonEmptyString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate empty spaces', () => { + const payload = ' '; + const decoded = NonEmptyString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value " " supplied to "NonEmptyString"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts new file mode 100644 index 0000000000000..88f251af0013e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the NonEmptyString as: + * - A string that is not empty + */ +export const NonEmptyString = new t.Type( + 'NonEmptyString', + t.string.is, + (input, context): Either => { + if (typeof input === 'string' && input.trim() !== '') { + return t.success(input); + } else { + return t.failure(input, context); + } + }, + t.identity +); + +export type NonEmptyStringC = typeof NonEmptyString; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.ts new file mode 100644 index 0000000000000..6c7eb0ae33ab9 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +import { NonEmptyArray } from '../non_empty_array'; + +export const machine_learning_job_id_normalized = NonEmptyArray(t.string); +export type MachineLearningJobIdNormalized = t.TypeOf; + +export const machineLearningJobIdNormalizedOrUndefined = t.union([ + machine_learning_job_id_normalized, + t.undefined, +]); +export type MachineLearningJobIdNormalizedOrUndefined = t.TypeOf< + typeof machineLearningJobIdNormalizedOrUndefined +>; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts new file mode 100644 index 0000000000000..7f06ec2153a50 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { OnlyFalseAllowed } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('only_false_allowed', () => { + test('it should validate a boolean false as false', () => { + const payload = false; + const decoded = OnlyFalseAllowed.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a boolean true', () => { + const payload = true; + const decoded = OnlyFalseAllowed.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "true" supplied to "DefaultBooleanTrue"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = OnlyFalseAllowed.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultBooleanTrue"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default false', () => { + const payload = null; + const decoded = OnlyFalseAllowed.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(false); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts new file mode 100644 index 0000000000000..7a11e1e1c1c31 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the OnlyFalseAllowed as: + * - If null or undefined, then a default false will be set + * - If true is sent in then this will return an error + * - If false is sent in then this will allow it only false + */ +export const OnlyFalseAllowed = new t.Type( + 'DefaultBooleanTrue', + t.boolean.is, + (input, context): Either => { + if (input == null) { + return t.success(false); + } else { + if (typeof input === 'boolean' && input === false) { + return t.success(false); + } else { + return t.failure(input, context); + } + } + }, + t.identity +); diff --git a/src/plugins/discover/public/application/angular/context/query/state.js b/packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts similarity index 59% rename from src/plugins/discover/public/application/angular/context/query/state.js rename to packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts index c4f3fbb80cce4..516cb05c2d969 100644 --- a/src/plugins/discover/public/application/angular/context/query/state.js +++ b/packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts @@ -6,12 +6,14 @@ * Side Public License, v 1. */ -import { LOADING_STATUS } from './index'; +import * as t from 'io-ts'; -export function createInitialLoadingStatusState() { - return { - anchor: LOADING_STATUS.UNINITIALIZED, - predecessors: LOADING_STATUS.UNINITIALIZED, - successors: LOADING_STATUS.UNINITIALIZED, - }; +export const operatorIncluded = t.keyof({ included: null }); + +export const operator = t.keyof({ + equals: null, +}); +export type Operator = t.TypeOf; +export enum OperatorEnum { + EQUALS = 'equals', } diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts new file mode 100644 index 0000000000000..d8aa9d2939589 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import moment from 'moment'; +import dateMath from '@elastic/datemath'; + +/** + * TODO: Move this to kbn-securitysolution-utils + * @deprecated Use the parseScheduleDates from the kbn-securitysolution-utils. + */ +export const parseScheduleDates = (time: string): moment.Moment | null => { + const isValidDateString = !isNaN(Date.parse(time)); + const isValidInput = isValidDateString || time.trim().startsWith('now'); + const formattedDate = isValidDateString + ? moment(time) + : isValidInput + ? dateMath.parse(time) + : null; + + return formattedDate ?? null; +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts new file mode 100644 index 0000000000000..c6c841b746089 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { PositiveInteger } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('positive_integer_greater_than_zero', () => { + test('it should validate a positive number', () => { + const payload = 1; + const decoded = PositiveInteger.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a zero', () => { + const payload = 0; + const decoded = PositiveInteger.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a negative number', () => { + const payload = -1; + const decoded = PositiveInteger.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveInteger"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a string', () => { + const payload = 'some string'; + const decoded = PositiveInteger.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "PositiveInteger"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts new file mode 100644 index 0000000000000..6cfd458766b69 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the positive integer are: + * - Natural Number (positive integer and not a float), + * - zero or greater + */ +export const PositiveInteger = new t.Type( + 'PositiveInteger', + t.number.is, + (input, context): Either => { + return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0 + ? t.success(input) + : t.failure(input, context); + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts new file mode 100644 index 0000000000000..4655207a6448e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { PositiveIntegerGreaterThanZero } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('positive_integer_greater_than_zero', () => { + test('it should validate a positive number', () => { + const payload = 1; + const decoded = PositiveIntegerGreaterThanZero.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a zero', () => { + const payload = 0; + const decoded = PositiveIntegerGreaterThanZero.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a negative number', () => { + const payload = -1; + const decoded = PositiveIntegerGreaterThanZero.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a string', () => { + const payload = 'some string'; + const decoded = PositiveIntegerGreaterThanZero.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts new file mode 100644 index 0000000000000..b94bd0af5b485 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the positive integer greater than zero is: + * - Natural Number (positive integer and not a float), + * - 1 or greater + */ +export const PositiveIntegerGreaterThanZero = new t.Type( + 'PositiveIntegerGreaterThanZero', + t.number.is, + (input, context): Either => { + return typeof input === 'number' && Number.isSafeInteger(input) && input >= 1 + ? t.success(input) + : t.failure(input, context); + }, + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts new file mode 100644 index 0000000000000..41754a7ce0606 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultStringArray } from '../default_string_array'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('default_string_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of strings', () => { + const payload = ['value 1', 'value 2']; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['value 1', 5]; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultStringArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultStringArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts new file mode 100644 index 0000000000000..9e7708e8dc5f6 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the ReferencesDefaultArray as: + * - If null or undefined, then a default array will be set + */ +export const ReferencesDefaultArray = new t.Type( + 'referencesWithDefaultArray', + t.array(t.string).is, + (input, context): Either => + input == null ? t.success([]) : t.array(t.string).validate(input, context), + t.identity +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts new file mode 100644 index 0000000000000..bca8b92134928 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../test_utils'; +import { RiskScore } from '.'; + +describe('risk_score', () => { + test('it should validate a positive number', () => { + const payload = 1; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a zero', () => { + const payload = 0; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a negative number', () => { + const payload = -1; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "RiskScore"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a string', () => { + const payload = 'some string'; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "RiskScore"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a risk score greater than 100', () => { + const payload = 101; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "101" supplied to "RiskScore"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts new file mode 100644 index 0000000000000..0aca7dd70ba1d --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the risk score as: + * - Natural Number (positive integer and not a float), + * - Between the values [0 and 100] inclusive. + */ +export const RiskScore = new t.Type( + 'RiskScore', + t.number.is, + (input, context): Either => { + return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0 && input <= 100 + ? t.success(input) + : t.failure(input, context); + }, + t.identity +); + +export type RiskScoreC = typeof RiskScore; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts new file mode 100644 index 0000000000000..1d7ca20e80b3b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { RiskScore } from '../risk_score'; + +import { operator } from '../operator'; + +export const riskScoreOrUndefined = t.union([RiskScore, t.undefined]); +export type RiskScoreOrUndefined = t.TypeOf; + +export const risk_score_mapping_field = t.string; +export const risk_score_mapping_value = t.string; +export const risk_score_mapping_item = t.exact( + t.type({ + field: risk_score_mapping_field, + value: risk_score_mapping_value, + operator, + risk_score: riskScoreOrUndefined, + }) +); + +export const risk_score_mapping = t.array(risk_score_mapping_item); +export type RiskScoreMapping = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts new file mode 100644 index 0000000000000..4d1d39bcd6a97 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * Don't use this type, it's simply a helper type for {@link SavedObjectAttribute} + * + * @public + */ +export type SavedObjectAttributeSingle = + | string + | number + | boolean + | null + | undefined + | SavedObjectAttributes; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * Type definition for a Saved Object attribute value + * + * @public + */ +export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttributeSingle[]; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * The data for a Saved Object is stored as an object in the `attributes` + * property. + * + * @public + */ +export interface SavedObjectAttributes { + [key: string]: SavedObjectAttribute; +} + +export const saved_object_attribute_single: t.Type = t.recursion( + 'saved_object_attribute_single', + () => t.union([t.string, t.number, t.boolean, t.null, t.undefined, saved_object_attributes]) +); +export const saved_object_attribute: t.Type = t.recursion( + 'saved_object_attribute', + () => t.union([saved_object_attribute_single, t.array(saved_object_attribute_single)]) +); +export const saved_object_attributes: t.Type = t.recursion( + 'saved_object_attributes', + () => t.record(t.string, saved_object_attribute) +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts new file mode 100644 index 0000000000000..4caafa6b6ecb2 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const severity = t.keyof({ low: null, medium: null, high: null, critical: null }); +export type Severity = t.TypeOf; + +export const severityOrUndefined = t.union([severity, t.undefined]); +export type SeverityOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts new file mode 100644 index 0000000000000..9e7ee7d2831cd --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +import { operator } from '../operator'; +import { severity } from '../severity'; + +export const severity_mapping_field = t.string; +export const severity_mapping_value = t.string; +export const severity_mapping_item = t.exact( + t.type({ + field: severity_mapping_field, + operator, + value: severity_mapping_value, + severity, + }) +); +export type SeverityMappingItem = t.TypeOf; + +export const severity_mapping = t.array(severity_mapping_item); +export type SeverityMapping = t.TypeOf; + +export const severityMappingOrUndefined = t.union([severity_mapping, t.undefined]); +export type SeverityMappingOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts new file mode 100644 index 0000000000000..c1db6dd4647a5 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either, either } from 'fp-ts/lib/Either'; + +export type StringToPositiveNumberC = t.Type; + +/** + * Types the StrongToPositiveNumber as: + * - If a string this converts the string into a number + * - Ensures it is a number (and not NaN) + * - Ensures it is positive number + */ +export const StringToPositiveNumber: StringToPositiveNumberC = new t.Type( + 'StringToPositiveNumber', + t.number.is, + (input, context): Either => { + return either.chain( + t.string.validate(input, context), + (numberAsString): Either => { + const stringAsNumber = +numberAsString; + if (numberAsString.trim().length === 0 || isNaN(stringAsNumber) || stringAsNumber <= 0) { + return t.failure(input, context); + } else { + return t.success(stringAsNumber); + } + } + ); + }, + String +); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts new file mode 100644 index 0000000000000..48bcca0551352 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { DefaultStringArray } from '../default_string_array'; + +export const tags = DefaultStringArray; +export type Tags = t.TypeOf; +export const tagsOrUndefined = t.union([tags, t.undefined]); +export type TagsOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts new file mode 100644 index 0000000000000..ec57671791c6b --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/test_utils/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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { formatErrors } from '../format_errors'; + +/* + * FUNFACT: These don't have tests as these are used within test utilities. However, I could see + * someone adding tests. If one day we see these escape tests, then they should have unit tests. + */ + +interface Message { + errors: t.Errors; + schema: T | {}; +} + +const onLeft = (errors: t.Errors): Message => { + return { schema: {}, errors }; +}; + +const onRight = (schema: T): Message => { + return { + schema, + errors: [], + }; +}; + +export const foldLeftRight = fold(onLeft, onRight); + +/** + * Convenience utility to keep the error message handling within tests to be + * very concise. + * @param validation The validation to get the errors from + */ +export const getPaths = (validation: t.Validation): string[] => { + return pipe( + validation, + fold( + (errors) => formatErrors(errors), + () => ['no errors'] + ) + ); +}; + +/** + * Convenience utility to remove text appended to links by EUI + */ +export const removeExternalLinkText = (str: string) => + str.replace(/\(opens in a new tab or window\)/g, ''); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts new file mode 100644 index 0000000000000..0e4022e3ec26e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { threat_tactic } from '../threat_tactic'; +import { threat_techniques } from '../threat_technique'; + +export const threat_framework = t.string; + +export const threat = t.intersection([ + t.exact( + t.type({ + framework: threat_framework, + tactic: threat_tactic, + }) + ), + t.exact( + t.partial({ + technique: threat_techniques, + }) + ), +]); + +export type Threat = t.TypeOf; + +export const threats = t.array(threat); +export type Threats = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts new file mode 100644 index 0000000000000..7f754fb2d87de --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { + concurrent_searches, + items_per_search, + ThreatMapping, + threatMappingEntries, + ThreatMappingEntries, + threat_mapping, +} from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; +import { exactCheck } from '../exact_check'; + +describe('threat_mapping', () => { + describe('threatMappingEntries', () => { + test('it should validate an entry', () => { + const payload: ThreatMappingEntries = [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation with an extra entry item', () => { + const payload: ThreatMappingEntries & Array<{ extra: string }> = [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + extra: 'blah', + }, + ]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation with a non string', () => { + const payload = ([ + { + field: 5, + type: 'mapping', + value: 'field.one', + }, + ] as unknown) as ThreatMappingEntries[]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation with a wrong type', () => { + const payload = ([ + { + field: 'field.one', + type: 'invalid', + value: 'field.one', + }, + ] as unknown) as ThreatMappingEntries[]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('threat_mapping', () => { + test('it should validate a threat mapping', () => { + const payload: ThreatMapping = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ], + }, + ]; + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); + + test('it should fail validate with an extra key', () => { + const payload: ThreatMapping & Array<{ extra: string }> = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ], + extra: 'invalid', + }, + ]; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with an extra inner entry', () => { + const payload: ThreatMapping & Array<{ entries: Array<{ extra: string }> }> = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + extra: 'blah', + }, + ], + }, + ]; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with an extra inner entry with the wrong data type', () => { + const payload = ([ + { + entries: [ + { + field: 5, + type: 'mapping', + value: 'field.one', + }, + ], + }, + ] as unknown) as ThreatMapping; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "entries,field"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with empty array', () => { + const payload: string[] = []; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when concurrent_searches is < 0', () => { + const payload = -1; + const decoded = concurrent_searches.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when concurrent_searches is 0', () => { + const payload = 0; + const decoded = concurrent_searches.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when items_per_search is 0', () => { + const payload = 0; + const decoded = items_per_search.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when items_per_search is < 0', () => { + const payload = -1; + const decoded = items_per_search.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts new file mode 100644 index 0000000000000..4fc64fe1e0982 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { language } from '../language'; +import { NonEmptyArray } from '../non_empty_array'; +import { NonEmptyString } from '../non_empty_string'; +import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero'; + +export const threat_query = t.string; +export type ThreatQuery = t.TypeOf; +export const threatQueryOrUndefined = t.union([threat_query, t.undefined]); +export type ThreatQueryOrUndefined = t.TypeOf; + +export const threat_indicator_path = t.string; +export type ThreatIndicatorPath = t.TypeOf; +export const threatIndicatorPathOrUndefined = t.union([threat_indicator_path, t.undefined]); +export type ThreatIndicatorPathOrUndefined = t.TypeOf; + +export const threat_filters = t.array(t.unknown); // Filters are not easily type-able yet +export type ThreatFilters = t.TypeOf; +export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]); +export type ThreatFiltersOrUndefined = t.TypeOf; + +export const threatMapEntry = t.exact( + t.type({ + field: NonEmptyString, + type: t.keyof({ mapping: null }), + value: NonEmptyString, + }) +); + +export type ThreatMapEntry = t.TypeOf; + +export const threatMappingEntries = t.array(threatMapEntry); +export type ThreatMappingEntries = t.TypeOf; + +export const threatMap = t.exact( + t.type({ + entries: threatMappingEntries, + }) +); +export type ThreatMap = t.TypeOf; + +export const threat_mapping = NonEmptyArray(threatMap, 'NonEmptyArray'); +export type ThreatMapping = t.TypeOf; + +export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]); +export type ThreatMappingOrUndefined = t.TypeOf; + +export const threat_index = t.array(t.string); +export type ThreatIndex = t.TypeOf; +export const threatIndexOrUndefined = t.union([threat_index, t.undefined]); +export type ThreatIndexOrUndefined = t.TypeOf; + +export const threat_language = t.union([language, t.undefined]); +export type ThreatLanguage = t.TypeOf; +export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]); +export type ThreatLanguageOrUndefined = t.TypeOf; + +export const concurrent_searches = PositiveIntegerGreaterThanZero; +export type ConcurrentSearches = t.TypeOf; +export const concurrentSearchesOrUndefined = t.union([concurrent_searches, t.undefined]); +export type ConcurrentSearchesOrUndefined = t.TypeOf; + +export const items_per_search = PositiveIntegerGreaterThanZero; +export type ItemsPerSearch = t.TypeOf; +export const itemsPerSearchOrUndefined = t.union([items_per_search, t.undefined]); +export type ItemsPerSearchOrUndefined = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts new file mode 100644 index 0000000000000..8d64f53cb1623 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const threat_subtechnique_id = t.string; +export const threat_subtechnique_name = t.string; +export const threat_subtechnique_reference = t.string; + +export const threat_subtechnique = t.type({ + id: threat_subtechnique_id, + name: threat_subtechnique_name, + reference: threat_subtechnique_reference, +}); + +export const threat_subtechniques = t.array(threat_subtechnique); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts new file mode 100644 index 0000000000000..151c138c7e0da --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const threat_tactic_id = t.string; +export const threat_tactic_name = t.string; +export const threat_tactic_reference = t.string; + +export const threat_tactic = t.type({ + id: threat_tactic_id, + name: threat_tactic_name, + reference: threat_tactic_reference, +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts new file mode 100644 index 0000000000000..ed2e771e1e118 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { threat_subtechniques } from '../threat_subtechnique'; + +export const threat_technique_id = t.string; +export const threat_technique_name = t.string; +export const threat_technique_reference = t.string; + +export const threat_technique = t.intersection([ + t.exact( + t.type({ + id: threat_technique_id, + name: threat_technique_name, + reference: threat_technique_reference, + }) + ), + t.exact( + t.partial({ + subtechnique: threat_subtechniques, + }) + ), +]); +export const threat_techniques = t.array(threat_technique); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts new file mode 100644 index 0000000000000..19e75dcc3be07 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export const throttle = t.string; +export type Throttle = t.TypeOf; + +export const throttleOrNull = t.union([throttle, t.null]); +export type ThrottleOrNull = t.TypeOf; + +export const throttleOrNullOrUndefined = t.union([throttle, t.null, t.undefined]); +export type ThrottleOrUndefinedOrNull = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts new file mode 100644 index 0000000000000..0ba0062c5dbeb --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const updated_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts new file mode 100644 index 0000000000000..2c0b2d523f1e3 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const updated_by = t.string; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts new file mode 100644 index 0000000000000..e8214ac60313f --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { UUID } from '.'; +import { foldLeftRight, getPaths } from '../test_utils'; + +describe('uuid', () => { + test('it should validate a uuid', () => { + const payload = '4656dc92-5832-11ea-8e2d-0242ac130003'; + const decoded = UUID.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a non uuid', () => { + const payload = '4656dc92-5832-11ea-8e2d'; + const decoded = UUID.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "4656dc92-5832-11ea-8e2d" supplied to "UUID"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = UUID.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "UUID"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts new file mode 100644 index 0000000000000..0bdfecc1c57db --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +/** + * Types the risk score as: + * - Natural Number (positive integer and not a float), + * - Between the values [0 and 100] inclusive. + */ +export const UUID = new t.Type( + 'UUID', + t.string.is, + (input, context): Either => { + return typeof input === 'string' && regex.test(input) + ? t.success(input) + : t.failure(input, context); + }, + t.identity +); + +export type UUIDC = typeof UUID; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts new file mode 100644 index 0000000000000..45014ecebb33a --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { left, right } from 'fp-ts/lib/Either'; +import * as t from 'io-ts'; +import { validate, validateEither } from '.'; + +describe('validate', () => { + test('it should do a validation correctly', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 1 }; + const [validated, errors] = validate(payload, schema); + + expect(validated).toEqual(payload); + expect(errors).toEqual(null); + }); + + test('it should do an in-validation correctly', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 'some other value' }; + const [validated, errors] = validate(payload, schema); + + expect(validated).toEqual(null); + expect(errors).toEqual('Invalid value "some other value" supplied to "a"'); + }); +}); + +describe('validateEither', () => { + it('returns the ORIGINAL payload as right if valid', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 1 }; + const result = validateEither(schema, payload); + + expect(result).toEqual(right(payload)); + }); + + it('returns an error string if invalid', () => { + const schema = t.exact(t.type({ a: t.number })); + const payload = { a: 'some other value' }; + const result = validateEither(schema, payload); + + expect(result).toEqual(left(new Error('Invalid value "some other value" supplied to "a"'))); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/validate/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/validate/index.ts new file mode 100644 index 0000000000000..38f9ee2d0b227 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/validate/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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fold, Either, mapLeft } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fromEither, TaskEither } from 'fp-ts/lib/TaskEither'; +import * as t from 'io-ts'; +import { exactCheck } from '../exact_check'; +import { formatErrors } from '../format_errors'; + +export const validate = ( + obj: object, + schema: T +): [t.TypeOf | null, string | null] => { + const decoded = schema.decode(obj); + const checked = exactCheck(obj, decoded); + const left = (errors: t.Errors): [T | null, string | null] => [ + null, + formatErrors(errors).join(','), + ]; + const right = (output: T): [T | null, string | null] => [output, null]; + return pipe(checked, fold(left, right)); +}; + +export const validateNonExact = ( + obj: unknown, + schema: T +): [t.TypeOf | null, string | null] => { + const decoded = schema.decode(obj); + const left = (errors: t.Errors): [T | null, string | null] => [ + null, + formatErrors(errors).join(','), + ]; + const right = (output: T): [T | null, string | null] => [output, null]; + return pipe(decoded, fold(left, right)); +}; + +export const validateEither = ( + schema: T, + obj: A +): Either => + pipe( + obj, + (a) => schema.validate(a, t.getDefaultContext(schema.asDecoder())), + mapLeft((errors) => new Error(formatErrors(errors).join(','))) + ); + +export const validateTaskEither = ( + schema: T, + obj: A +): TaskEither => fromEither(validateEither(schema, obj)); diff --git a/packages/kbn-securitysolution-io-ts-utils/src/version/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/version/index.ts new file mode 100644 index 0000000000000..38cb47ebce53e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/version/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; +import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero'; + +/** + * Note this is just a positive number, but we use it as a type here which is still ok. + * This type was originally from "x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts" + * but is moved here to make things more portable. No unit tests, but see PositiveIntegerGreaterThanZero integer for unit tests. + */ +export const version = PositiveIntegerGreaterThanZero; +export type Version = t.TypeOf; diff --git a/packages/kbn-securitysolution-io-ts-utils/tsconfig.json b/packages/kbn-securitysolution-io-ts-utils/tsconfig.json new file mode 100644 index 0000000000000..22718276926f0 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": true, + "outDir": "target", + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-securitysolution-io-ts-utils/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-securitysolution-utils/BUILD.bazel b/packages/kbn-securitysolution-utils/BUILD.bazel new file mode 100644 index 0000000000000..33c25a3091c24 --- /dev/null +++ b/packages/kbn-securitysolution-utils/BUILD.bazel @@ -0,0 +1,86 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-securitysolution-utils" + +PKG_REQUIRE_NAME = "@kbn/securitysolution-utils" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.mock.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "@npm//tslib", + "@npm//uuid", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/uuid" +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + srcs = SRCS, + args = ["--pretty"], + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + root_dir = "src", + source_map = True, + tsconfig = ":tsconfig", + deps = DEPS, +) + +js_library( + name = PKG_BASE_NAME, + package_name = PKG_REQUIRE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + visibility = ["//visibility:public"], + deps = [":tsc"] + DEPS, +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ], +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/packages/kbn-securitysolution-utils/README.md b/packages/kbn-securitysolution-utils/README.md new file mode 100644 index 0000000000000..bf0ab96e5c659 --- /dev/null +++ b/packages/kbn-securitysolution-utils/README.md @@ -0,0 +1,4 @@ +# kbn-securitysolution-utils + +This is where shared utils for security solution should go that are going to be shared among plugins. +This was originally created to remove the dependencies between security_solution and other projects such as lists. diff --git a/packages/kbn-securitysolution-utils/jest.config.js b/packages/kbn-securitysolution-utils/jest.config.js new file mode 100644 index 0000000000000..12213b6777245 --- /dev/null +++ b/packages/kbn-securitysolution-utils/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-utils'], +}; diff --git a/packages/kbn-securitysolution-utils/package.json b/packages/kbn-securitysolution-utils/package.json new file mode 100644 index 0000000000000..d4b46ed07bfdd --- /dev/null +++ b/packages/kbn-securitysolution-utils/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-utils", + "version": "1.0.0", + "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts new file mode 100644 index 0000000000000..30656bc75d115 --- /dev/null +++ b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { addIdToItem, removeIdFromItem } from '.'; + +jest.mock('uuid', () => ({ + v4: jest.fn().mockReturnValue('123'), +})); + +describe('add_remove_id_to_item', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('addIdToItem', () => { + test('it adds an id to an empty item', () => { + expect(addIdToItem({})).toEqual({ id: '123' }); + }); + + test('it adds a complex object', () => { + expect( + addIdToItem({ + field: '', + type: 'mapping', + value: '', + }) + ).toEqual({ + id: '123', + field: '', + type: 'mapping', + value: '', + }); + }); + + test('it adds an id to an existing item', () => { + expect(addIdToItem({ test: '456' })).toEqual({ id: '123', test: '456' }); + }); + + test('it does not change the id if it already exists', () => { + expect(addIdToItem({ id: '456' })).toEqual({ id: '456' }); + }); + + test('it returns the same reference if it has an id already', () => { + const obj = { id: '456' }; + expect(addIdToItem(obj)).toBe(obj); + }); + + test('it returns a new reference if it adds an id to an item', () => { + const obj = { test: '456' }; + expect(addIdToItem(obj)).not.toBe(obj); + }); + }); + + describe('removeIdFromItem', () => { + test('it removes an id from an item', () => { + expect(removeIdFromItem({ id: '456' })).toEqual({}); + }); + + test('it returns a new reference if it removes an id from an item', () => { + const obj = { id: '123', test: '456' }; + expect(removeIdFromItem(obj)).not.toBe(obj); + }); + + test('it does not effect an item without an id', () => { + expect(removeIdFromItem({ test: '456' })).toEqual({ test: '456' }); + }); + + test('it returns the same reference if it does not have an id already', () => { + const obj = { test: '456' }; + expect(removeIdFromItem(obj)).toBe(obj); + }); + }); +}); diff --git a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts new file mode 100644 index 0000000000000..682e38ae2b8bf --- /dev/null +++ b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import uuid from 'uuid'; + +/** + * This is useful for when you have arrays without an ID and need to add one for + * ReactJS keys. I break the types slightly by introducing an id to an arbitrary item + * but then cast it back to the regular type T. + * Usage of this could be considered tech debt as I am adding an ID when the backend + * could be doing the same thing but it depends on how you want to model your data and + * if you view modeling your data with id's to please ReactJS a good or bad thing. + * @param item The item to add an id to. + */ +type NotArray = T extends unknown[] ? never : T; +export const addIdToItem = (item: NotArray): T => { + const maybeId: typeof item & { id?: string } = item; + if (maybeId.id != null) { + return item; + } else { + return { ...item, id: uuid.v4() }; + } +}; + +/** + * This is to reverse the id you added to your arrays for ReactJS keys. + * @param item The item to remove the id from. + */ +export const removeIdFromItem = ( + item: NotArray +): + | T + | Pick< + T & { + id?: string | undefined; + }, + Exclude + > => { + const maybeId: typeof item & { id?: string } = item; + if (maybeId.id != null) { + const { id, ...noId } = maybeId; + return noId; + } else { + return item; + } +}; diff --git a/packages/kbn-securitysolution-utils/src/index.ts b/packages/kbn-securitysolution-utils/src/index.ts new file mode 100644 index 0000000000000..0bb36c590ffdf --- /dev/null +++ b/packages/kbn-securitysolution-utils/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './add_remove_id_to_item'; diff --git a/packages/kbn-securitysolution-utils/tsconfig.json b/packages/kbn-securitysolution-utils/tsconfig.json new file mode 100644 index 0000000000000..783e67666d8b8 --- /dev/null +++ b/packages/kbn-securitysolution-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": true, + "outDir": "target", + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-securitysolution-utils/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-server-http-tools/package.json b/packages/kbn-server-http-tools/package.json index 5a1bb0d5b536a..c44bf17079aab 100644 --- a/packages/kbn-server-http-tools/package.json +++ b/packages/kbn-server-http-tools/package.json @@ -10,10 +10,7 @@ "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, - "dependencies": { - "@kbn/crypto": "link:../kbn-crypto" - }, "devDependencies": { "@kbn/utility-types": "link:../kbn-utility-types" } -} \ No newline at end of file +} diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 15d6ac90b2ebe..a668d8c1f8588 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -14,7 +14,6 @@ "devOnly": true }, "dependencies": { - "@kbn/es": "link:../kbn-es", "@kbn/i18n": "link:../kbn-i18n", "@kbn/optimizer": "link:../kbn-optimizer" } diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js index ac5b608a36820..487ac7e35ff3b 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js @@ -13,6 +13,7 @@ import { REPO_ROOT } from '@kbn/utils'; import { loadTestFiles } from './load_test_files'; import { filterSuitesByTags } from './filter_suites_by_tags'; import { MochaReporterProvider } from './reporter'; +import { validateCiGroupTags } from './validate_ci_group_tags'; /** * Instantiate mocha and load testfiles into it @@ -45,6 +46,9 @@ export async function setupMocha(lifecycle, log, config, providers) { updateSnapshots: config.get('updateSnapshots'), }); + // valiate that there aren't any tests in multiple ciGroups + validateCiGroupTags(log, mocha); + // Each suite has a tag that is the path relative to the root of the repo // So we just need to take input paths, make them relative to the root, and use them as tags // Also, this is a separate filterSuitesByTags() call so that the test suites will be filtered first by diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js new file mode 100644 index 0000000000000..3446c5be5d4a7 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; + +import { REPO_ROOT } from '@kbn/dev-utils'; + +/** + * Traverse the suites configured and ensure that each suite has no more than one ciGroup assigned + * + * @param {ToolingLog} log + * @param {Mocha} mocha + */ +export function validateCiGroupTags(log, mocha) { + const tagCache = new Map(); + const getTags = (suite) => { + const cached = tagCache.get(suite); + if (cached) { + return cached; + } + + const allTags = [ + ...new Set([...(suite.parent ? getTags(suite.parent) : []), ...(suite._tags ?? [])]), + ]; + tagCache.set(suite, allTags); + return allTags; + }; + + const getCiGroups = (suite) => { + return getTags(suite).filter((t) => t.startsWith('ciGroup')); + }; + + const getTitles = (suite) => { + const all = suite.parent ? getTitles(suite.parent) : []; + if (suite.title) { + all.push(suite.title.trim()); + } + return all; + }; + + const suitesWithMultipleCiGroups = []; + + const queue = [mocha.suite]; + while (queue.length) { + const suite = queue.shift(); + if (getCiGroups(suite).length > 1) { + suitesWithMultipleCiGroups.push(suite); + } else { + queue.push(...(suite.suites ?? [])); + } + } + + if (suitesWithMultipleCiGroups.length) { + const list = suitesWithMultipleCiGroups + .map((s) => { + const groups = getCiGroups(s).join(', '); + const title = getTitles(s).join(' > ') || ''; + const from = s.file ? ` (from: ${Path.relative(REPO_ROOT, s.file)})` : ''; + + return ` - ${groups}: ${title}${from}`; + }) + .join('\n'); + + log.error( + `${suitesWithMultipleCiGroups.length} suites found which are assigned to multiple ciGroups:\n${list}` + ); + + throw new Error('some suites have mutliple ciGroup tags'); + } +} diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 8b08f64ba0f62..54d983bf1bf44 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,6 @@ "kbn:watch": "node scripts/build --dev --watch" }, "dependencies": { - "@kbn/analytics": "link:../kbn-analytics", "@kbn/i18n": "link:../kbn-i18n", "@kbn/monaco": "link:../kbn-monaco" } diff --git a/rfcs/0000_template.md b/rfcs/0000_template.md index 85d8674f1affc..52785ccc767c0 100644 --- a/rfcs/0000_template.md +++ b/rfcs/0000_template.md @@ -1,25 +1,47 @@ - Start Date: (fill me in with today's date, YYYY-MM-DD) -- RFC PR: (leave this empty) -- Kibana Issue: (leave this empty) +- TTL: (e.g. "April 20th, 2021", time the review is expected to be completed by. Don't use relative days.) +- Champion: (usually you, person who writes and updates the draft and incorporates feedback) +- Main reviewer: (somebody familiar with the subject matter, who has committed to provide timely and detailed reviews for this RFC) +- Owner team: (team who will own implementation, if it is accepted) +- Stakeholders: (people or groups who will be affected by the proposed changes) +- RFC PR: (leave this empty, it will be a link to PR of this RFC) +- PoC PR: (optional, link to a PoC implementation of the feature) +- Kibana Issue: (link to issue where the proposed feature is tracked) -# Summary -Brief explanation of the feature. +# Executive Summary -# Basic example +Summarize this RFC so those unfamiliar with the project and code can quickly understand +what the problem is, why it is important, +and the proposed solution. Below are some suggested sections for the Executive +Summary. Tweak as you desire and try to keep it succinct. -If the proposal involves a new or changed API, include a basic code example. -Omit this section if it's not applicable. +## Problem statement -# Motivation +What is the problem we are trying to solve? Supply any relevant background +context. Why is this something we should focus on _now_. -Why are we doing this? What use cases does it support? What is the expected -outcome? +Focus on explaining the problem so that if this RFC is not accepted, this +information could be used to develop alternative solutions. In other words, +don't couple this too closely with the solution you have in mind. + +## Goals + +What are the goals of this project? How will we know if it was successful? + +## Proposal + +What are we doing to achieve the goals and solve the problem? + + +# Who is affected and how + +Use this section to hone in on who will be affected and how. For example: + +- Are consumers of a specific plugin affected because of a public API change? +- Will all Kibana Contributors be affected because of a change that may affect + the development experience? -Please focus on explaining the motivation so that if this RFC is not accepted, -the motivation could be used to develop alternative solutions. In other words, -enumerate the constraints you are trying to solve without coupling them too -closely to the solution you have in mind. # Detailed design @@ -29,7 +51,15 @@ implementation to implement. This should get into specifics and corner-cases, and include examples of how the feature is used. Any new terminology should be defined here. -# Drawbacks +Include architectural diagrams if you see fit, a picture is worth a thousand +words. + +## Terminology + +A glossary of new terms can be very helpful. + + +# Risks Why should we *not* do this? Please consider: @@ -40,22 +70,26 @@ Why should we *not* do this? Please consider: There are tradeoffs to choosing any path. Attempt to identify them here. + # Alternatives What other designs have been considered? What is the impact of not doing this? + # Adoption strategy If we implement this proposal, how will existing Kibana developers adopt it? Is this a breaking change? Can we write a codemod? Should we coordinate with other projects or libraries? + # How this scales Does this change affect Kibana's performance in a substantial way? Have we discovered the upper bounds before we see performance degradations? Will any load tests be added to cover these scenarios? + # How we teach this What names and terminology work best for these concepts and why? How is this @@ -67,7 +101,8 @@ at any level? How should this feature be taught to existing Kibana developers? + # Unresolved questions Optional, but suggested for first drafts. What parts of the design are still -TBD? \ No newline at end of file +TBD? diff --git a/rfcs/README.md b/rfcs/README.md index 0088ba69b8cc7..5809f2198f925 100644 --- a/rfcs/README.md +++ b/rfcs/README.md @@ -1,7 +1,5 @@ # Kibana RFCs -> We are currently trialing a new RFC process for the Kibana Core team at this time. - Many changes, including small to medium features, fixes, and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow. @@ -33,7 +31,7 @@ You should consider using this process if you intend to make "substantial" changes to Kibana or its documentation. Some examples that would benefit from an RFC are: - - A new feature that creates new API surface area, such as a new core + - A new feature that creates new API surface area, such as a new service available to plugins. - The removal of features that already shipped as part of a release. - The introduction of new idiomatic usage or conventions, even if they @@ -60,9 +58,9 @@ Some changes do not require an RFC: ## What the process is -In short, to get a major feature added to Kibana Core, one usually first gets -the RFC merged into the RFC tree as a markdown file. At that point the RFC -is 'active' and may be implemented with the goal of eventual inclusion +In short, to get a major feature added to the Kibana codebase, one usually +first gets the RFC merged into the RFC tree as a markdown file. At that point +the RFC is 'active' and may be implemented with the goal of eventual inclusion into Kibana. * Fork the Kibana repo http://github.com/elastic/kibana @@ -98,8 +96,8 @@ become 'active'. Once an RFC becomes active, then authors may implement it and submit the feature as a pull request to the Kibana repo. Becoming 'active' is not a rubber stamp, and in particular still does not mean the feature will ultimately -be merged; it does mean that the core team has agreed to it in principle -and are amenable to merging it. +be merged; it does mean that the team in ownership of the feature has agreed to +it in principle and are amenable to merging it. Furthermore, the fact that a given RFC has been accepted and is 'active' implies nothing about what priority is assigned to its @@ -129,8 +127,9 @@ feel free to ask (e.g. by leaving a comment on the associated issue). Each week the team will attempt to review some set of open RFC pull requests. -Every accepted feature should have a core team champion, -who will represent the feature and its progress. +Every accepted feature should have a champion from the team which will +ultimately maintain the feature long-term. The champion will represent the +feature and its progress. **Kibana's RFC process owes its inspiration to the [React RFC process], [Yarn RFC process], [Rust RFC process], and [Ember RFC process]** diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index e69006911e7f4..581d614c9a371 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -199,6 +199,9 @@ export class DocLinksService { percolate: `${ELASTICSEARCH_DOCS}query-dsl-percolate-query.html`, queryDsl: `${ELASTICSEARCH_DOCS}query-dsl.html`, }, + search: { + sessions: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/search-sessions.html`, + }, date: { dateMath: `${ELASTICSEARCH_DOCS}common-options.html#date-math`, dateMathIndexNames: `${ELASTICSEARCH_DOCS}date-math-index-names.html`, @@ -501,6 +504,9 @@ export interface DocLinksStart { readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; + readonly search: { + readonly sessions: string; + }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts index d9612d113632a..73418eafccd71 100644 --- a/src/core/public/http/types.ts +++ b/src/core/public/http/types.ts @@ -339,7 +339,7 @@ export interface IHttpFetchError extends Error { * @deprecated Provided for legacy compatibility. Prefer the `response` property instead. */ readonly res?: Response; - readonly body?: any; + readonly body?: any; // TODO: this should be unknown } /** @public */ diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 13660da598ea0..0523c523baf6f 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -589,6 +589,9 @@ export interface DocLinksStart { readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; + readonly search: { + readonly sessions: string; + }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index 8e538f6e12384..1dd4a8fbf6388 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -169,7 +169,7 @@ export class DocumentMigrator implements VersionedTransformer { } /** - * Gets the latest version of each migratable property. + * Gets the latest version of each migrate-able property. * * @readonly * @type {SavedObjectsMigrationVersion} diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index 44dd60097f1cd..76fdd5e73d804 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -30,7 +30,7 @@ export interface FullIndexInfo { // When migrating from the outdated index we use a read query which excludes // saved objects which are no longer used. These saved objects will still be -// kept in the outdated index for backup purposes, but won't be availble in +// kept in the outdated index for backup purposes, but won't be available in // the upgraded index. export const excludeUnusedTypesQuery: estypes.QueryContainer = { bool: { diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts index c6dfd2c2d1809..37cea5d2de3d2 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts @@ -344,13 +344,13 @@ const mockV2MigrationOptions = () => { options.client.openPointInTime = jest .fn() - .mockImplementationOnce(() => + .mockImplementation(() => elasticsearchClientMock.createSuccessTransportRequestPromise({ id: 'pit_id' }) ); options.client.closePointInTime = jest .fn() - .mockImplementationOnce(() => + .mockImplementation(() => elasticsearchClientMock.createSuccessTransportRequestPromise({ succeeded: true }) ); diff --git a/src/core/server/saved_objects/migrationsv2/actions/catch_retryable_es_client_errors.ts b/src/core/server/saved_objects/migrationsv2/actions/catch_retryable_es_client_errors.ts index 71f1e0d954869..3d9a51e3b1eba 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/catch_retryable_es_client_errors.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/catch_retryable_es_client_errors.ts @@ -31,16 +31,16 @@ export const catchRetryableEsClientErrors = ( e instanceof EsErrors.ConnectionError || e instanceof EsErrors.TimeoutError || (e instanceof EsErrors.ResponseError && - (retryResponseStatuses.includes(e.statusCode) || + (retryResponseStatuses.includes(e?.statusCode) || // ES returns a 400 Bad Request when trying to close or delete an // index while snapshots are in progress. This should have been a 503 // so once https://github.com/elastic/elasticsearch/issues/65883 is // fixed we can remove this. - e.body?.error?.type === 'snapshot_in_progress_exception')) + e?.body?.error?.type === 'snapshot_in_progress_exception')) ) { return Either.left({ type: 'retryable_es_client_error' as const, - message: e.message, + message: e?.message, error: e, }); } else { diff --git a/src/core/server/saved_objects/migrationsv2/actions/index.test.ts b/src/core/server/saved_objects/migrationsv2/actions/index.test.ts index b144905cf01ad..ba6aafbb2f651 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/index.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/index.test.ts @@ -18,7 +18,7 @@ describe('actions', () => { jest.clearAllMocks(); }); - // Create a mock client that rejects all methods with a 503 statuscode + // Create a mock client that rejects all methods with a 503 status code // response. const retryableError = new EsErrors.ResponseError( elasticsearchClientMock.createApiResponse({ @@ -30,6 +30,11 @@ describe('actions', () => { elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) ); + const nonRetryableError = new Error('crash'); + const clientWithNonRetryableError = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(nonRetryableError) + ); + describe('fetchIndices', () => { it('calls catchRetryableEsClientErrors when the promise rejects', async () => { const task = Actions.fetchIndices(client, ['my_index']); @@ -52,6 +57,11 @@ describe('actions', () => { } expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('cloneIndex', () => { @@ -64,6 +74,11 @@ describe('actions', () => { } expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('pickupUpdatedMappings', () => { @@ -92,7 +107,7 @@ describe('actions', () => { describe('readWithPit', () => { it('calls catchRetryableEsClientErrors when the promise rejects', async () => { - const task = Actions.readWithPit(client, 'pitId', Option.none, 10_000); + const task = Actions.readWithPit(client, 'pitId', { match_all: {} }, 10_000); try { await task(); } catch (e) { @@ -134,7 +149,7 @@ describe('actions', () => { 'my_target_index', Option.none, false, - Option.none + {} ); try { await task(); @@ -156,6 +171,11 @@ describe('actions', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('waitForPickupUpdatedMappingsTask', () => { @@ -169,6 +189,11 @@ describe('actions', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('updateAliases', () => { @@ -182,6 +207,11 @@ describe('actions', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('createIndex', () => { @@ -195,6 +225,11 @@ describe('actions', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('re-throws non retry-able errors', async () => { + const task = Actions.setWriteBlock(clientWithNonRetryableError, 'my_index'); + await task(); + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + }); }); describe('updateAndPickupMappings', () => { @@ -263,4 +298,17 @@ describe('actions', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); }); + + describe('refreshIndex', () => { + it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + const task = Actions.refreshIndex(client, 'target_index'); + try { + await task(); + } catch (e) { + /** ignore */ + } + + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); + }); + }); }); diff --git a/src/core/server/saved_objects/migrationsv2/actions/index.ts b/src/core/server/saved_objects/migrationsv2/actions/index.ts index 049cdc41b7527..79261aecf675c 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/index.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/index.ts @@ -107,34 +107,37 @@ export const setWriteBlock = ( IndexNotFound | RetryableEsClientError, 'set_write_block_succeeded' > => () => { - return client.indices - .addBlock<{ - acknowledged: boolean; - shards_acknowledged: boolean; - }>( - { - index, - block: 'write', - }, - { maxRetries: 0 /** handle retry ourselves for now */ } - ) - .then((res: any) => { - return res.body.acknowledged === true - ? Either.right('set_write_block_succeeded' as const) - : Either.left({ - type: 'retryable_es_client_error' as const, - message: 'set_write_block_failed', - }); - }) - .catch((e: ElasticsearchClientError) => { - if (e instanceof EsErrors.ResponseError) { - if (e.message === 'index_not_found_exception') { - return Either.left({ type: 'index_not_found_exception' as const, index }); + return ( + client.indices + .addBlock<{ + acknowledged: boolean; + shards_acknowledged: boolean; + }>( + { + index, + block: 'write', + }, + { maxRetries: 0 /** handle retry ourselves for now */ } + ) + // not typed yet + .then((res: any) => { + return res.body.acknowledged === true + ? Either.right('set_write_block_succeeded' as const) + : Either.left({ + type: 'retryable_es_client_error' as const, + message: 'set_write_block_failed', + }); + }) + .catch((e: ElasticsearchClientError) => { + if (e instanceof EsErrors.ResponseError) { + if (e.body?.error?.type === 'index_not_found_exception') { + return Either.left({ type: 'index_not_found_exception' as const, index }); + } } - } - throw e; - }) - .catch(catchRetryableEsClientErrors); + throw e; + }) + .catch(catchRetryableEsClientErrors) + ); }; /** @@ -263,12 +266,12 @@ export const cloneIndex = ( }); }) .catch((error: EsErrors.ResponseError) => { - if (error.body.error.type === 'index_not_found_exception') { + if (error?.body?.error?.type === 'index_not_found_exception') { return Either.left({ type: 'index_not_found_exception' as const, index: error.body.error.index, }); - } else if (error.body.error.type === 'resource_already_exists_exception') { + } else if (error?.body?.error?.type === 'resource_already_exists_exception') { /** * If the target index already exists it means a previous clone * operation had already been started. However, we can't be sure @@ -452,21 +455,18 @@ export interface ReadWithPit { /* * Requests documents from the index using PIT mechanism. - * Filter unusedTypesToExclude documents out to exclude them from being migrated. * */ export const readWithPit = ( client: ElasticsearchClient, pitId: string, - /* When reading we use a source query to exclude saved objects types which - * are no longer used. These saved objects will still be kept in the outdated - * index for backup purposes, but won't be available in the upgraded index. - */ - unusedTypesQuery: Option.Option, + query: estypes.QueryContainer, batchSize: number, - searchAfter?: number[] + searchAfter?: number[], + seqNoPrimaryTerm?: boolean ): TaskEither.TaskEither => () => { return client .search({ + seq_no_primary_term: seqNoPrimaryTerm, body: { // Sort fields are required to use searchAfter sort: { @@ -479,8 +479,7 @@ export const readWithPit = ( // Improve performance by not calculating the total number of hits // matching the query. track_total_hits: false, - // Exclude saved object types - query: Option.isSome(unusedTypesQuery) ? unusedTypesQuery.value : undefined, + query, }, }) .then((response) => { @@ -531,6 +530,7 @@ export const transformDocs = ( transformRawDocs: TransformRawDocs, outdatedDocuments: SavedObjectsRawDoc[], index: string, + // used for testing purposes only refresh: estypes.Refresh ): TaskEither.TaskEither< RetryableEsClientError | IndexNotFound | TargetIndexHadWriteBlock, @@ -551,6 +551,22 @@ export interface ReindexResponse { taskId: string; } +/** + * Wait for Elasticsearch to reindex all the changes. + */ +export const refreshIndex = ( + client: ElasticsearchClient, + targetIndex: string +): TaskEither.TaskEither => () => { + return client.indices + .refresh({ + index: targetIndex, + }) + .then(() => { + return Either.right({ refreshed: true }); + }) + .catch(catchRetryableEsClientErrors); +}; /** * Reindex documents from the `sourceIndex` into the `targetIndex`. Returns a * task ID which can be tracked for progress. @@ -569,7 +585,7 @@ export const reindex = ( * are no longer used. These saved objects will still be kept in the outdated * index for backup purposes, but won't be available in the upgraded index. */ - unusedTypesQuery: Option.Option + unusedTypesQuery: estypes.QueryContainer ): TaskEither.TaskEither => () => { return client .reindex({ @@ -584,10 +600,7 @@ export const reindex = ( // Set reindex batch size size: BATCH_SIZE, // Exclude saved object types - query: Option.fold( - () => undefined, - (query) => query - )(unusedTypesQuery), + query: unusedTypesQuery, }, dest: { index: targetIndex, @@ -785,22 +798,22 @@ export const updateAliases = ( }) .catch((err: EsErrors.ElasticsearchClientError) => { if (err instanceof EsErrors.ResponseError) { - if (err.body.error.type === 'index_not_found_exception') { + if (err?.body?.error?.type === 'index_not_found_exception') { return Either.left({ type: 'index_not_found_exception' as const, index: err.body.error.index, }); } else if ( - err.body.error.type === 'illegal_argument_exception' && - err.body.error.reason.match( + err?.body?.error?.type === 'illegal_argument_exception' && + err?.body?.error?.reason?.match( /The provided expression \[.+\] matches an alias, specify the corresponding concrete indices instead./ ) ) { return Either.left({ type: 'remove_index_not_a_concrete_index' as const }); } else if ( - err.body.error.type === 'aliases_not_found_exception' || - (err.body.error.type === 'resource_not_found_exception' && - err.body.error.reason.match(/required alias \[.+\] does not exist/)) + err?.body?.error?.type === 'aliases_not_found_exception' || + (err?.body?.error?.type === 'resource_not_found_exception' && + err?.body?.error?.reason?.match(/required alias \[.+\] does not exist/)) ) { return Either.left({ type: 'alias_not_found_exception' as const, @@ -893,7 +906,7 @@ export const createIndex = ( }); }) .catch((error) => { - if (error.body.error.type === 'resource_already_exists_exception') { + if (error?.body?.error?.type === 'resource_already_exists_exception') { /** * If the target index already exists it means a previous create * operation had already been started. However, we can't be sure @@ -997,6 +1010,8 @@ interface SearchForOutdatedDocumentsOptions { * Search for outdated saved object documents with the provided query. Will * return one batch of documents. Searching should be repeated until no more * outdated documents can be found. + * + * Used for testing only */ export const searchForOutdatedDocuments = ( client: ElasticsearchClient, diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts index b31f20950ae77..832d322037465 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts @@ -408,7 +408,7 @@ describe('migration actions', () => { 'reindex_target', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -440,13 +440,13 @@ describe('migration actions', () => { 'reindex_target_excluded_docs', Option.none, false, - Option.of({ + { bool: { must_not: ['f_agent_event', 'another_unused_type'].map((type) => ({ term: { type }, })), }, - }) + } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -477,7 +477,7 @@ describe('migration actions', () => { 'reindex_target_2', Option.some(`ctx._source.title = ctx._source.title + '_updated'`), false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -510,7 +510,7 @@ describe('migration actions', () => { 'reindex_target_3', Option.some(`ctx._source.title = ctx._source.title + '_updated'`), false, - Option.none + { match_all: {} } )()) as Either.Right; let task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -527,7 +527,7 @@ describe('migration actions', () => { 'reindex_target_3', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -577,7 +577,7 @@ describe('migration actions', () => { 'reindex_target_4', Option.some(`ctx._source.title = ctx._source.title + '_updated'`), false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -627,7 +627,7 @@ describe('migration actions', () => { 'reindex_target_5', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, reindexTaskId, '10s'); @@ -662,7 +662,7 @@ describe('migration actions', () => { 'reindex_target_6', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, reindexTaskId, '10s'); @@ -677,14 +677,9 @@ describe('migration actions', () => { }); it('resolves left index_not_found_exception if source index does not exist', async () => { expect.assertions(1); - const res = (await reindex( - client, - 'no_such_index', - 'reindex_target', - Option.none, - false, - Option.none - )()) as Either.Right; + const res = (await reindex(client, 'no_such_index', 'reindex_target', Option.none, false, { + match_all: {}, + })()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); await expect(task()).resolves.toMatchInlineSnapshot(` Object { @@ -704,7 +699,7 @@ describe('migration actions', () => { 'existing_index_with_write_block', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); @@ -726,7 +721,7 @@ describe('migration actions', () => { 'existing_index_with_write_block', Option.none, true, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '10s'); @@ -748,7 +743,7 @@ describe('migration actions', () => { 'reindex_target', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; const task = waitForReindexTask(client, res.right.taskId, '0s'); @@ -775,7 +770,7 @@ describe('migration actions', () => { 'reindex_target_7', Option.none, false, - Option.none + { match_all: {} } )()) as Either.Right; await waitForReindexTask(client, res.right.taskId, '10s')(); @@ -840,7 +835,7 @@ describe('migration actions', () => { const readWithPitTask = readWithPit( client, pitResponse.right.pitId, - Option.none, + { match_all: {} }, 1000, undefined ); @@ -856,7 +851,7 @@ describe('migration actions', () => { const readWithPitTask = readWithPit( client, pitResponse.right.pitId, - Option.none, + { match_all: {} }, 3, undefined ); @@ -865,14 +860,14 @@ describe('migration actions', () => { await expect(docsResponse.right.outdatedDocuments.length).toBe(3); }); - it('it excludes documents not matching the provided "unusedTypesQuery"', async () => { + it('it excludes documents not matching the provided "query"', async () => { const openPitTask = openPit(client, 'existing_index_with_docs'); const pitResponse = (await openPitTask()) as Either.Right; const readWithPitTask = readWithPit( client, pitResponse.right.pitId, - Option.some({ + { bool: { must_not: [ { @@ -887,7 +882,7 @@ describe('migration actions', () => { }, ], }, - }), + }, 1000, undefined ); @@ -904,8 +899,93 @@ describe('migration actions', () => { `); }); + it('only returns documents that match the provided "query"', async () => { + const openPitTask = openPit(client, 'existing_index_with_docs'); + const pitResponse = (await openPitTask()) as Either.Right; + + const readWithPitTask = readWithPit( + client, + pitResponse.right.pitId, + { + match: { title: { query: 'doc' } }, + }, + 1000, + undefined + ); + + const docsResponse = (await readWithPitTask()) as Either.Right; + + expect(docsResponse.right.outdatedDocuments.map((doc) => doc._source.title).sort()) + .toMatchInlineSnapshot(` + Array [ + "doc 1", + "doc 2", + "doc 3", + ] + `); + }); + + it('returns docs with _seq_no and _primary_term when specified', async () => { + const openPitTask = openPit(client, 'existing_index_with_docs'); + const pitResponse = (await openPitTask()) as Either.Right; + + const readWithPitTask = readWithPit( + client, + pitResponse.right.pitId, + { + match: { title: { query: 'doc' } }, + }, + 1000, + undefined, + true + ); + + const docsResponse = (await readWithPitTask()) as Either.Right; + + expect(docsResponse.right.outdatedDocuments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + _seq_no: expect.any(Number), + _primary_term: expect.any(Number), + }), + ]) + ); + }); + + it('does not return docs with _seq_no and _primary_term if not specified', async () => { + const openPitTask = openPit(client, 'existing_index_with_docs'); + const pitResponse = (await openPitTask()) as Either.Right; + + const readWithPitTask = readWithPit( + client, + pitResponse.right.pitId, + { + match: { title: { query: 'doc' } }, + }, + 1000, + undefined + ); + + const docsResponse = (await readWithPitTask()) as Either.Right; + + expect(docsResponse.right.outdatedDocuments).toEqual( + expect.arrayContaining([ + expect.not.objectContaining({ + _seq_no: expect.any(Number), + _primary_term: expect.any(Number), + }), + ]) + ); + }); + it('rejects if PIT does not exist', async () => { - const readWithPitTask = readWithPit(client, 'no_such_pit', Option.none, 1000, undefined); + const readWithPitTask = readWithPit( + client, + 'no_such_pit', + { match_all: {} }, + 1000, + undefined + ); await expect(readWithPitTask()).rejects.toThrow('illegal_argument_exception'); }); }); @@ -973,49 +1053,6 @@ describe('migration actions', () => { }); }); - describe('searchForOutdatedDocuments', () => { - it('only returns documents that match the outdatedDocumentsQuery', async () => { - expect.assertions(2); - const resultsWithQuery = ((await searchForOutdatedDocuments(client, { - batchSize: 1000, - targetIndex: 'existing_index_with_docs', - outdatedDocumentsQuery: { - match: { title: { query: 'doc' } }, - }, - })()) as Either.Right).right.outdatedDocuments; - expect(resultsWithQuery.length).toBe(3); - - const resultsWithoutQuery = ((await searchForOutdatedDocuments(client, { - batchSize: 1000, - targetIndex: 'existing_index_with_docs', - outdatedDocumentsQuery: undefined, - })()) as Either.Right).right.outdatedDocuments; - expect(resultsWithoutQuery.length).toBe(5); - }); - it('resolves with _id, _source, _seq_no and _primary_term', async () => { - expect.assertions(1); - const results = ((await searchForOutdatedDocuments(client, { - batchSize: 1000, - targetIndex: 'existing_index_with_docs', - outdatedDocumentsQuery: { - match: { title: { query: 'doc' } }, - }, - })()) as Either.Right).right.outdatedDocuments; - expect(results).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - _id: expect.any(String), - _seq_no: expect.any(Number), - _primary_term: expect.any(Number), - _source: expect.any(Object), - }), - ]) - ); - }); - // I haven't been able to find a way to reproduce a partial search result - // it.todo('rejects if only partial search results can be obtained'); - }); - describe('waitForPickupUpdatedMappingsTask', () => { it('rejects if there are failures', async () => { const res = (await pickupUpdatedMappings( diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/archives/8.0.0_migrated_with_outdated_docs.zip b/src/core/server/saved_objects/migrationsv2/integration_tests/archives/8.0.0_migrated_with_outdated_docs.zip new file mode 100644 index 0000000000000000000000000000000000000000..ca936c6448b43361a310d948309c54b398862bda GIT binary patch literal 48381 zcmd43W0Ymxk}jOKZQHhOSEACkZQHg{Y1_8#O51kcde7pD8=I~o6d6qkQJ%3q^F0Q@zI$kH+X=$~KNe;$JV z52H95n_3&&I63Mv8vl(g=f7C`?`$>nE@QuLze29z@$(}l{LJ6PzH2fzAK0Gz(Wg7ucr zWpZxMqD(muIz&&R`eUYtk?G4f$H(XqYq;IrDJ$(yC`Cpv-`}s&!ua*hE3uH*wUY2L za&k81k+6c2O&A#rCu;pv0fAY6Nmwk<5)*c*J(@&ebcDZtxe-~J*OTA61Bgw)*;j}q zTImJGO4MZ#=&Lc~Q~aBH|F>cQ$bXR}{!t9tAH^X5gJN_U_2~>v{*OwbyHd``%1E`a z*vZP(jML3X-TR}Ur1YPY03ePzEV5kh?H4r;1CbjAmu*ilI5ZySl&CNWdEmvkFZ+Zh z>S=@6C&dTk!bA6Yjrb8>k|uO2UhqkDf{FBg5X@KthrEi1j>>s=$qE_J2Nb3GGE)k& z_kIEH3W6r0M#-cIaFJ{(^RIrx2j~jgFw26x&Ho_Zs*6$s{2$p2{}9qYk`v%={D42pHigObkSh02qmh(UpA|Lv0P}1AJ?RKwMP~GzxSyEOexN z5MvFcd<4UN1ub$?BiZ3q3Q9Sc`D4dsqwxD@f+6d$GR(0vZx8*lLC_m#8)lgeX6Tot zX{4t0Dry-b*_jam&uOTpsK+K{MVF>kvvhwZWsPT~DF4jr16>45HfCp;WoDUU-Ul(K zq?4Sa-C<>6W@2DqWnpD+Ze%bpiDj2rl$MhYo*%LzXy-^&#KRIf>LYy>Eu*waFXN;f zvUwG43n%qxd!;PXlB@0`yzK+5uBL+lXCdp+^8@-_jGPAemuLPRI)6EKlw96H+P{Pj z>wiGU>5nh{MgQRDfBDn@19XI7a6WDk4$;VN!8}OQLH~vzUWLD~v1hFCh7hUqQwlU_ z$_x|NoPzW?X0j5HLN}9lmj1N@nuq|~Q_ zqtxpJFnWwW&no1EzdYhU;m7umkn;d!{`C#@9e052msO-Tz@zt;!#?|u*G zRPyVn7>zUqU=5ty=%~0zb&yyj!uR9DTfp!R8jNDL)z=jHKXLn)NoZ2YbCdtgB>xTV zPXA$%zXI=H^iSCQzqbgdD<;Pu4<4tL*@Jyhx=X|)5P+9@&mT=SIuaVP%yWYLV-hHt z8{niIaHLAVTFm#n05~%;RDr`8!M{5-LBGtNg0mKZurvXVi73RB2>{Au;LjOM2xa%b zNto6&ynFr+0^Uwi6#V7RdVdJK8kcK8Wx}l0E{Ez5^C{=+SNQN<|dyO?>bY^D+5@a-oq8 zSXma>*!Cv?xgqJy@=fzi2Xk~QQ`M8Q`c(Dx(QHkKfR;5>GSm{%Gb75gt2lZZDL9i^ zX@50x1VWd>Qcc*H<(Zllm<~hED`_R9Y4%#0nHcHmS(sVam|2+jO`_=*q-LqY=(p~H|2u!0)tj#1|B1i9GWd1wDxVPu0Duqpze-e& z|5GGyQt_6-Hpb|I9~TQHky`<^KMA6|D8$2U6djOiuGU9l$eYn)65zk6d)QyQ~O0STR-{ChZUK0NuPUg z-L#+L3Y;vjmtJ^(9}m~VWxut zT}acZv0;3`AA;JG5W2m~%2oAat=^D1P5- zYJZm4LK1&s6?#sF(weGmEJS{c7^I&V!EJpeH024)GaAbQCeO`FmzP1XAy?dG~Q;e9TutFBrSWt z9MO$eES@dA=NlT7f4b;kpsD1ClZMB0O38p=_#3jr5-)@L7r7T_9wL(DC|2rU)-i2m z9O^l+;2gEvLrv(<$(5u2N>{whIB;SauN(YEWKs?{7-q?*vai{_G*bY9to3N&iK@it zi7K8FvY5eCW3Q^2S$u%KSrR(DN zQBlPJy+9BFct`B>poym^7iU?kB!pyuawv|k?bX$9QG`Y-FE1F^cU;G{J;4xJ1|#_i zv4Dg?tQ8h-S+*~_8itqGY*2&{8JRFUR_7TNULom0c>M>-jT;-O#4DhH+E>|D;T zXh=ahvqccPiN{|8f=`R_>#oP`)b-m&io`LK-i9Vv@1gf0yi2yhH^~Nb>&mn1V!dUh z1Zol;icjPC>vE{B(3y8BMHh>Wh1>$Hdzk;GS;mW}(V>UgsrGZ`I&ys5gW|Zmi#b`j zVc#@}7(eCv;o~5q25tM|HrckqtD5I}H|O4W<2SGP65A!7y0e|f_rb4N=rQ5f;_6ZL z0DN!&_c*mE9|P7rcDtfe6ON(y?;%Fu zm=!lR&Sz>O5YL$6R=si=;l{e3VQ&-51esG-E1wC{rC=kFMwpeNR2O{@HeYx$0Ds2x zA$;1h9~fn2SO)ZY(@uxMlU+Q%!#Q#B#L#t5cbHnPcv&bQA)tlJIGTMYmZ2Y{RRVMy zsqeb^9Yvv=hx=a=tf2?vDx3t6mZ?xV2A5=w)R=VVx`kl8f&bU(5$<6)!44e&zyRMr z3s`?mkNzI8{>AjD$@~2#TlxC?gm%Oh?1T z=+$(4!^BWpgabIe+sdGb*2760$Ta$Mpo9sNBcV>~~g7n*$4;C^#xB0#2 zG!4c=$@Tk6>vUa9J%(jU4qO_0@8FEykOW!I#oqthgwlN_hX4#P8iFIw3!X9?~ET z%@3)D6Z}s_kr89+<(1t}vi^kMOdQiEMrtu%l|3$?Z7p;@vD=A^TrT>-G_Sn&(kdQY z-AfKD+uzhCLyqmBd(ZTHkMGohI>B`Quc@Axbn}=AeuOtmC+N&gs!->y(dqd@kl=$7 z-D=_s**jmPXBr zYQLFGvo2@0$zv0_ndA~ehd+5w&F(z+yJg&XZv`hTA#=%}Vmh?aZqD9~#x_NStqA<8 zX9|(SADblgf2am<8eP|J;QyFw5MrV7Jc~qQLw7MwJvkTo%Y9v zwjo7`BSsuc^tJ=ilCh(fS|tV_f`CIl9|i=X$GzK^IUaab>0~=e)}&iOuhODr2|Jv7 zOm@F)q9*4t zlCZFn_vy_g2TS%lNhM{aUk-#%?Wb>bpjlXWdm3dS3W^JBfZoqua-U7|EY?jX(y=S8 z%W=Qa_))n+?#ZKM=T3r?{6?(LSo)TnvYJPZg_ddDgL6uQyfmGs$xR_f3^h$m0>&$( z4)to>Qkrt-9+Ok0NV$o*aT(ufu!xV?xyZ>{@H-dcI3bvA4nJ8%cHs@!fI>u=V42np z#kEr0Eyb&ESj#x_{?$td`_>6;$nOG;FSul)4%mX^A_$sHP$gKktN~Xo_iJ+l!5Fxs`{=S>$af z@8o)uESP%3JFUe+!by7FCcV)asvKLm0;e0pRYc`S8!1Ly03sm)Xp-HpS(@^CEK3|e z$0KE;;jmRA$SFnF3KYbfF&oJr-vaAI4gyF(7O+Lxlm1O+07G5LZE*x{qHFlD(o`P& zSW!D7A5yiV*g4RUr*X~vxN>8H^jMT9DgEqy@p(at$7?vqngrHnbV<7?f#(GKvqXCM z#MHB5^A&b?aX3lD^(W}Vh*$G3f*Pm?1aInagILrVs6+JOf>o}O^E9J5$+bQZ$E#W+ z81O<<66#jJK|-ntv*4YV-v?D7m;H?o91Fw-1t3gA!wK?bHMjkYJ2JU-0olA~Ld+O4~L-^Yr8D)uI3=GM$)^Lq8`reQul0dZ#cyF*FCVVW9COoWV^Ha#vzo#G~{{ zFPGI#)$P7og(D~#CD&DFfssJUS#&T0F@Sw00=k&3>ws>}i>*$2C+cf~*L8x+EcC^N z5!!>%H1*1H8i7mm7Nvbnmppbs4Ub6HmB7jpl7xF|ZS2PrC1B@^rWx0=t9lJOHA(1S zrjdkj!@7zvtlPig>C^A8#fGDwb`Fy9jNS<=WhP433YO}g2~51MFiyIxI-@a*KuYyd zSW+G{HB8fh%||6ViGQAR;w^mBZ$i)=vvHolNzd4W&jyF_>92YB5bi>lZn*}GsS*47 ziC@747qe-Emj^%%eqh_gmo#Cn#ks%$syb}QJh-rYkxB6L0HaCtWkO(RBMgR4?8>1R z!M6e6nZkO)oO&|p2kM%q_Upz!2qXJ@4(VMc$y6GQ;-rI-mQyunk%e$c@H3z;?+f$Z zxujS!V$}p9h{-~SgeMwyM4yVM3c!j4px?DV#WuTBS;wh$A(+ z?yWZuVfjwWbsF=am}k)Am*6#H&`w!N9d(N`(5)mfvZNa@yAGPg2n@$t>CxAd0F^}Q zrOz<7%DnzC;Px0cD-W<2*5eLC|M0_21q~E-U_gn6nCfL<(-R5_`n9_FndqktE18%N zsW0{fK^_%8lgZRF$za^rj5zU*VeFYg)^CeEMh^4LZnz#kU==V(`fO(|ZVKoL)eDCl z99&{9ULqWjy&ikY*0+BYbQ;~ute4@*k23hvb)K@&dQPd*{EL(HLs&`Kjx0V6NzvFF4&}$k%n*=`fE|joJ zwFv?#FJw{+WF^J9h!;hHc>5ejT7)3M%Y$lR_GJtx*+mO&@z^m&sr5UOCd>| z0}L31?-GFL^MjfG|}&;1Tq(=K}g!;Rf0jgv#{EML=lw6=r1fjlpCi3*`F3 zL`(B)N20S*Yp&Z+JZ&vLs1JBjT%5tD3kfJ1oKPxl$Y|76Amm#^g8Kn4k=9)(&JNuO zCkIS(5AqYJz!r@}MlW5d*;3C^1x5^^beBl8n#ER-<0LVF=xv-?7}`m2{YGr3PGoc1 z?y<6koTf2mHN&N*$ENdf$g%zz-ovLx7`jpUM~u9Yrfvhroj$ z4e#Z2H7&relU~NMm0Zi%zpP0`d2L`wTxAtIqj zmgmZQe1W{;S;T;)++-_at-~Ip(QYv_4O9Jpqr++&MXO<4k+L=7`-|MRYz=cTAY0L} zV^(&_Gpf2%5gH%Vb^D3u?&I1Lb;C!3aOEo|WR}xZjZezk#a(*1*qZuv!DCbECryqF3?zKu0|@;@LWpdkl0 z$9=@z6)B*Pt|TJALp)a?aKeLS@00-)xIK8x~T;~9Wi%1cnSb#KJ z1M5#qSkSOUd5p@gCaVDAU1(un2{=g%G$gqB(4Do2%}>f@zEv-BOH`M%He)c3v69Ce z=B!~;cVm7e67ycGt#oT6xP*RfDJS~?Oa8k}&`KBF!rf zL904%$Okw4QXm}gO&QJ91Y2;##C(!4gg4HeP>?I6MS8fJfs13zm}x=;iTc0}8Z9b` z5YY_YzwV&2r0qfrl0aWk=ZK?_6h1l^@);#wsk{q*Q@h>9B3hg3LrwlZln9=oU|jat zqSYtGDnRQeRL~T;>FJ0M6=BE=rnIx)V=X3^a#VmQ$WjRQO5ryVgr|IXH25n zjT|(zm&jA6eMOu^II`=n-)jSuVcv1fTarU1>aIl!CjevfM@^1rL{6{)eSyppO2VRz z4g~Pb|0S<$%@DA;5mYM*P2(YrWf)*~&W8X;*aI&IybrOBo8rVRVEawc)Qu+vwpjX( z#!%8PiW4Z6K*2g&3)jPSzYUxo-3Efs$AHB~dL#9Uv9?X=X)>=cEK}Ey^Hpb5>+=m= z%X_buHug<`IPV+g_ija0l*Iw}9;~492#LYo!Vp?BJf*1+Q1a%@yShyebic31t0b9* zI$DDuV*`Lkeec+-o?Z#8+#4l*Vq0i1)CB=3PM97cOJ?&?dz*d`cFxtmW zXw86XLN(_|lKl-sd|J4sxAh4z0E1XjglLFvlyd% z(;w83sMo(grZ;Us)=Ji&+v6I2wv(F%vP_N-dJm93@J$FkdER@+qsMpc7|@1$wI0X{ zYzgQ=I+!$wKqCv%YI*y{(^+-)g9>i7Vt#k3Q>#nr)^+$L5w^{4?!y{lD!1IF*Hpt^ zFQ$;Y$G1^^s6u(*M_sr?3n!Z&I+n%)fV*Q2e3KDIB%P|?C{Ky4W2N{*sMp@efzrG^ zdxYzcRxzaEG5<|6%u!L7!NNxHA5!0>i|#1sQu$`7C}pC991)EIy$_bLFF_1Q3mGu+}od@{t%2m4PWbJs4(BdbHec-cLXZpE2#i#g$-n zORyo@QIGyOtK~rqO9$4bW2%HQ?g>+x1wdR0Iy;mab}9Z+^jqiZRSaA7XOiKftxp0h>Xk$SC;Y{?C7IwR^5xdQx6PFR!r(M@NS(q@0D%&Mn zX;|XQj+mKu^$rl6m2Be%gxSkr&!oVDt6vU*$yq-U%tjZM$!;1!~(4Vw60qsy;t2g-$TU+rcZ96*{uI@Q}{*h4w{8y(ln|0%w=e zQRrT8Bi&bynDpy-wE&dYuQ?cz^{oAl`a3ZYy_RsAi<|H6Q+n4QpW^V6m#&=WT}=~Qtq7wT{e0u`!P zeDO2TfFcf@<`spOs$DG+LDjgj{S1OpwL#tZozX;CN?}VP3OTO~{YN^|7u@30LM3B^ z76jbmIc-JWiF}{I!@CGWvLE;m_|c0<rWESW-7{!hlW^<04=a!FXGFl%d4tYcvN=i#Ha7s|l z(hVG)4Mb>foM=uC=x|HE-mm#~P^EWyyI~E4p^5^*A6O(VBUIsxJVT5l5+2ut30a`O z;-J8`6jd_&I>HJ&v}sI`y(hIs&bUUMRWy24J;B>8VUa>Tv^LBQHuMH=G(wFGPPpT) zbc7vnU>9y8EIweFcn2;)?icFp5$4%tLmo1b^l>6iyJ5iER~2%_?+o6W7N_JFB7@)%q_( zDOHs|I0266h&$!Pz{E7t@3@Mdx-IPT?caWbX%4dg9_qNLHa<1d4205Ut^`1_3F zR09sc=NEtn+cw-zBA7IFpn|(@L}7c>ae&dZ_qaIRps7C%cqNL!5XW7$#`ar2LjvGq z_<-Di&j%gM(zEL_CGTx|_E?6bGcZLu{I+Ak_z1GYtIm0En_;_xAZ41AVu_cBisSo22U!VgJ&yWD64d_Q}0pilPzV4V+g|`*Ar_3IXrXRpx@w z>7t)PN~E$3PyMcL z2E9<~{`uFuoVY%dkqaqdLY|2X| z|JDnvcZ}aDL6(&bgxsA8gRIad>xL1&4I3`$O16B4djCNpY54(`X36E=!TfE(aS(mB zjc{XuC%@x;%o=nx?5_7=;E^@=S;Jy2g9%@uKr)&mJUVqP7vdTd<8uG%Y4Fj7yh}8a zyZl(|(o}wLS_1S=XO!)R5Or+D@)CbtCgfE^oawM(DP3DMJU!6t2CBm?i9YJ<5By;U zn-U@kIaMM(hydIL^bK6;c@;t?Xp8+F)HpK>X?$8p05=~&!Wcyf-1^%e*fJIRONcsk zn4jS%v@Yr8M_~JvcEkiHb_+w33)#|`roTH1XD80XXj)`HCWmbiY`aaw$_uii3S5}J zh7>qncogP7*)g3;3PkIh34C>k!B3=FGz`^Z%s7`>u%3d;5yr9tIZzdXu3Mi)rB=z% z$>QGi-g=bQ&#fs%U+N#-`s(DfvdJwN)sRZ?av-Y$!@r@*z)J#v6%W`bet}+cK)eq% zqvb=WpN|^Vq5uJhro|Z&(teM?=w*Zpdhmc+wcrl5ZRbb;ui6_htzHSNx^7~Adm-`X zO7-3JK=<9)d~|~^?C30#fBNnDZArK4CQMoo8@l3bfk>JP;LK`lg>DjO8iI>+m-&JY zvl29o`I^AM-GTK7OSM3it!qO}0?9HX?2i!}CiBR&Rwb27?55a|ZRPtT$D3m*3uzdE zPoMLF?Y@4A?0|bYwB-|EMHLzBDG!QJrh~a%+x)!x8vR+`o3;O9gSmY#L8Puj5YGEq zz9hM!aRBH?8d#PCN6}7h-APf_3DE*?zkHZ68b(!1hkod6 zNehNafFeiGDYn4Y`>4H>s~0;hw(-@aZY|H=*mZOYyT5ct)uswD$l&a;-b)X8rvrHX zrU@resge;I#$Lo)Bw%NPwCkh^ZLwALA!|xQ4y6e#Qz7?`|5)7X4$1HxsAbxvn;Dt< z3?0zIhS_zfa_>Bh-ddlXMl7A*SPFq@GhZ_lRQ%f3$N>jl4}RAZ@53tc%O{BsfADYl z{paV~OsZqk*+l**&(}4$S{_L>I`RFs6>_`O)&xAGhaNT@oXZvqZ(r8E+A@Y}1U*($ z$Lye_KI}Zfym~Jpdl=BcU&wk?Y3vwG@TA0QUPPECoH@#QRqWdtGXAF-N1y|=C2KK* zq%U;cD`NJG8jmBHR9m4t# zWhJ{z`Jvga#lR&o?T)_~tC`c6J8kG+4S19!SV_Q_EL8;;wCC1ru`6+lG}?nO`Vlsz z;;S~F4S`T7rS^!aNJ9I9o2KM;cGhw8^_c5Z}dI!Bk zhj<3{DM(y4076XG3^OHu^V0ER#t`ThGg7#F%`UAFfhyv)rV1Z*+!N?Cimlb)Dl}Ax zMjQx=sXEgc@i2u2>sZAz%-M!ag2o^N8C{%P{?ZbJ;wBilIyogeDju{Vn3bKz6l>W# z9TvvkqO~1N>q!qnC^kE=@&B{mY^9tFcjefdSC_t|VHbW;oK4pv0S^eF^09Y^)AJ3N zzR3$of|8<{bX2?0t)JM;h{mqUXcHxe-v% zPMDfk!r^6BhGEID+P5{y$fp&)S8yDB^Xf+9WE)w`O7wIHTlQY>m?0!b910qUQPvOF zvlEIc%;mvK>RVN);vQONeGcj+D?HZ%aTe)H6?Y%czcK#Qj^6sBY+hAsj6{N;Zo$D& zhCdE=KNawv+)!DI|6?qz6;muGpKnnU*q87?G-V2OfN3CTpV`lSFfIwUti?2-h4buD zYPSbFjUQovI*tJu%~0{DnO?a}y0|W&CxsJ$dSNu|Eqc43wOa;FWZ+Zq_ zwzc9I`big!bjutaJgvUXfZz>mRUmjqS%}qAt=LRheC&Dflz?3TwMNe6)`VhD*@E(* z{Gj>V{R^kdX8wRMmTg){Ye*4-aCiit8a13$y*A8*^K2TA53yRj_TK7+SX&jQkebzX zZu9Ra4YCdDN#YBq(xtIsdcU^J^tT%vJyWI&gP6+T*MwP7rmH`(NyES%nA;9!Al0S! zDzG(2Sfo99(#1*mfyx6L8FuL{wY+F zmKTONi~Veuaa>}U;jtCh;)f&@R1H8UjCsGj-$ckS3KzWOfV8;o&l~r6q?iexh_>*R zo#njk2UN3>#|*lo_&J0aadd0Y^90&GFb|?UxshG-S*M_O?zi7|JaUn7?rZw_CD|O1 zlUf|I_w?JwovAjao6xE1Br_HjIm0Myc2xLsq8G2VxJ=m0y>ryLCZte6OyJOI{fY%MmaZFFY7N!as3D-87sz4^_I>;x?g=GW#3=WR6_NtiC+A zUS^PWYM;KdcMHowtb~Y}HyEAKdY>hf;Vm9ZP2owL{56b)ysZJ(Il0AKQLP|Dd?t=7 z83!vbwo5=v33_pT!^?{A_L~Hkxn}RK&!I^1L05!NE9!=s$kC2hh6OKe+JE0km*~{= zq_@L)(TP3WDFZ3MHElUXl3_+!>Xk5iBC3Nga8hpLHl2FCbNp0%nspMe(wNi`5_s`e z(qzkeqLzXv_Se0j0lh~E^QK~gJ7qXNdlDrYh^xB+tbNi$^~f^fnkN)rXlCI2`7L=B zo7CoMgo^5eV0Z>%NfWTRTO$G4-%g%bThWSMW`}8ADY2k&BW(No#Aen|p&J^I4RbTY z1w$Oe)-M-Rj~b<*kQiVh9pw@Ud;X0v%zj$tr!G-eF7^g$GVj%plK;UC8rNM!WY?5C zXfWtDPhIcs^#0bB!LJo%lJJ7UI+$CD2YeB*D9$G&%hud?@JW&twJP~c*CU0G)iD+~ zmF&90$4LOtw;fK3u`#T*T$p3Xt@kB%xI&DYvj=}Mx5)8C3(2X%sijV}D9WxxKX^4s zpM99MPvn8%nsqR<9&Our-&%m4qoxk6YUgSw;|d)0S_e_s-@9tm=g?;Z+f_?Lu+EBY zddBA=)*Ize;}*?ysaiSxu$HRgno{Rp_h>X)IOgUt)tel)I=3Y;{Zik}&XLPCHoNDV z)E1T%=#v#YmgiKL9l&i>1EDL5hRi$pS?=zb#!8S z&GYp8Rtfs)Tu0(7UVF!W_e@p-JNS-EVdT}!fq=egU)3>n+*j&v& zmbg<^=;B2cBrGA9Bp$OatUuF3U})D`Mqv|3w^2Ke>;- z%n6mk5s^ycbbyYIZapt|==q71FrI#3rt=|?ld&R$=iF{EJ)h?2KB=K9Ei`?X)JJ69jfgVos{i}AQe&xIq6^E;jB<~4>}?Oh zoC7!{ZQ9#phZ|!CtL*dZM$F-bC@D# z{8scuKB$I#C*bD2=m(KkMm3fWiAnJK#t*0Pjb?)5MS6cvD|^B(jSb<$E%$+7SfDNN z3UxJIZEcM%q4S~o*d5IM+)kW*el6@7zZy*c+R-`0eQo^3-Ng$-aMFu81!)XNu_jLx z*CaMd6>j4!m#j4HTSKNbd}gkT2zK20mCE2M7d7i*-U1^5MFopo@Z(laQOC=vY?m4t zY}qFhlENy;-VX1*ty#m=K#rJ#2)#`(*(oI6vR1V1>&8g^v1;Ow15SXDd+8&ZjLdK2 ztY0uOO<4nHhw8Ro9r(UyO9jKYr=!0Pb?fPyzzcnPN`~4s7tQ{xCSQLgLG!tl+dkMi z4rF}@C26=vU*6vx(Pbp2^4ne^@Me>=7i%26*5imP!p+B1XB?5sn8fE+idKX@k;a{A zio>)dEOBUf((VQX(brdGjuJ8&Jd2q>o|4lo;UTccAAt2o6O&G3)9Z4=>B?-&YhFC$ zk>9n?Evzz{DqHD#O-N34c&xYaL${w9V$+X2Rm6i%f+l)Qr5~-zGIlwxsmBLcSo!Ap z%Oa2aS&@|;M(#>u%dHARuIYvQ6zr|vw{?)PllE4#stm!(O1^AH=mlQonW}=FNRoXT zIabzzl>wkC#q(mkMxO#ezsghlz`9h`K&7015@t`DeP~Esc4J#QjpbhsLx*Ra2ZMo_ zYwvs1GscL?yKeY`yDa&H_yrq_>cudyc@oxb940F4iAy^9*SQ`v)x~<3nY%_U2fVK( z@x*HcHHRUEHgKYmyvjYBFwLBn5pl|yb8757v_o_ZNMfhT{XQ0;frhZu?q<S0GK`lnufv6ZV4Nj60PRFj5()6YDvHb2-Vc^)VU#jb zIRYuWov8?H(rNG)#JYhSj&W><#rGt;oEWSZ`lI#VFjlA>hrn%7hopK_&kQLw5NCk0 z-13!TkTg?+XM;Vya;>fyt5aHf4+^gBJuw$dPZKOtr<5Z%NvHk=ZW7$ZbrLQf<~ZCt zMJ3T8e*FnFSmn$w^4?L0bjvOutyQ|85@TJ@1JZ0D!s>OirRcEU#WuiJoG!}^uM?!n zMpTkC)^w>ya^mD|#X6Z#Y^n8#r1NDY*4V*Iqi)6pU+LQQ$Kk_mpcfS|UjZG#P0~Ew(^eJE&Hqzax)jJw`je_qt5jBcMB*qH6g5=7HgGmg=!B%Ws)yoem`=2qS zDR2M)!5a)Th=!rwzq74NlPH+gj`F$aO%?AyURfwb3)BNoiFG&qfCPs z`q^JlYc%$8l<7mJshGT!WlCnH&Y50d7~pimLE{J& z&x?Z;a53~jaPyLbu-b5JHFw1KdoYCe(>`;X?1V{Pm86i~=#JpH0ZhMm*^2YouyJ#U z#6bgfMFD26MK^(I;gOubQww1AB`Z40B`$ltM+DaIj_XL>bOXH)Y|EV7jSX#`$h#cQ zvM`VDzf~Si+)baV`>HD(N!Qy*ge=2@ANdN@3}Y#PB9lZWY`A4~(WD3RRC%ZgcArM1 zUVZu=hL9AaTKNVHXV%Xl-hfJ!-+88@HsbpY|7Xhq3ZKC)Q?^Wa0;7TfP)6dqgP`iV zMn(wK;B_-oUkgFlmfEshMBj%E)8a9^Px>ahOK83BcbPdbL=)Z`cy4BQJcKvPMI6oG z{0>Ufn++ol8n-8*J5$=0UGQ3PtGg^4CwsE+yqw!kIkF+Q{h8XYPpXoET_cKN4KFb5 zjAq^In-4^()449#Kd(8B$3dGeB5>`Wim~0IPnLEd#B0KKzgOo3$U88>-YL=%x4K*O zT5Gf|o+zcxw-~PMy$0)&88WNU@j}^SeZA?FocKYQRdy+7)yEuVxy!{xrS<}S89J6; z&PBOOiJf_0d0@^EesQYuf;!?>&?+=C7vKbGeVxqgwh_Wubu#9;R(tW<4AlhL`e{GU zay^6BCj1T{c<7R3KK&jn*caNGM+-lX`623TpTh?2dYG|;B!gMu<5fS)Op#p%62!4-<+3r)$>yZ2BK?pi8nk+&vs*6hm0q!oO% zM8C1?q767jKWYKNFQa2L&K;8YUeSy+g>r7W{#!DiGMufBiW=EvP{86=w!$&M1_j*>&W$=?`}gE13_ z*SA9O22OtCliZ_$z#gW7!>>5&d!E2ZA^IsLUE)$F`b)>K`_kJ>d{i~}lZK%6Sf-1AdU2#WN3YI(74w(6g1hQ^`w~*9 zA`T!ffIjI`7sM7_eKmx8j;s4UU$E0|<2crmZSj>oxgb87 zij@7>SnfM@cIR+!=g*tEQry>zq1hk^Nvh4}cRT6e()-vi$AN@X=rqyG;m=}iF5dUaRNVcqj63tHuK!)uizuv}GyYla=WWDvl$ zv@xXx@=v1Agg`dP%sRL0MIGw(mQgBC@b?5ryVc_ZE73*HyvCUKs$E3HzKpS0Z=zr1 z>+#x#eF$Ekv~_oOdw@Oh^-gU*4^D>}{4FV0L=O=gGZYKkUJ3nmXn7GEN%e96y@g-n zh`7;|sChHjuyA@n=JU?IY#j0K>pswawm$b&xV875^4vLLJ@an*jjK67mqXYdXGUk}T}7P*{p|EcV;{8KmlPYc)nepdc(MQi`PaIHzzQw3QK zD_0T#va8XiMVh|d2v+w0p`dC}r@8|dK_jetQ z%|c&uF|ry!l(58-`6B-#$||U1JCN!wSN06QA#a^N>wxFc{_J=Eq@LGQPFdRTb_XV0HnGOL-x5NRVFU6+SdeV z8aY{jt`u4H!Bn7;Cb)ytLCwukU7DxpS&#= z;=3(-l8bq^ZItJ}NT}!S5(*dJA2v%7>!}Bn&@INd_5rjFc*UnQThr{|h`bRFb7{%% z(AK+KSqr5O9ORvmqXUfrJE+^QEW!vc#`jg`(k!eI!Q+x>=Y*IC(K>T* zixg%0piY3~MS!P4ls8PYOaMGB=oEpgD&E!bvyF=Et*JCQ~gr zg60pMXc7!DeslslPJVz%V4ZMVBaE=HgcLTBi5NNLjcti)<1wj-#c@XNVWh)-f0iCW zPRdmBYt`BkVsP;unJO03N4GmOC#}n#PpXUdVI#r7pcB@;p7EEH5`U!pWUlo_E_9pg z=sKC@7W2r%m#hC!_F=vLyTX`0h{EQ9imfVKJwK_s-}+M5 zRpr3A+IHqDt+~6!+}v@sD>Ol#t^{N{`*0x*>34YsZo3_c?A(fPw?X^wYT54(*MR}( zl=WVV9w$3D$$GOlUFVX@5M|-~RQgJ;4bycbQ4zkdqFDDFm`&MO94MI&5r;ey!&jQv zVN63#Xm($`gZmLnaw#ZujmVYkHk~aNyK7o%6TA)k_j8DamT#!G#SDCnCWjr+=Qya2 zdM`m;4e8i2OEY2fi=+rnT4~|Wp}Q(>rv-WD>%COwW7+7{h3sXOFUuL%jWZYaMa|sKjiu#{ z&r?_5?k-2!-!*a}BsH_!Zx<)oF*@4)eFy0y7B%LP9nDsWp35kPOF5o8kv#UZcSC$s z9EWdx^FIqJg4ypn*h!pO z$m2UEK1_8Xdvbk@e^+52(>9Gq_49iV#(FJSOO8X0_24)j%S`ZHN0#g`XIG3?_b<{z zftgXY#n`r&+Q4En7j-Gtog5s}@KoP>?XS;2Wq%Hj4|kS3E(lc_*NEYBE_}yUKYUMn zI_ZABOx#4zMLeqMY|pj64JGE9u5`ZTTSi^Am12~` zU>&d~HVNooRgwz)R2Q$DBEg;3jxSj#{kHd2QCTm~o9=Fq?(S5&q`L*_Mp^^`rCYii z=@JnHDd|p;R6(U%2?;^?y)PKRl`HtWe|){?J;&oYcb?hV-PzgMndftEs*rw66|hnJ z*A!U-A|>GOT$}%|DL+rog7`y@xwVnGp|QgW!3uD8{*|WXycqKHVp#5I$lO)rmRB)j zwG-ztl9JHpa^Wz&BnixU>2#Nrewrf&7&l%pgkO9XH^}r@7$HGGxS&8lIIaeAOh?1O z@`DEA0{#Ja6-xzlb&OCg%XLuFHwLQVq!>YMKB&Uc8L)Z?0eKAR#6np2v#E@riNj-Kt_}I77m%3B+ zp6~tM3+c^CQ9m)whUxV-zK3yNK=FV4 z(gTzp9cC7Wig3SQVAqA5=i&k(DJ%Z*2k)oT!pbzTf<88x{@@H!EN z;-l0?+`vHU5#0VWVRph@J+2R*D3Dq|g;NPVV)sBx$9R z5_PTOS~PW<`q_>#aS0a}Pq84Cl=*1LybT@6v7Hb3*Z9iW!lX ziBxBNx}9aC_W@* zY|sBV@w~s{xquwaXIAwc#5aVYyu3E!I2c$;A7*J?H%9C-tRD9J=T@h(i!WGw4Jzm1Hjtjq}y*CJ!WsgO2gf#z0b2K97ZyVyi@l{I>o-= zu7HGQ!MixZSt7}>+gv-VkqMc0-A<_~s~hgFvennEi0?W~+*Q_m_=yTUz@_~2HUm*} znE>At70UfNtA#YqSIuToMo`;EDvr2s{3;gXWLW!3G-ek$g~|&R7Tr>(|g z^hqc%b$-X8^NxT2eV!)64ijD=LTk_5r>@aO6|U{lg;ZMIxXB#pGOU-=4tXNCWA`Fn z$9_HVZrz7YwKgyOw6?pRmS&B8XUV00L?kg_g4caR&Y_ZZCf;!*e_~a`;+C~%2MrS@ z3lD+T$6F37>s3nja=Tv)d_r7>pkOyt8K6MRhHnBM2UOUD8gszQl zI)9|F`kXbHwK1$W+JsX0J+?`sOYTes&$moWGs(5SHLEAjDz;A#XoSrag5F!AmCEJB zof~>ij(TS)z@gwlQ1qOGvFtOc62)S-=PX6r7W8;SgBp=JHq=Y6jpN8?w3DZJ!b``F zM0MVOx}*4z$<0}QYbJ8X>7kw4GPEznwcOEEov2FZ65B|5-G!IYFXmp0jcOxIWlW`Y zGD-%P?aB0FT6TTehP~|#tCVKxS!HCqBKe-Q-zC!)HY`-`gXS}Wq@n6fij*SGTZBeL zo`d%xHUjkgmKnl5U^1yNJLtez)V%e$`4yk2bd1miz6cM{Q^HO|#g;Eo$kdvQuCqm6Mz3YM9qrkI?=_) z6y+tIGxJ}bFbtQLf8{d<5q&XO1Q23L0Di((g|Rnw1n`uZ+gLML8S6V3JN%+#x!}M# zrY!ph?+|=RuNm+V1hQsyE9UvPLPI`loJT?9)5*FY& zyfLVG_Z~oXcfgP9s_LH{l0UhUuA=iBxD%aYZm?}CDbY!-n;G#lfq=Ievyk{fe$ z_?<^7Pyay+^ z5E%_EZI7dJASm#6F62922+7Gh0QRYd9uUwu1ek^cB1FMfo`IPof`Rx0u$;jCg?-#p zvkg>kkn2`D+wn|;fbjplatv&YjsUCtQ9nhc=y>(ysI)$Sk_asZPlWaeHC-YwJ$+-= z6;Ub7I66stZo+}eP^Fyxgv@# z`{~=xZ-UXw-*$MI{)gGAjI0bgd$4Q(>{PLE89{%;PPKZo2mc(^9MWK6OgCU9#sG}Z zdR4?5qn9Fo^wQ|@_;hY4OBEfAD;FT6DyJ zvaBFgSqK*B-70w|WbAtw0o-rx@-klKF?RZPmC*_Ha)JB8s)Un)70}=;MDpgz;umHx zM)Ie#>_Mu4Ht(nRke=GW!1XF#ylc__w`ODckImMPAP{D&_LAxHvDLVfo~Wb14rGd)y_UWq zl;>w6%wSvx5u*$0C<&($UJOUejZCfqs$V882uUC~BBJ{2rXb}0s}LrSE&=~8*9>1I z6#;(|9}`xvaTTzSwn7tq+#io=Bf)W;Y$bJ}&Y7-<9A2*PZ%xPcADfN|2e9G>m1vJ` zcH?uqNY7K|0ImUP8fnjUnA(gIGUHR!|GtPG1Xy2Ot@^cQpX(G~KI+ zSE2WiIuXFpm$C6m!9^f^Go7>ai4R;KctSL+ep!_Wq>iAMLX6dfLgND!E@R=@@$B@C zyTsGd>0WHl-5*z4RmGuY9)*jaNf zRnD7&6hT13)p&&7U6KRsc$C77go;vJU8FHdTOoKpcs{c21B1Gj$1?~OaPVc$Z0M*@ z`yLP0M)tf@QCYQW-OCp1%r7$M$!^8f7QCaN=_CTqCxXt$CKnz%71{C*?6wFS892DI zh_VBjAv&2!g)?~FOu330v1&5xM+~+uk>+;Z7v%NS9IXs&98}q34E5AprJ_t+0u(Xb zogCcc{M-%0!@kE$7C`}}k82(LoZV9epGo)r?TTgnk5_Cz?#X69!wp#q*z8$?JRAyd zh{TkEM6U%-#LSaCv~MK#>=KJ zg$TBZKx)2DPy8xGsVWFrFAPaAfUgCDYQp9V*x=(3U#M^Ksqo{QnGp_AR#+;SN@U_C z0-VSm>JnERkO5Z^QEzuh?y_%)VyCJ`-Hk zH#2aRfLB2RZ#d5nk0Vf)8wYWK#i)dbndS&7H6JO{$Cf1D!BjUXB3X-Mu2CO zVQD!!KZX1v!GdEBYyQh~SbLS*^opJHVPkkyE`NP!{?`=Oa0s>Z_s40fj-CEKFyfO+ z2V5<_5(S!E}1Psxvd6aF#$39z$(x=%CyPy-&2u+0|m{i4m+tLAhhqHV|Kt?N&r_AaN31qCq$j(x$Kp4K0YR_9`E>T!?FLjhb9jb@J0qWB7rwD z!2MzKOn~evJnY*%D=WCM*Qu}v2?&2N_9A8S_If_0Z@3FeE7KBN#p30qgD2*IAp>}fN8tbRDtl51b-`7(S54OJ z+D(kq1f6d~j<1KzSNy8^^Q;#eE7YD*-140o8V}&M_YQ~|Lxe0H3~k~Zd{Qe=@QyM) zR@h90CM)+at6dn}-0YJt3LIZF#C>xKVYzScyU!|xWvBZjYPKcbJa>>tM{Y?<*_o_- zU%RvYXwF*IKTWf|Tcep~J^Xgzaz&2BGYW80-NVDf)@#d;9_pG+)YlxjeL-fvS(~=% z+@G^qU7%^A#%1yB%XOpxVlZ`qk5%aN>tyshzh8Q)vj|~ zot6JoV`X>B&tEmCYB4#?SsHouI z5MLw)EqD?_e4E03?a%n!E5sI)Kj7)g1O8Y-Q+e)`QCaC>xukAC3w@EnZuWw>_a5^) z7}%f?;bOIi`qo~gNPX&GHWIu%?Le<_C@Xbi6*nO%WDvcI`SQx<=;c=b*M(t$`aJ`( zy>G=OT!>Z%nVvDQNei&rF|hTl0^AThYxFR7@>tw3F3z!qPJmK~8O6fjF(zYUZ?w0g zvORRuayikfdVq2flHB-3R*& zBG#|#L1s1-^|5XM6>1;-p5B)U4IR$jN&+FS=rOv|(k$Lsl=hX^z3&2zEDFeFgDh`u zhT6VMzK)re7BY%dZ`YDsI+Y-7PGKE`m!l)aEnLkKr5cBAdDGU4*N>x0r4^!w)ryzU z0m8C46xLCF$#DJyN^v6D*65w!=rtJn{1DAvhHRz9X7k0}XUXw*Ay$hQ**=Xqy<;8O z+8WA_k$KE$5=OBRIKG%gHYPhODtw=z{+)c6ebdXVF)%mB^+~9=Uq}}>qwI&_vy5INEwvwcwFS7=c$76sXAiRGUXM?DgZPk^tMum6`6~OS6 zWx)*b$~aWJ1X3e@wZb7TpAX@ctidNap&jKMsx`_tYdM)u4nun1c=w~bf*sL799E|1 zMa054q8rc~9gHqBJ%@kgDHFWvS(~ov_`0NlxCv7gGJRFZBemZ!lVAojQ^fSypqe7n z7a{8$o%}pmYA=abErnvTRJb0uK4jh=Qd{189PIgkkk>~sLxXE5(W(ejvSet$&;^mt z*ZP`q52zD}XfUExe8H3Tu*KpvcNAgfnZ`|I&3J#~(p#YSOgmpEv4s#H9c6bsz#D7_ zJaqL((an#XRCoM(;O>3S%wq_+=%fRF0#^(ib(wWpbY<=u8e1E4GwU)l8adrLjvz1a zL&x{@#qS@^_z;nWSj?aYKZ{F5Mg9x0%3MH%eK z;NVozox4OZdK)1Kc?|?@yI~N=Ar!hmuH6vcIk&1$`X-AU;8wvA{=%&SQ(b>>t2zPK zR54OrO*f{W-N0C7+9wO4gL~U%H<2SNR*{so_(bO}N2y&fjtLvr+i^;^&I6;U=Te`= zLXfo{3S#Y3!%g*zq_y~ydQk_wgr(aO>Qd4k+9ih6ubjM5^}27S5}Co7IJv57-fgF5 zKG}6G<&dJ@W~VkrjP^QxTe+}enFk2{24k%-J4i@VO9N;DYZxy2fZtf`^XXkzYY znk6Zr!9ra0MM!7$Sdy9ndcI%{d_L$@eXj)8K}0d-s+w|IrR;3pNO$f!B;I>TfmJ$K z-_XX=C`{FYJCH$ljLw$j8s7w+z#C>vunmjA7U7YChb|PKohfx`zIxYfJsT`*r)~ z*HlO;%CY3CT{x=EqP}E4U`Y}=pOruxybzVv_(8ZdVT9naLhsLy%d&oge%dFruqrUA z*s|O{97yzNG)m>+6w=f!fs!K;HzY?3O^1RIS_0RRtZRlxUu|@&S+sozV(C zb)VX^+35?O(>pVQXLm{qbsnHZZnV~e76b+$E!gGgBzM}bWIl;XhjIe5c6xa5dDXhf z+6LOm!G6A_`WrMAqt43xlvk_I$xtYrFvaCt*LV0phF%coy<(GojvU9MV;s5Eo>)8< z%RMZ~mBO`z&V=@1zgFxchIvUr!)xoT9n*VHs^Pas&^=b9){YQ}T^)6ZWUIcwcE0aB zOg?y~ax-sE`;h+$a{Oi`_)-TtqicJ;ruIFQ@R}&S2Nm#mMp}?#mTWywDsX8<7?wY) zq}h)nI67E!Y=yw`-D{-=51f~54H>}Wj%4Q`=9sBzm0a=M#X^xc&39;)C!Y@}Duq#Mq?c3`m36F56KS;R!H0jKebc804HG zL)dq3>pZ&#(9DT7E;RYh=cUB>AhAALw&0JB^z7H9sp1Nz$b3KJ(&?KcKB#(1`lO96 zxNL7ji(O6fem>%M`dnYT?)&o~?WvClZ*D1^8L@|HRiC{n6Jh{lnM( ztDohZ^ZQSpxPi7(Y%-q7;05?rn{?hce5uLYHs-i3n-ZRtk^BwRyTq{yxOR$%xdHNoPKOc&(>fIU$b9?g~P7<1qQGcyOH}JhN0zVZC5d z%O>KM0f*uROi<+uur#T{Q0}4KYD{+IWCr=62wBLaF7j{=Slh3vDvKeUbi`i>mG{Es zv-EcO!q&QpMu0*$@={peVj8@YeY=+#^Ggxx(3X$UNQ>26rufh-I-62yJS&9QhetSO z#E&gdKwf~{UNmEHPD0n|p_^|IS)hAFewTt|u^AP85!PBPmE^V=4PTHmzB(dgIzfyOHh-d}gP?TxrBS!ubX~!c64IYGM*)>|jm> z{;#3wG4nWe>`4ctZwJwD@Pw~d`^X3fQCv@F2>pn_6Nh-`?WV3=lM9pO#7F=4H#Ky1 zGRp4s;MQniWlk^q$GJ4i>u4hgy(>eXc!S%ptcsGu$G@;UYpX5ks4Bg8*rQNJBj|!` z+1ChN^I&!%*#nKEaWJJh%t)167m9=nYxg8@`wFbd_qCH98tgJriad>xTs&vjgy54@SZoy~r2OoJUNdN#VHH?p7q}a!*3`d^ z#QXYadg4p&S2UeF%>*M=3i9bW^WhI@OY29|o;7tkJ#=k)e_si2>=x1Uavb6Lux$d= z0!ZGM46OCOCd%>|7LE87kuV}mFYYJq(rOz_)E@0mxK>yn9-@T%bax+Ngui2bpNH^G zM|hh%=^&&wQ+I|21p$m}7L1HlJ7WYBY+)}>uhosP+-kN5_HfDjd9XEgj7eRY-v(KT zj3oc8mC$Hve7jmtY5VJxEQ67nw}S~R(qAZEnnd^^x!vzw6H&L3IXd?M+a^_sa|Cc@ zUI1sn{uiMmqlxvec=4hf)y#KirVISc-aiAySK;0~{A|9Sfe{}CFqk`` zvamewLe5|VK0K9;T{DY4OBuV6?MZ_a(1=C(A{sL;C)-$!8ISpCJ9aKs+x#r>qi9Wt zao&seo$^T1Jv6ed*43bLcbTVgae17lPZ!4|m>D2;U?0Eh^fY|%#_extK-G=aQb|ro zYZoTFrM!t2Wv`^Hk$|JA-&Tf#%hFQqr2v8EL?JC{EJO<^6z~3UmsFRR`EXEbN?FN4 z2~ck{&V%F8iHd-ekE3&(SI6sCSVb8?IOlb%;LDYx0frX)8?Os|$@!DlmB$c5SzFW8 z6Kli{f|z}3Fa=Mb2t9!F*qWG_x`VV$l&V>-1a~53JS9~OMV3?CFa&$3B`P&wUEcYF zb*(cCKN@mkKx6zjP`s7Y#fVh*Mo@IR1V?%GVRcm;g1Z_iDKk$<(J2B ze94};^wB{5DBmbHP?~xnxw}?iCuc9 z`YPtoUj(_u9Zqz_bjkUdk=ZI~+rA&Vx!eoYr*O0}RJCcHCB81*Fv*V1o*?p%nRF zb1#5&0Z9Uf?eNxvOWYu4BapQ8dH2+c&z^oT5qec?nWRcp;GyKJcBmpF!mYat&GSgN zyS7ZB(046t2&utSx@6sYZSk`W4>}|Wwb{ifsRkFz68a<^R03xj4`Dc;uaoD5ho-bz zH6}qW+f|h05B>jGg5Um1Fw1WmYS4>d>RLDn-8m8g$qcrt+k8`&s zuvRb|>z|~jZecL!P%Mk zzeinnNW8?eCEW07MUgzDJ-u!_4_ml{NI`zl)2UW2d$Sfh!4Q@%*(_vOyV&X-2rYk` z7ng~@Y255Fwk$KrHSdU)!ayo-wzrzs-#TSrqf?Fdto7|Y-0VH-cdG%{9h7zLpNo_b zs%r{J>@46B3kvXqv30Gj*!s}iKQ7?ExvYEblZjR%h&UJ4z&Bx0S-GG^{6U&zI^WUp z4j+XQ5(4uwiKUK@52{u_UQb{3vAGjIu*T@oEUY4KPLq?RQin$Mc^y_03?JfJxgB(S zgbC!M!fFz_(Jy%g#-f>Q#&;jkK5j?MnOCB5A%ts)8+auntVjZ48+8y)@Vx3x(25sb zUylEPZd!+hz4#Co8q^@`JAnkhBH>}2Ad^r+k{FJ@wyct#p!=b~7 zX!WMt#gsN}96nRd?=h#=pN_89Zj(ds_ONCPymyt0PrTz(_mW1o%@wx<%KHPBo2e$} z(Q+l>a>tFkVl!E#kLV8}-`R3YLQ4f}NJb(Hc1(aBgmNRp-!q2}EvcM*M(fD_)lWy+ zSvv;P8>^*H(Zx%IzMamnw@X^t7cNC4mTtosVQ*F~Gz(t$Zp##PoL&^tjF4s-bTiqV zHSa>(=JtZ=u!9GpLj>tdUnvn(MU;dLG3FoP-K!F9u-hRq^SH4(0wRN>r)-1e@Se8} zhrR6alm7ff3|>Mj*gzBMxB*sovimCa7+A`Na~%UuF>X8*QX*wxzkWx&b;ynqPaYnR>(>>U!mI6l;$+LX9a?O< zzAs8ka_8vLGYh5i9$~sk;qB-3SrF|wwFJ7;`H=`}LgLKY&zA~Fwp66s)P?3|b;hJ( zFeX2Qp^T|=3g2??C6*(>N z!@d^GCZRwNHuX2BFr9!tw8!pTQ7JI1?HW8OaWz|T6%xKy#=+zk4~;D=s2fJ+lKpu- z-oeG<=dCsQN7de6*ip!r$)yKYm!O zSFPU?T?k_`VS-c9iov3=^}V?-sp_c3YjGdpYnjh6y#uA>^?T)3-xzVFOcWQZAwYgf zCtZ+*I06^?Ozey*PYOc_Atto_A>%Qjs0yz{%6QM4adJz0R5z5lQP=k`KlS!-;X23M zsxcYrBPICKB_jS(Z>$tfN!@C&RlNGl+ZBU^CcB&)qJ!CgGt2U-Y zNLDW*qbBykNmrcuAd$Qk2dWU=lurEuYeNe}FrRAcsZ&2G%Sc$+_QLd&Tku6pMpX5y zxeeDeuDjE8H%Q2uQRgPYwwB|-ma*=H8(weP7}N^!absouyyer!@V`u$ELE$GPo?JP((NGo7~@O@k~&5I`WB<>vXcG z=2Q&t^$wZ~>B)iWpS>4`84@_Dzmi2dftp2^YafuViBzF@uO*Rz80^ujU;$iW{HlgB zoe4B|$?*!d3j22*sS5U-tmd=BXsQ}=cdO=8A{O~!f_r#}vKc?*ig>*@NUq0zPdp3@ z1yQdGp2`1oa%BlLdI{3|t_MdDsM%~nvNho?0knU z@8qpswxr>_ze_Np$}Unz%XkwxY`tiDfGVgoR(~tjI__GpwcG3$TK-8WO&V(SlBQ?S z*A5MY$8>ACMO5t?U^{~S1!EJy0`ltrOdRVWoLoSC!*xYB6 za(W!cv2xqQ2=DpiHA+O%YhTSb$w%mYbYQd|)%#$3QiJUJnWfA7#IjTCRre1Mi_*&G zgP_)uWJ4iBz@p``OYH2AX4Fi=e5i#Hj6!}T5VSYl;_||A2)S&VRkx}%f3vngbI)teWALoZy zgE;l-e3rdEMATap#0dS> zq0disOWdn&>Z|W}y2uZ|AB!+1Tx{>BBBEBh6=Q0tQGB?zzK)zda`V7>V{@6U{@jg= zIft|)6W|0C|0W0kKCu2F2Ik;tyijl*Xq!@jQfV%H|znT3*aQew-99hD-2kT(J)+1%7PwxS$bHo1GH z$Mw_BgHOG#Y;VhqhVb#_TAv2K6`n5^9h6GY(zvT4*xp^@AU|1>H7aiuD?pIQ_0pTL zpiZDj+>N$8cxD@<=P#TiNoHG zYe_xInzD1p^YhRpah76XfB`uGzdf(zTRf&ZEb(z_AtUlWBd?EVdSv+*AB%o!qzd$f zv={n{8Zgv4f(HHHbZCte#xuG_0so*4^FYzMT!*3fT>Oc7T&(Jhi zOXA18Q|dslvlTAzMwx&fe3A#Tsq1%B7jDi;jI;CRmoVI<=6ABU7~dK-41M16;MCdL zMqM3UR^MUW2cI;v-!^W_FhE`GZWZ??A(+M^m<}~IWay`QjOL$~q0#_NyoKS#XPb7z zix^%ng-TuXDa9>K5A>V4Yn_rY;We?votgs-T;+L)y%~O`mfn41I2@7)VcYrJ3hac7 zHZ{dcQEcFWmc7_-)^`IqSh@P{o-(_P-zFJ0h@LyX{)kOZhnJ%=M$ZmqLh%LBcX0;^hV3Ot&kPC0y_`|#ox!I0GGV7CH$v1! zqa@3;K**Jh=}Bl`H!aZ+|0w_02$?w@cUs73t&VyO$iViZ^aI84>1MpPjo_kI;NJG% zi@|!*gjnCfIDBv*(&-a-pq^O^X063HU-@inTRg(wqrGaPx3kD1SU{Wy=IJ+wXiqec z1v0+r>ecsAtW%-d1Y&KOLuo`(pUL(`3cbt_6&I*OobJy$rgasPXr<52y5FfQWGqQ3 z_u#x?7v3MnC%=^ZMN_*z&wXc(>ugNmzTXJ<a`My3dof{Lf~(s^EXQ7L1Ec4^RvCT3SxpSE2PUYQa1qDrnAS-3%P-GyZyqvHfRu=VAI; z&icQ&!_2w>JFMvY4htetp}oQmgYrj*u}ynd0&~vtylwCGMF4wD8t}VvT>{7Xx`hAm zt*o%Y5C>WLn$7oOT<*iYB-V4fM@T$Y)duLD{-5anD@P8jtN2Y5ALEs3Do*q#vH?+P z==)3rhm#J{Xe5edVk8JTBC+)G1F%Ysc^$iIXZ&33nfsXq{;#wBfC>2zv;Bn)W*v?A zX$egYEggU%#2bMt1vgZKZ3F`vJO_g(HiGTZrMCtfFrWbylIYr9LDH`WS?O>PamJ>D zmEos1H*j%ra6bBVH#%0$Q2A5LMduiBO@QQHk6#Z7!LMD16icWI6pLA>$QP@UVK0T6 z2|gHnJfmWw_qj2|RPRFolQaWuUr)bbMnrMGOQ0lAK24EKND=e2W+(Z$ZuW2M`)OVfeKLxd2-x-@W^=KgTvUroiG1j z&XBvWsllaYqM#_v%F19W?c%7ea#QBsC3!jQ&YfQWV9vNe`suD_r$2SE0zqI#lAXfA zKtM16_mzJzXZ$_nmKKrd%*KrKFgnXuGw5yMWtr4F~7Y zg6?F(YKG#HG!()U;s}v3xd(NVwUodxsQm)*m!x|-W$5L6;qj9+qIKbv(9+ZsujwX7 zXhbU&JsubUs~F>{C*~+2Flj51OmLye$GqPumLEtVSiDXnNWt%CA`S(W{BH29AtaoY&Z_9t_9xbn1F~HVO_3ekK+!4v^-kYs3ZV zp~C?DPI;&rVJ0>vo?LbcRVF}VpqZ_ZqJ@@)qQ!0d0czqZtqNxq;c8d`_e{!n+;!|HqL7_Ipc zC_qQt{1KeDodkB)nW^&{aO;~PKY&2#t&t9) z6TQWHaT!JpdJ*1;OT1ma>27Ip9sVn<&f!HOCJDdOC{ zdF`HI7C|X>#)tL%7u_L)PqqQi(6T{^O7&24{jaMJmtV{vRUUMH3CtER!<4sGXJrAD zDD?MyE<;QtfACyLc!syROU-s|G3n`jd8z>sPNm?jcuZM{Uc$Q-6YA3|uhXn*q z=^{c22ycRUH8I%NZi0Ekb@xp4bUV27)rE9FJY1vcf-Zo7vT-xHt#w%PzPL4}l_I~Y z%N^Jb&@dEx3q?0NJ}&iZVKFzI!puuV;sUN&}V5d4+0BzqU8u2aH z7BFY9MOt@+%>}}9WMGmauF)yF*!ug3l-Sq=#ewv)tt-D z!@)G%bRdA?!G3TPoR~$NJw;6~a~uLlJmWAG#ikTh2EW0!DTRY*WRDJDtJw@ff2}l) zE-k$=&89ph&FY{WERE&>AMc3hA{~Ub8RW3>dKyS+)8a+@>_Bj{T$UkA3$sbD2(GywUH^3?Zx4WctEYq0c2FfGY-wq`H~`&wB;aL*WeR{c=b%Q_>NA5unt`Ip~|g-O5=$hsi%?_k*bA^5bM~yi(;bkaKgx5 zrc7Sgs2Gg`DqF;aK98MMgqCDj4NYGgRrUu;9dc$cc)!?C@AoNGDe#MgX% ziqH`ujN7r*HglaFe%TAksJvfb$13ubMdY<|M|)Pz8V1(3cFs-aW{pmk3Pcz{v8ltm_!yB@!4B>Rw zbZ;1l{Sx*zJ2MlBB(sHOqE>NZ!MXqmVZ^knB880#UhNh63rJx{s1Hw4SLTW}5p#)K zOdI4}dMj?^!M8PPhfY16+)flLjc&Gdy@Mr+=4w2uy!z=A+vx0b*nq+(ai9)y8Cv}W z_nXq+7_7M~gi(dAJk0*O!MOS9!yzA8>M9DKC@yt1p}ZNR3y81?F*r7?q6hM=rj4)q z?r-ny=BnD*d*$)yW?!?&)!NSQN_pZ`@X{RLE+|!he+0))ySc)x8Z~~8hocr$wpeXe zF9w|Gb|?EFwOw1#H0Bg*(6W9Ob^d)V1wI&)7cfKPG|>-3yPP_2?`H0K2`SLo>2(Qv zaeRa*&vV!f7H*Hs>am-__gzkjly!)77-FuLW712{hk1~=Tx+DvHQ+Xw=u5?BH#2Gw z#Zr0~34QaCW&lm6GD)*>PuD2XzQG&3Pm^B~!8WxB-fMxwDAZSdRRBe)%lxDl{EpBP zi?8vNN;L~Yzzc$C?RqqX8I&9 z9lG$pIQSfI&9Knmt;Vw(9p6kD-UulIZB~(#!O`3Tn+yTk6jC-TXxws_DxoUIew-VY zBb@QYj7H-#k}y^fUAo%C4PGoA>2mbXC?;6gl=#i`*P|f2%v8kF z;3L1sn|+^L@O1PEtdN)<-DYjfp5MU2HlF_r;o6$1@!n_F5TBdS^U2n-RuoxXnw++_ z9_oAl?f!I~0ZPN{!Bdt893WbNAK?YkejXGo$H5O!F7Wre?1SRJw8KaB0FqRVEsY?c zJ$jK5R7?UC953mg=W(+MgiSPoz|b=w16#jU+Z6C})?iOXht>c!0owiibCH_ojA;W< z5%@c&rh+sm7$Nvc+TOsu1w6M*5EW0Bb^mfCFD8{zcUJ8z{mVQ35|i z{k(1hB>pCYbE4eIWfLe-baJ{5R3*dEp>b zx#Up*F<1cM=x2nd0{-QA|?%t@&X7!CsI7tqcKegH^+ofiy@{{Ac% zI8`BX=`-=aRd^h5K%bTSz3Utx_%}Dy)0O~kfR7{ zxrwoZ<9AX)fcW3+gwx`Ideg^JrNw;L`>Z{zAkT`|b^K}P3)kDPRK$7@_2VC5H|iO+ zWdY>8r_~0?{ch$zhy_}Ma5BPE5umOn;D>oeY%bv26Q{)jiIgvG3E+DEls3k11Tg6X zfS|KW?k3Dx(I-m|5IbFNX%Nf?5`SO>0cqwxFL)Oax1H8mm*soy*PoXLaPH22a^r`4 z0Pa8v-gAP1b9bWfg+@McJLkPP))^xg0UmiyD}36mU2eUde65JA))D{@oxyl)nJ!^m%Y0;IAEjmVEp44CG%8@FbIWLdAXcu~MEHtGLYN z#tKa4ULA|!3|7viv94&@vuWMamPG_KC?4;}KTIVv(|5=VlDz*m^j|Z4mOJ*e9z9pp z<8-$6-@s3H4lrE^aQHv}FrBe`x!h%q@FSslGTFajSv`F^0zuD`yPh5~0U$8FoaKLl z{`kvDIu$VLODJ%b!1c5O)qk(R&y?xE>F|5Y)1R@tvBG7I@FN%cZ-~DZ+F6#?)1&Ub zBI0+>RsigUXUH$uvmB+TVMPJU?{c$!GSDCC*vsLbCHXv!>#BT3-0zg1m%}~Fka-%n zLgk9M-}y2xhkKS^@-*&e)hptjuuWbL_AI^PX;?ErkN1_P_dD6*<#5lk6Q0J+(6}P* zcdo+A;htspJB>RI2%oPs-V?6BKf?lj=2>#P)3Azwd(2f}f8HC}jt}QQxJzKrbC!ke zG&Z@;)v$H{9DM$A9A`PmPQ#|$zB26bJIbA=vpKawn zjm&CyMda^|-Iv2X+d_H<_wE&Ozc-X#4)<*9+8JDzE8<=$nQ^vT<}@xB;9C_}IW_<> z-xYky*|Pl8xZ<8y#62m}|2H%LJ`uqAGpGjUZW_F<274|CbBUY!+4}U;LuU5A8n*69 zwfese`PV6VwmkbZY@W{*VNZ&+F9&U0oK?T4y)F#Bes9;aX)obNm&H15*+0Ai9jCX?d}-;l zW%I8Jd2!n7!lD22TW8A>PwQb8dc~oiOM6`ieq#53PkSkZT^9UD;_1?9ud{W7r$=0M zb;%AT=$UBqf&+<}=~Sxk z4{w3LciCsdzUqXl;hqco{@_Z^KkH%q3^{?%S+t4YvHwx30XT^M@Q4hQe6~>Iv^AYC yko void 0); +} + +describe('migration v2', () => { + let esServer: kbnTestServer.TestElasticsearchUtils; + let root: Root; + + beforeAll(async () => { + await removeLogFile(); + }); + + afterAll(async () => { + if (root) { + await root.shutdown(); + } + if (esServer) { + await esServer.stop(); + } + + await new Promise((resolve) => setTimeout(resolve, 10000)); + }); + + it('migrates the documents to the highest version', async () => { + const migratedIndex = `.kibana_${pkg.version}_001`; + const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + // original SO: + // { + // type: 'foo', + // foo: {}, + // migrationVersion: { + // foo: '7.13.0', + // }, + // }, + // contains migrated index with 8.0 aliases to skip migration, but run outdated doc search + dataArchive: Path.join(__dirname, 'archives', '8.0.0_migrated_with_outdated_docs.zip'), + }, + }, + }); + + root = createRoot(); + + esServer = await startES(); + const coreSetup = await root.setup(); + + coreSetup.savedObjects.registerType({ + name: 'foo', + hidden: false, + mappings: { properties: {} }, + namespaceType: 'agnostic', + migrations: { + '7.14.0': (doc) => doc, + }, + }); + + const coreStart = await root.start(); + const esClient = coreStart.elasticsearch.client.asInternalUser; + + const migratedDocs = await fetchDocs(esClient, migratedIndex); + + expect(migratedDocs.length).toBe(1); + const [doc] = migratedDocs; + expect(doc._source.migrationVersion.foo).toBe('7.14.0'); + expect(doc._source.coreMigrationVersion).toBe('8.0.0'); + }); +}); + +function createRoot() { + return kbnTestServer.createRootWithCorePlugins( + { + migrations: { + skip: false, + enableV2: true, + }, + logging: { + appenders: { + file: { + type: 'file', + fileName: logFilePath, + layout: { + type: 'json', + }, + }, + }, + loggers: [ + { + name: 'root', + appenders: ['file'], + }, + ], + }, + }, + { + oss: true, + } + ); +} + +async function fetchDocs(esClient: ElasticsearchClient, index: string) { + const { body } = await esClient.search({ + index, + body: { + query: { + bool: { + should: [ + { + term: { type: 'foo' }, + }, + ], + }, + }, + }, + }); + + return body.hits.hits; +} diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts index bffe590a39432..7a87a645a249c 100644 --- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts +++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts @@ -296,38 +296,35 @@ describe('migrationsStateActionMachine', () => { }, }, "unusedTypesQuery": Object { - "_tag": "Some", - "value": Object { - "bool": Object { - "must_not": Array [ - Object { - "term": Object { - "type": "fleet-agent-events", - }, + "bool": Object { + "must_not": Array [ + Object { + "term": Object { + "type": "fleet-agent-events", }, - Object { - "term": Object { - "type": "tsvb-validation-telemetry", - }, + }, + Object { + "term": Object { + "type": "tsvb-validation-telemetry", }, - Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "match": Object { + "type": "search-session", }, - Object { - "match": Object { - "search-session.persisted": false, - }, + }, + Object { + "match": Object { + "search-session.persisted": false, }, - ], - }, + }, + ], }, - ], - }, + }, + ], }, }, "versionAlias": ".my-so-index_7.11.0", @@ -396,38 +393,35 @@ describe('migrationsStateActionMachine', () => { }, }, "unusedTypesQuery": Object { - "_tag": "Some", - "value": Object { - "bool": Object { - "must_not": Array [ - Object { - "term": Object { - "type": "fleet-agent-events", - }, + "bool": Object { + "must_not": Array [ + Object { + "term": Object { + "type": "fleet-agent-events", }, - Object { - "term": Object { - "type": "tsvb-validation-telemetry", - }, + }, + Object { + "term": Object { + "type": "tsvb-validation-telemetry", }, - Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "match": Object { + "type": "search-session", }, - Object { - "match": Object { - "search-session.persisted": false, - }, + }, + Object { + "match": Object { + "search-session.persisted": false, }, - ], - }, + }, + ], }, - ], - }, + }, + ], }, }, "versionAlias": ".my-so-index_7.11.0", @@ -584,38 +578,35 @@ describe('migrationsStateActionMachine', () => { }, }, "unusedTypesQuery": Object { - "_tag": "Some", - "value": Object { - "bool": Object { - "must_not": Array [ - Object { - "term": Object { - "type": "fleet-agent-events", - }, + "bool": Object { + "must_not": Array [ + Object { + "term": Object { + "type": "fleet-agent-events", }, - Object { - "term": Object { - "type": "tsvb-validation-telemetry", - }, + }, + Object { + "term": Object { + "type": "tsvb-validation-telemetry", }, - Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "match": Object { + "type": "search-session", }, - Object { - "match": Object { - "search-session.persisted": false, - }, + }, + Object { + "match": Object { + "search-session.persisted": false, }, - ], - }, + }, + ], }, - ], - }, + }, + ], }, }, "versionAlias": ".my-so-index_7.11.0", @@ -679,38 +670,35 @@ describe('migrationsStateActionMachine', () => { }, }, "unusedTypesQuery": Object { - "_tag": "Some", - "value": Object { - "bool": Object { - "must_not": Array [ - Object { - "term": Object { - "type": "fleet-agent-events", - }, + "bool": Object { + "must_not": Array [ + Object { + "term": Object { + "type": "fleet-agent-events", }, - Object { - "term": Object { - "type": "tsvb-validation-telemetry", - }, + }, + Object { + "term": Object { + "type": "tsvb-validation-telemetry", }, - Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "match": Object { + "type": "search-session", }, - Object { - "match": Object { - "search-session.persisted": false, - }, + }, + Object { + "match": Object { + "search-session.persisted": false, }, - ], - }, + }, + ], }, - ], - }, + }, + ], }, }, "versionAlias": ".my-so-index_7.11.0", diff --git a/src/core/server/saved_objects/migrationsv2/model.test.ts b/src/core/server/saved_objects/migrationsv2/model.test.ts index 57a7a7f2ea24a..213e8b43c0ea0 100644 --- a/src/core/server/saved_objects/migrationsv2/model.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model.test.ts @@ -21,9 +21,12 @@ import type { ReindexSourceToTempRead, ReindexSourceToTempClosePit, ReindexSourceToTempIndex, + RefreshTarget, UpdateTargetMappingsState, UpdateTargetMappingsWaitForTaskState, - OutdatedDocumentsSearch, + OutdatedDocumentsSearchOpenPit, + OutdatedDocumentsSearchRead, + OutdatedDocumentsSearchClosePit, OutdatedDocumentsTransform, MarkVersionIndexReady, BaseState, @@ -72,7 +75,7 @@ describe('migrations v2 model', () => { versionAlias: '.kibana_7.11.0', versionIndex: '.kibana_7.11.0_001', tempIndex: '.kibana_7.11.0_reindex_temp', - unusedTypesQuery: Option.of({ + unusedTypesQuery: { bool: { must_not: [ { @@ -82,7 +85,7 @@ describe('migrations v2 model', () => { }, ], }, - }), + }, }; describe('exponential retry delays for retryable_es_client_error', () => { @@ -214,7 +217,7 @@ describe('migrations v2 model', () => { }, }; - test('INIT -> OUTDATED_DOCUMENTS_SEARCH if .kibana is already pointing to the target index', () => { + test('INIT -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT if .kibana is already pointing to the target index', () => { const res: ResponseType<'INIT'> = Either.right({ '.kibana_7.11.0_001': { aliases: { @@ -227,7 +230,7 @@ describe('migrations v2 model', () => { }); const newState = model(initState, res); - expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH'); + expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'); // This snapshot asserts that we merge the // migrationMappingPropertyHashes of the existing index, but we leave // the mappings for the disabled_saved_object_type untouched. There @@ -888,78 +891,120 @@ describe('migrations v2 model', () => { sourceIndex: Option.some('.kibana') as Option.Some, targetIndex: '.kibana_7.11.0_001', }; - it('CLONE_TEMP_TO_TARGET -> OUTDATED_DOCUMENTS_SEARCH if response is right', () => { + it('CLONE_TEMP_TO_TARGET -> REFRESH_TARGET if response is right', () => { const res: ResponseType<'CLONE_TEMP_TO_TARGET'> = Either.right({ acknowledged: true, shardsAcknowledged: true, }); const newState = model(state, res); - expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH'); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + expect(newState.controlState).toBe('REFRESH_TARGET'); + expect(newState.retryCount).toBe(0); + expect(newState.retryDelay).toBe(0); }); - it('CLONE_TEMP_TO_TARGET -> OUTDATED_DOCUMENTS_SEARCH if response is left index_not_fonud_exception', () => { + it('CLONE_TEMP_TO_TARGET -> REFRESH_TARGET if response is left index_not_fonud_exception', () => { const res: ResponseType<'CLONE_TEMP_TO_TARGET'> = Either.left({ type: 'index_not_found_exception', index: 'temp_index', }); const newState = model(state, res); - expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH'); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + expect(newState.controlState).toBe('REFRESH_TARGET'); + expect(newState.retryCount).toBe(0); + expect(newState.retryDelay).toBe(0); }); }); - describe('OUTDATED_DOCUMENTS_SEARCH', () => { - const outdatedDocumentsSourchState: OutdatedDocumentsSearch = { + describe('OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', () => { + const state: OutdatedDocumentsSearchOpenPit = { ...baseState, - controlState: 'OUTDATED_DOCUMENTS_SEARCH', + controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', versionIndexReadyActions: Option.none, sourceIndex: Option.some('.kibana') as Option.Some, targetIndex: '.kibana_7.11.0_001', }; - test('OUTDATED_DOCUMENTS_SEARCH -> OUTDATED_DOCUMENTS_TRANSFORM if some outdated documents were found', () => { - const outdatedDocuments = ([ - Symbol('raw saved object doc'), - ] as unknown) as SavedObjectsRawDoc[]; - const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH'> = Either.right({ - outdatedDocuments, + it('OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT -> OUTDATED_DOCUMENTS_SEARCH_READ if action succeeds', () => { + const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'> = Either.right({ + pitId: 'pit_id', }); - const newState = model(outdatedDocumentsSourchState, res) as OutdatedDocumentsTransform; - expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_TRANSFORM'); - expect(newState.outdatedDocuments).toEqual(outdatedDocuments); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + const newState = model(state, res) as OutdatedDocumentsSearchRead; + expect(newState.controlState).toBe('OUTDATED_DOCUMENTS_SEARCH_READ'); + expect(newState.pitId).toBe('pit_id'); + expect(newState.lastHitSortValue).toBe(undefined); + expect(newState.retryCount).toBe(0); + expect(newState.retryDelay).toBe(0); }); - test('OUTDATED_DOCUMENTS_SEARCH -> UPDATE_TARGET_MAPPINGS if none outdated documents were found and some versionIndexReadyActions', () => { - const aliasActions = ([Symbol('alias action')] as unknown) as AliasAction[]; - const outdatedDocumentsSourchStateWithSomeVersionIndexReadyActions = { - ...outdatedDocumentsSourchState, - ...{ - versionIndexReadyActions: Option.some(aliasActions), - }, - }; - const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH'> = Either.right({ - outdatedDocuments: [], + }); + + describe('OUTDATED_DOCUMENTS_SEARCH_READ', () => { + const state: OutdatedDocumentsSearchRead = { + ...baseState, + controlState: 'OUTDATED_DOCUMENTS_SEARCH_READ', + versionIndexReadyActions: Option.none, + sourceIndex: Option.some('.kibana') as Option.Some, + pitId: 'pit_id', + targetIndex: '.kibana_7.11.0_001', + lastHitSortValue: undefined, + hasTransformedDocs: false, + }; + + it('OUTDATED_DOCUMENTS_SEARCH_READ -> OUTDATED_DOCUMENTS_TRANSFORM if found documents to transform', () => { + const outdatedDocuments = [{ _id: '1', _source: { type: 'vis' } }]; + const lastHitSortValue = [123456]; + const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH_READ'> = Either.right({ + outdatedDocuments, + lastHitSortValue, }); - const newState = model( - outdatedDocumentsSourchStateWithSomeVersionIndexReadyActions, - res - ) as MarkVersionIndexReady; - expect(newState.controlState).toEqual('UPDATE_TARGET_MAPPINGS'); - expect(newState.versionIndexReadyActions.value).toEqual(aliasActions); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + const newState = model(state, res) as OutdatedDocumentsTransform; + expect(newState.controlState).toBe('OUTDATED_DOCUMENTS_TRANSFORM'); + expect(newState.outdatedDocuments).toBe(outdatedDocuments); + expect(newState.lastHitSortValue).toBe(lastHitSortValue); }); - test('OUTDATED_DOCUMENTS_SEARCH -> UPDATE_TARGET_MAPPINGS if none outdated documents were found and none versionIndexReadyActions', () => { - const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH'> = Either.right({ + + it('OUTDATED_DOCUMENTS_SEARCH_READ -> OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT if no outdated documents to transform', () => { + const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH_READ'> = Either.right({ outdatedDocuments: [], + lastHitSortValue: undefined, }); - const newState = model(outdatedDocumentsSourchState, res); - expect(newState.controlState).toEqual('UPDATE_TARGET_MAPPINGS'); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + const newState = model(state, res) as OutdatedDocumentsSearchClosePit; + expect(newState.controlState).toBe('OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT'); + expect(newState.pitId).toBe('pit_id'); + }); + }); + + describe('OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT', () => { + const state: OutdatedDocumentsSearchClosePit = { + ...baseState, + controlState: 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT', + versionIndexReadyActions: Option.none, + sourceIndex: Option.some('.kibana') as Option.Some, + pitId: 'pit_id', + targetIndex: '.kibana_7.11.0_001', + hasTransformedDocs: false, + }; + + it('OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT -> UPDATE_TARGET_MAPPINGS if action succeeded', () => { + const res: ResponseType<'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT'> = Either.right({}); + const newState = model(state, res) as UpdateTargetMappingsState; + expect(newState.controlState).toBe('UPDATE_TARGET_MAPPINGS'); + // @ts-expect-error pitId shouldn't leak outside + expect(newState.pitId).toBe(undefined); + }); + }); + + describe('REFRESH_TARGET', () => { + const state: RefreshTarget = { + ...baseState, + controlState: 'REFRESH_TARGET', + versionIndexReadyActions: Option.none, + sourceIndex: Option.some('.kibana') as Option.Some, + targetIndex: '.kibana_7.11.0_001', + }; + + it('REFRESH_TARGET -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT if action succeeded', () => { + const res: ResponseType<'REFRESH_TARGET'> = Either.right({ refreshed: true }); + const newState = model(state, res) as UpdateTargetMappingsState; + expect(newState.controlState).toBe('OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'); }); }); + describe('OUTDATED_DOCUMENTS_TRANSFORM', () => { const outdatedDocuments = ([ Symbol('raw saved object doc'), @@ -971,17 +1016,21 @@ describe('migrations v2 model', () => { sourceIndex: Option.some('.kibana') as Option.Some, targetIndex: '.kibana_7.11.0_001', outdatedDocuments, + pitId: 'pit_id', + lastHitSortValue: [3, 4], + hasTransformedDocs: false, }; - test('OUTDATED_DOCUMENTS_TRANSFORM -> OUTDATED_DOCUMENTS_SEARCH if action succeeds', () => { + test('OUTDATED_DOCUMENTS_TRANSFORM -> OUTDATED_DOCUMENTS_SEARCH_READ if action succeeds', () => { const res: ResponseType<'OUTDATED_DOCUMENTS_TRANSFORM'> = Either.right( 'bulk_index_succeeded' ); const newState = model(outdatedDocumentsTransformState, res); - expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH'); + expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH_READ'); expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); }); + describe('UPDATE_TARGET_MAPPINGS', () => { const updateTargetMappingsState: UpdateTargetMappingsState = { ...baseState, @@ -1236,38 +1285,35 @@ describe('migrations v2 model', () => { }, }, "unusedTypesQuery": Object { - "_tag": "Some", - "value": Object { - "bool": Object { - "must_not": Array [ - Object { - "term": Object { - "type": "fleet-agent-events", - }, + "bool": Object { + "must_not": Array [ + Object { + "term": Object { + "type": "fleet-agent-events", }, - Object { - "term": Object { - "type": "tsvb-validation-telemetry", - }, + }, + Object { + "term": Object { + "type": "tsvb-validation-telemetry", }, - Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "match": Object { + "type": "search-session", }, - Object { - "match": Object { - "search-session.persisted": false, - }, + }, + Object { + "match": Object { + "search-session.persisted": false, }, - ], - }, + }, + ], }, - ], - }, + }, + ], }, }, "versionAlias": ".kibana_task_manager_8.1.0", diff --git a/src/core/server/saved_objects/migrationsv2/model.ts b/src/core/server/saved_objects/migrationsv2/model.ts index 2097b1de88aab..318eff19d5e24 100644 --- a/src/core/server/saved_objects/migrationsv2/model.ts +++ b/src/core/server/saved_objects/migrationsv2/model.ts @@ -189,10 +189,10 @@ export const model = (currentState: State, resW: ResponseType): ) { return { ...stateP, - // Skip to 'OUTDATED_DOCUMENTS_SEARCH' so that if a new plugin was + // Skip to 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT' so that if a new plugin was // installed / enabled we can transform any old documents and update // the mappings for this plugin's types. - controlState: 'OUTDATED_DOCUMENTS_SEARCH', + controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', // Source is a none because we didn't do any migration from a source // index sourceIndex: Option.none, @@ -574,51 +574,100 @@ export const model = (currentState: State, resW: ResponseType): if (Either.isRight(res)) { return { ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH', + controlState: 'REFRESH_TARGET', }; } else { const left = res.left; if (isLeftTypeof(left, 'index_not_found_exception')) { - // index_not_found_exception means another instance alread completed + // index_not_found_exception means another instance already completed // the MARK_VERSION_INDEX_READY step and removed the temp index - // We still perform the OUTDATED_DOCUMENTS_* and + // We still perform the REFRESH_TARGET, OUTDATED_DOCUMENTS_* and // UPDATE_TARGET_MAPPINGS steps since we might have plugins enabled // which the other instances don't. return { ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH', + controlState: 'REFRESH_TARGET', }; } else { throwBadResponse(stateP, left); } } - } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_SEARCH') { + } else if (stateP.controlState === 'REFRESH_TARGET') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + return { + ...stateP, + controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', + }; + } else { + throwBadResponse(stateP, res); + } + } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + return { + ...stateP, + controlState: 'OUTDATED_DOCUMENTS_SEARCH_READ', + pitId: res.right.pitId, + lastHitSortValue: undefined, + hasTransformedDocs: false, + }; + } else { + throwBadResponse(stateP, res); + } + } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_SEARCH_READ') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { - // If outdated documents were found, transform them if (res.right.outdatedDocuments.length > 0) { return { ...stateP, controlState: 'OUTDATED_DOCUMENTS_TRANSFORM', outdatedDocuments: res.right.outdatedDocuments, + lastHitSortValue: res.right.lastHitSortValue, }; } else { - // If there are no more results we have transformed all outdated - // documents and can proceed to the next step return { ...stateP, - controlState: 'UPDATE_TARGET_MAPPINGS', + controlState: 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT', }; } } else { throwBadResponse(stateP, res); } + } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_REFRESH') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + return { + ...stateP, + controlState: 'UPDATE_TARGET_MAPPINGS', + }; + } else { + throwBadResponse(stateP, res); + } + } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + const { pitId, hasTransformedDocs, ...state } = stateP; + if (hasTransformedDocs) { + return { + ...state, + controlState: 'OUTDATED_DOCUMENTS_REFRESH', + }; + } + return { + ...state, + controlState: 'UPDATE_TARGET_MAPPINGS', + }; + } else { + throwBadResponse(stateP, res); + } } else if (stateP.controlState === 'OUTDATED_DOCUMENTS_TRANSFORM') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { return { ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH', + controlState: 'OUTDATED_DOCUMENTS_SEARCH_READ', + hasTransformedDocs: true, }; } else { throwBadResponse(stateP, res as never); @@ -813,7 +862,7 @@ export const createInitialState = ({ retryAttempts: migrationsConfig.retryAttempts, batchSize: migrationsConfig.batchSize, logs: [], - unusedTypesQuery: Option.of(excludeUnusedTypesQuery), + unusedTypesQuery: excludeUnusedTypesQuery, }; return initialState; }; diff --git a/src/core/server/saved_objects/migrationsv2/next.ts b/src/core/server/saved_objects/migrationsv2/next.ts index 6d61634a6948e..536c07d6a071d 100644 --- a/src/core/server/saved_objects/migrationsv2/next.ts +++ b/src/core/server/saved_objects/migrationsv2/next.ts @@ -20,7 +20,6 @@ import type { LegacyReindexState, LegacyReindexWaitForTaskState, LegacySetWriteBlockState, - OutdatedDocumentsSearch, OutdatedDocumentsTransform, SetSourceWriteBlockState, State, @@ -33,6 +32,11 @@ import type { SetTempWriteBlock, WaitForYellowSourceState, TransformRawDocs, + OutdatedDocumentsSearchOpenPit, + OutdatedDocumentsSearchRead, + OutdatedDocumentsSearchClosePit, + RefreshTarget, + OutdatedDocumentsRefresh, } from './types'; import * as Actions from './actions'; import { ElasticsearchClient } from '../../elasticsearch'; @@ -67,6 +71,10 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra Actions.readWithPit( client, state.sourceIndexPitId, + /* When reading we use a source query to exclude saved objects types which + * are no longer used. These saved objects will still be kept in the outdated + * index for backup purposes, but won't be available in the upgraded index. + */ state.unusedTypesQuery, state.batchSize, state.lastHitSortValue @@ -83,9 +91,8 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra * Since we don't run a search against the target index, we disable "refresh" to speed up * the migration process. * Although any further step must run "refresh" for the target index - * before we reach out to the OUTDATED_DOCUMENTS_SEARCH step. - * Right now, we rely on UPDATE_TARGET_MAPPINGS + UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK - * to perform refresh. + * before we reach out to the OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT step. + * Right now, it's performed during REFRESH_TARGET step. */ false ), @@ -93,31 +100,40 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra Actions.setWriteBlock(client, state.tempIndex), CLONE_TEMP_TO_TARGET: (state: CloneTempToSource) => Actions.cloneIndex(client, state.tempIndex, state.targetIndex), + REFRESH_TARGET: (state: RefreshTarget) => Actions.refreshIndex(client, state.targetIndex), UPDATE_TARGET_MAPPINGS: (state: UpdateTargetMappingsState) => Actions.updateAndPickupMappings(client, state.targetIndex, state.targetIndexMappings), UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK: (state: UpdateTargetMappingsWaitForTaskState) => Actions.waitForPickupUpdatedMappingsTask(client, state.updateTargetMappingsTaskId, '60s'), - OUTDATED_DOCUMENTS_SEARCH: (state: OutdatedDocumentsSearch) => - Actions.searchForOutdatedDocuments(client, { - batchSize: state.batchSize, - targetIndex: state.targetIndex, - outdatedDocumentsQuery: state.outdatedDocumentsQuery, - }), + OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT: (state: OutdatedDocumentsSearchOpenPit) => + Actions.openPit(client, state.targetIndex), + OUTDATED_DOCUMENTS_SEARCH_READ: (state: OutdatedDocumentsSearchRead) => + Actions.readWithPit( + client, + state.pitId, + // search for outdated documents only + state.outdatedDocumentsQuery, + state.batchSize, + state.lastHitSortValue + ), + OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT: (state: OutdatedDocumentsSearchClosePit) => + Actions.closePit(client, state.pitId), + OUTDATED_DOCUMENTS_REFRESH: (state: OutdatedDocumentsRefresh) => + Actions.refreshIndex(client, state.targetIndex), OUTDATED_DOCUMENTS_TRANSFORM: (state: OutdatedDocumentsTransform) => - // Wait for a refresh to happen before returning. This ensures that when - // this Kibana instance searches for outdated documents, it won't find - // documents that were already transformed by itself or another Kibana - // instance. However, this causes each OUTDATED_DOCUMENTS_SEARCH -> - // OUTDATED_DOCUMENTS_TRANSFORM cycle to take 1s so when batches are - // small performance will become a lot worse. - // The alternative is to use a search_after with either a tie_breaker - // field or using a Point In Time as a cursor to go through all documents. Actions.transformDocs( client, transformRawDocs, state.outdatedDocuments, state.targetIndex, - 'wait_for' + /** + * Since we don't run a search against the target index, we disable "refresh" to speed up + * the migration process. + * Although any further step must run "refresh" for the target index + * before we reach out to the MARK_VERSION_INDEX_READY step. + * Right now, it's performed during OUTDATED_DOCUMENTS_REFRESH step. + */ + false ), MARK_VERSION_INDEX_READY: (state: MarkVersionIndexReady) => Actions.updateAliases(client, state.versionIndexReadyActions.value), diff --git a/src/core/server/saved_objects/migrationsv2/types.ts b/src/core/server/saved_objects/migrationsv2/types.ts index 50664bc9398fb..ac807e9d61776 100644 --- a/src/core/server/saved_objects/migrationsv2/types.ts +++ b/src/core/server/saved_objects/migrationsv2/types.ts @@ -42,7 +42,7 @@ export interface BaseState extends ControlState { readonly tempIndexMappings: IndexMapping; /** Script to apply to a legacy index before it can be used as a migration source */ readonly preMigrationScript: Option.Option; - readonly outdatedDocumentsQuery: Record; + readonly outdatedDocumentsQuery: estypes.QueryContainer; readonly retryCount: number; readonly retryDelay: number; /** @@ -101,14 +101,14 @@ export interface BaseState extends ControlState { * are no longer used. These saved objects will still be kept in the outdated * index for backup purposes, but won't be available in the upgraded index. */ - readonly unusedTypesQuery: Option.Option; + readonly unusedTypesQuery: estypes.QueryContainer; } -export type InitState = BaseState & { +export interface InitState extends BaseState { readonly controlState: 'INIT'; -}; +} -export type PostInitState = BaseState & { +export interface PostInitState extends BaseState { /** * The source index is the index from which the migration reads. If the * Option is a none, we didn't do any migration from a source index, either: @@ -121,20 +121,20 @@ export type PostInitState = BaseState & { /** The target index is the index to which the migration writes */ readonly targetIndex: string; readonly versionIndexReadyActions: Option.Option; - readonly outdatedDocumentsQuery: Record; -}; + readonly outdatedDocumentsQuery: estypes.QueryContainer; +} -export type DoneState = PostInitState & { +export interface DoneState extends PostInitState { /** Migration completed successfully */ readonly controlState: 'DONE'; -}; +} -export type FatalState = BaseState & { +export interface FatalState extends BaseState { /** Migration terminated with a failure */ readonly controlState: 'FATAL'; /** The reason the migration was terminated */ readonly reason: string; -}; +} export interface WaitForYellowSourceState extends BaseState { /** Wait for the source index to be yellow before requesting it. */ @@ -143,27 +143,27 @@ export interface WaitForYellowSourceState extends BaseState { readonly sourceIndexMappings: IndexMapping; } -export type SetSourceWriteBlockState = PostInitState & { +export interface SetSourceWriteBlockState extends PostInitState { /** Set a write block on the source index to prevent any further writes */ readonly controlState: 'SET_SOURCE_WRITE_BLOCK'; readonly sourceIndex: Option.Some; -}; +} -export type CreateNewTargetState = PostInitState & { +export interface CreateNewTargetState extends PostInitState { /** Blank ES cluster, create a new version-specific target index */ readonly controlState: 'CREATE_NEW_TARGET'; readonly sourceIndex: Option.None; readonly versionIndexReadyActions: Option.Some; -}; +} -export type CreateReindexTempState = PostInitState & { +export interface CreateReindexTempState extends PostInitState { /** * Create a target index with mappings from the source index and registered * plugins */ readonly controlState: 'CREATE_REINDEX_TEMP'; readonly sourceIndex: Option.Some; -}; +} export interface ReindexSourceToTempOpenPit extends PostInitState { /** Open PIT to the source index */ @@ -197,37 +197,67 @@ export type SetTempWriteBlock = PostInitState & { readonly sourceIndex: Option.Some; }; -export type CloneTempToSource = PostInitState & { +export interface CloneTempToSource extends PostInitState { /** * Clone the temporary reindex index into */ readonly controlState: 'CLONE_TEMP_TO_TARGET'; readonly sourceIndex: Option.Some; -}; +} -export type UpdateTargetMappingsState = PostInitState & { +export interface RefreshTarget extends PostInitState { + /** Refresh temp index before searching for outdated docs */ + readonly controlState: 'REFRESH_TARGET'; + readonly targetIndex: string; +} + +export interface UpdateTargetMappingsState extends PostInitState { /** Update the mappings of the target index */ readonly controlState: 'UPDATE_TARGET_MAPPINGS'; -}; +} -export type UpdateTargetMappingsWaitForTaskState = PostInitState & { +export interface UpdateTargetMappingsWaitForTaskState extends PostInitState { /** Update the mappings of the target index */ readonly controlState: 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK'; readonly updateTargetMappingsTaskId: string; -}; +} -export type OutdatedDocumentsSearch = PostInitState & { +export interface OutdatedDocumentsSearchOpenPit extends PostInitState { + /** Open PiT for target index to search for outdated documents */ + readonly controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'; +} + +export interface OutdatedDocumentsSearchRead extends PostInitState { /** Search for outdated documents in the target index */ - readonly controlState: 'OUTDATED_DOCUMENTS_SEARCH'; -}; + readonly controlState: 'OUTDATED_DOCUMENTS_SEARCH_READ'; + readonly pitId: string; + readonly lastHitSortValue: number[] | undefined; + readonly hasTransformedDocs: boolean; +} + +export interface OutdatedDocumentsSearchClosePit extends PostInitState { + /** Close PiT for target index when found all outdated documents */ + readonly controlState: 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT'; + readonly pitId: string; + readonly hasTransformedDocs: boolean; +} + +export interface OutdatedDocumentsRefresh extends PostInitState { + /** Reindex transformed documents */ + readonly controlState: 'OUTDATED_DOCUMENTS_REFRESH'; + readonly targetIndex: string; +} -export type OutdatedDocumentsTransform = PostInitState & { +export interface OutdatedDocumentsTransform extends PostInitState { /** Transform a batch of outdated documents to their latest version and write them to the target index */ readonly controlState: 'OUTDATED_DOCUMENTS_TRANSFORM'; + readonly pitId: string; readonly outdatedDocuments: SavedObjectsRawDoc[]; -}; + readonly lastHitSortValue: number[] | undefined; + readonly hasTransformedDocs: boolean; +} -export type MarkVersionIndexReady = PostInitState & { +export interface MarkVersionIndexReady extends PostInitState { /** * Marks the version-specific index as ready. Once this step is complete, * future Kibana instances will not have to prepare a target index by e.g. @@ -239,9 +269,9 @@ export type MarkVersionIndexReady = PostInitState & { */ readonly controlState: 'MARK_VERSION_INDEX_READY'; readonly versionIndexReadyActions: Option.Some; -}; +} -export type MarkVersionIndexReadyConflict = PostInitState & { +export interface MarkVersionIndexReadyConflict extends PostInitState { /** * If the MARK_VERSION_INDEX_READY step fails another instance was * performing the migration in parallel and won the race to marking the @@ -256,13 +286,13 @@ export type MarkVersionIndexReadyConflict = PostInitState & { * start a new migration to the latest version. */ readonly controlState: 'MARK_VERSION_INDEX_READY_CONFLICT'; -}; +} /** * If we're migrating from a legacy index we need to perform some additional * steps to prepare this index so that it can be used as a migration 'source'. */ -export type LegacyBaseState = PostInitState & { +export interface LegacyBaseState extends PostInitState { readonly sourceIndex: Option.Some; readonly legacyPreMigrationDoneActions: AliasAction[]; /** @@ -270,44 +300,44 @@ export type LegacyBaseState = PostInitState & { * target index. */ readonly legacyReindexTargetMappings: IndexMapping; -}; +} -export type LegacySetWriteBlockState = LegacyBaseState & { +export interface LegacySetWriteBlockState extends LegacyBaseState { /** Set a write block on the legacy index to prevent any further writes */ readonly controlState: 'LEGACY_SET_WRITE_BLOCK'; -}; +} -export type LegacyCreateReindexTargetState = LegacyBaseState & { +export interface LegacyCreateReindexTargetState extends LegacyBaseState { /** * Create a new index into which we can reindex the legacy index. This * index will have the same mappings as the legacy index. Once the legacy * pre-migration is complete, this index will be used a migration 'source'. */ readonly controlState: 'LEGACY_CREATE_REINDEX_TARGET'; -}; +} -export type LegacyReindexState = LegacyBaseState & { +export interface LegacyReindexState extends LegacyBaseState { /** * Reindex the legacy index into the new index created in the * LEGACY_CREATE_REINDEX_TARGET step (and apply the preMigration script). */ readonly controlState: 'LEGACY_REINDEX'; -}; +} -export type LegacyReindexWaitForTaskState = LegacyBaseState & { +export interface LegacyReindexWaitForTaskState extends LegacyBaseState { /** Wait for the reindex operation to complete */ readonly controlState: 'LEGACY_REINDEX_WAIT_FOR_TASK'; readonly legacyReindexTaskId: string; -}; +} -export type LegacyDeleteState = LegacyBaseState & { +export interface LegacyDeleteState extends LegacyBaseState { /** * After reindexed has completed, delete the legacy index so that it won't * conflict with the `currentAlias` that we want to create in a later step * e.g. `.kibana`. */ readonly controlState: 'LEGACY_DELETE'; -}; +} export type State = | FatalState @@ -325,8 +355,12 @@ export type State = | CloneTempToSource | UpdateTargetMappingsState | UpdateTargetMappingsWaitForTaskState - | OutdatedDocumentsSearch + | OutdatedDocumentsSearchOpenPit + | OutdatedDocumentsSearchRead + | OutdatedDocumentsSearchClosePit | OutdatedDocumentsTransform + | RefreshTarget + | OutdatedDocumentsRefresh | MarkVersionIndexReady | MarkVersionIndexReadyConflict | LegacyCreateReindexTargetState diff --git a/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts b/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts index 599c32137c553..186962b568792 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts @@ -14,6 +14,7 @@ import { schema as s, ObjectType } from '@kbn/config-schema'; * Currently supported: * - filter * - histogram + * - nested * - terms * * Not implemented: @@ -32,7 +33,6 @@ import { schema as s, ObjectType } from '@kbn/config-schema'; * - ip_range * - missing * - multi_terms - * - nested * - parent * - range * - rare_terms @@ -42,6 +42,7 @@ import { schema as s, ObjectType } from '@kbn/config-schema'; * - significant_text * - variable_width_histogram */ + export const bucketAggsSchemas: Record = { filter: s.object({ term: s.recordOf(s.string(), s.oneOf([s.string(), s.boolean(), s.number()])), @@ -71,6 +72,9 @@ export const bucketAggsSchemas: Record = { }) ), }), + nested: s.object({ + path: s.string(), + }), terms: s.object({ field: s.maybe(s.string()), collect_mode: s.maybe(s.string()), diff --git a/src/core/server/saved_objects/service/lib/aggregations/validation.test.ts b/src/core/server/saved_objects/service/lib/aggregations/validation.test.ts index 8a7c1c3719eb0..57421db76f5b6 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/validation.test.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/validation.test.ts @@ -16,6 +16,12 @@ const mockMappings = { updated_at: { type: 'date', }, + references: { + type: 'nested', + properties: { + id: 'keyword', + }, + }, foo: { properties: { title: { @@ -182,6 +188,40 @@ describe('validateAndConvertAggregations', () => { }); }); + it('validates a nested root aggregations', () => { + expect( + validateAndConvertAggregations( + ['alert'], + { + aggName: { + nested: { + path: 'alert.references', + }, + aggregations: { + aggName2: { + terms: { field: 'alert.references.id' }, + }, + }, + }, + }, + mockMappings + ) + ).toEqual({ + aggName: { + nested: { + path: 'references', + }, + aggregations: { + aggName2: { + terms: { + field: 'references.id', + }, + }, + }, + }, + }); + }); + it('rewrites type attributes when valid', () => { const aggregations: AggsMap = { average: { @@ -428,4 +468,50 @@ describe('validateAndConvertAggregations', () => { `"[someAgg.aggs.nested.max.script]: definition for this key is missing"` ); }); + + it('throws an error when trying to access a property via {type}.{type}.attributes.{attr}', () => { + expect(() => { + validateAndConvertAggregations( + ['alert'], + { + aggName: { + cardinality: { + field: 'alert.alert.attributes.actions.group', + }, + aggs: { + aggName: { + max: { field: 'alert.alert.attributes.actions.group' }, + }, + }, + }, + }, + mockMappings + ); + }).toThrowErrorMatchingInlineSnapshot( + '"[aggName.cardinality.field] Invalid attribute path: alert.alert.attributes.actions.group"' + ); + }); + + it('throws an error when trying to access a property via {type}.{type}.{attr}', () => { + expect(() => { + validateAndConvertAggregations( + ['alert'], + { + aggName: { + cardinality: { + field: 'alert.alert.actions.group', + }, + aggs: { + aggName: { + max: { field: 'alert.alert.actions.group' }, + }, + }, + }, + }, + mockMappings + ); + }).toThrowErrorMatchingInlineSnapshot( + '"[aggName.cardinality.field] Invalid attribute path: alert.alert.actions.group"' + ); + }); }); diff --git a/src/core/server/saved_objects/service/lib/aggregations/validation.ts b/src/core/server/saved_objects/service/lib/aggregations/validation.ts index a2fd392183132..cd41a23f4a28b 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/validation.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/validation.ts @@ -56,10 +56,13 @@ const validateAggregations = ( aggregations: Record, context: ValidationContext ) => { - return Object.entries(aggregations).reduce((memo, [aggrName, aggrContainer]) => { - memo[aggrName] = validateAggregation(aggrContainer, childContext(context, aggrName)); - return memo; - }, {} as Record); + return Object.entries(aggregations).reduce>( + (memo, [aggrName, aggrContainer]) => { + memo[aggrName] = validateAggregation(aggrContainer, childContext(context, aggrName)); + return memo; + }, + {} + ); }; /** @@ -93,15 +96,18 @@ const validateAggregationContainer = ( container: estypes.AggregationContainer, context: ValidationContext ) => { - return Object.entries(container).reduce((memo, [aggName, aggregation]) => { - if (aggregationKeys.includes(aggName)) { - return memo; - } - return { - ...memo, - [aggName]: validateAggregationType(aggName, aggregation, childContext(context, aggName)), - }; - }, {} as estypes.AggregationContainer); + return Object.entries(container).reduce( + (memo, [aggName, aggregation]) => { + if (aggregationKeys.includes(aggName)) { + return memo; + } + return { + ...memo, + [aggName]: validateAggregationType(aggName, aggregation, childContext(context, aggName)), + }; + }, + {} + ); }; const validateAggregationType = ( @@ -143,7 +149,7 @@ const validateAggregationStructure = ( * }, * ``` */ -const attributeFields = ['field']; +const attributeFields = ['field', 'path']; /** * List of fields that have a Record as value * diff --git a/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts b/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts index f817497e3759e..0b2cc8e235c9c 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts @@ -24,15 +24,17 @@ export const isRootLevelAttribute = ( allowedTypes: string[] ): boolean => { const splits = attributePath.split('.'); - if (splits.length !== 2) { + if (splits.length <= 1) { return false; } - const [type, fieldName] = splits; - if (allowedTypes.includes(fieldName)) { + const [type, firstPath, ...otherPaths] = splits; + if (allowedTypes.includes(firstPath)) { return false; } - return allowedTypes.includes(type) && fieldDefined(indexMapping, fieldName); + return ( + allowedTypes.includes(type) && fieldDefined(indexMapping, [firstPath, ...otherPaths].join('.')) + ); }; /** @@ -45,7 +47,8 @@ export const isRootLevelAttribute = ( * ``` */ export const rewriteRootLevelAttribute = (attributePath: string) => { - return attributePath.split('.')[1]; + const [, ...attributes] = attributePath.split('.'); + return attributes.join('.'); }; /** diff --git a/src/fixtures/telemetry_collectors/nested_collector.ts b/src/fixtures/telemetry_collectors/nested_collector.ts index 61642ea94e59d..812b7d7f452dc 100644 --- a/src/fixtures/telemetry_collectors/nested_collector.ts +++ b/src/fixtures/telemetry_collectors/nested_collector.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CollectorSet, UsageCollector } from '../../plugins/usage_collection/server/collector'; +import { CollectorSet, Collector } from '../../plugins/usage_collection/server/collector'; import { loggerMock } from '../../core/server/logging/logger.mock'; const collectorSet = new CollectorSet({ @@ -19,7 +19,7 @@ interface Usage { } export class NestedInside { - collector?: UsageCollector; + collector?: Collector; createMyCollector() { this.collector = collectorSet.makeUsageCollector({ type: 'my_nested_collector', diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 8e246b625706e..ef3e020747f7a 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -612,6 +612,7 @@ describe('SearchSource', () => { searchSource.setField('fields', ['*']); const request = searchSource.getSearchRequestBody(); + expect(request.hasOwnProperty('docvalue_fields')).toBe(false); expect(request.fields).toEqual([ { field: 'foo-bar' }, { field: 'field1' }, diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index 452b081d6387f..4e9e4c318c957 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -14,7 +14,6 @@ "optionalPlugins": ["usageCollection"], "extraPublicDirs": ["common"], "requiredBundles": [ - "usageCollection", "kibanaUtils", "kibanaReact", "inspector" diff --git a/src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts b/src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts index fc0cea2fdbc52..326fcb1e8153d 100644 --- a/src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts +++ b/src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts @@ -7,8 +7,9 @@ */ import { first } from 'rxjs/operators'; -import { StartServicesAccessor } from '../../../../../core/public'; -import { METRIC_TYPE, UsageCollectionSetup } from '../../../../usage_collection/public'; +import { METRIC_TYPE } from '@kbn/analytics'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { AUTOCOMPLETE_EVENT_TYPE, AutocompleteUsageCollector } from './types'; export const createUsageCollector = ( diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 868330ce078c7..d4f0ccfe810c6 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -77,7 +77,6 @@ import { PublicUiSettingsParams } from 'src/core/server/types'; import React from 'react'; import * as React_3 from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; -import { Reporter } from '@kbn/analytics'; import { Request as Request_2 } from '@hapi/hapi'; import { RequestAdapter } from 'src/plugins/inspector/common'; import { RequestStatistics as RequestStatistics_2 } from 'src/plugins/inspector/common'; @@ -102,6 +101,7 @@ import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; +import { UiCounterMetricType } from '@kbn/analytics'; import { Unit } from '@elastic/datemath'; import { UnregisterCallback } from 'history'; import { URL } from 'url'; diff --git a/src/plugins/data/public/query/saved_query/saved_query_service.test.ts b/src/plugins/data/public/query/saved_query/saved_query_service.test.ts index 5e566a9ec0135..a74f81b6a046d 100644 --- a/src/plugins/data/public/query/saved_query/saved_query_service.test.ts +++ b/src/plugins/data/public/query/saved_query/saved_query_service.test.ts @@ -278,6 +278,74 @@ describe('saved query service', () => { await getSavedQuery('foo'); expect(mockSavedObjectsClient.get).toHaveBeenCalledWith('query', 'foo'); }); + + it('should parse a json query', async () => { + mockSavedObjectsClient.get.mockReturnValue({ + id: 'food', + attributes: { + title: 'food', + description: 'bar', + query: { + language: 'kuery', + query: '{"x": "y"}', + }, + }, + }); + + const response = await getSavedQuery('food'); + expect(response.attributes.query.query).toEqual({ x: 'y' }); + }); + + it('should handle null string', async () => { + mockSavedObjectsClient.get.mockReturnValue({ + id: 'food', + attributes: { + title: 'food', + description: 'bar', + query: { + language: 'kuery', + query: 'null', + }, + }, + }); + + const response = await getSavedQuery('food'); + expect(response.attributes.query.query).toEqual('null'); + }); + + it('should handle null quoted string', async () => { + mockSavedObjectsClient.get.mockReturnValue({ + id: 'food', + attributes: { + title: 'food', + description: 'bar', + query: { + language: 'kuery', + query: '"null"', + }, + }, + }); + + const response = await getSavedQuery('food'); + expect(response.attributes.query.query).toEqual('"null"'); + }); + + it('should not lose quotes', async () => { + mockSavedObjectsClient.get.mockReturnValue({ + id: 'food', + attributes: { + title: 'food', + description: 'bar', + query: { + language: 'kuery', + query: '"Bob"', + }, + }, + }); + + const response = await getSavedQuery('food'); + expect(response.attributes.query.query).toEqual('"Bob"'); + }); }); describe('deleteSavedQuery', function () { diff --git a/src/plugins/data/public/query/saved_query/saved_query_service.ts b/src/plugins/data/public/query/saved_query/saved_query_service.ts index e29066c8892cf..0f3da8f80a5ec 100644 --- a/src/plugins/data/public/query/saved_query/saved_query_service.ts +++ b/src/plugins/data/public/query/saved_query/saved_query_service.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { isObject } from 'lodash'; import { SavedObjectsClientContract, SavedObjectAttributes } from 'src/core/public'; import { SavedQueryAttributes, SavedQuery, SavedQueryService } from './types'; @@ -119,12 +120,15 @@ export const createSavedQueryService = ( id: string; attributes: SerializedSavedQueryAttributes; }) => { - let queryString; + let queryString: string | object = savedQuery.attributes.query.query; + try { - queryString = JSON.parse(savedQuery.attributes.query.query); - } catch (error) { - queryString = savedQuery.attributes.query.query; - } + const parsedQueryString: object = JSON.parse(savedQuery.attributes.query.query); + if (isObject(parsedQueryString)) { + queryString = parsedQueryString; + } + } catch (e) {} // eslint-disable-line no-empty + const savedQueryItems: SavedQueryAttributes = { title: savedQuery.attributes.title || '', description: savedQuery.attributes.description || '', diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.ts b/src/plugins/data/public/search/collectors/create_usage_collector.ts index 3fe135ea29152..d4f64fcfa962f 100644 --- a/src/plugins/data/public/search/collectors/create_usage_collector.ts +++ b/src/plugins/data/public/search/collectors/create_usage_collector.ts @@ -7,9 +7,10 @@ */ import { first } from 'rxjs/operators'; -import { UiCounterMetricType } from '@kbn/analytics'; -import { StartServicesAccessor } from '../../../../../core/public'; -import { METRIC_TYPE, UsageCollectionSetup } from '../../../../usage_collection/public'; +import { METRIC_TYPE } from '@kbn/analytics'; +import type { UiCounterMetricType } from '@kbn/analytics'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { SEARCH_EVENT_TYPE, SearchUsageCollector } from './types'; export const createUsageCollector = ( diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts index 0e81f362a030d..dfbc912453d6e 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts @@ -49,17 +49,24 @@ const complete = jest.fn(); function mockFetchImplementation(responses: any[]) { let i = 0; - fetchMock.mockImplementation((r) => { + fetchMock.mockImplementation((r, abortSignal) => { if (!r.request.id) i = 0; const { time = 0, value = {}, isError = false } = responses[i++]; value.meta = { size: 10, }; - return new Promise((resolve, reject) => + return new Promise((resolve, reject) => { setTimeout(() => { return (isError ? reject : resolve)(value); - }, time) - ); + }, time); + + if (abortSignal) { + if (abortSignal.aborted) reject(new AbortError()); + abortSignal.addEventListener('abort', () => { + reject(new AbortError()); + }); + } + }); }); } diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index 5fa0a5c301019..57b156a9b3c00 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -198,7 +198,11 @@ export class SearchInterceptor { options: IAsyncSearchOptions, searchAbortController: SearchAbortController ) { - const search = () => this.runSearch({ id, ...request }, options); + const search = () => + this.runSearch( + { id, ...request }, + { ...options, abortSignal: searchAbortController.getSignal() } + ); const { sessionId, strategy } = options; // track if this search's session will be send to background diff --git a/src/plugins/data/public/search/search_interceptor/utils.ts b/src/plugins/data/public/search/search_interceptor/utils.ts index 2f2d06d3a5494..02c7059a8fdf5 100644 --- a/src/plugins/data/public/search/search_interceptor/utils.ts +++ b/src/plugins/data/public/search/search_interceptor/utils.ts @@ -7,10 +7,8 @@ */ import stringify from 'json-stable-stringify'; +import { Sha256 } from '../../../../../core/public/utils'; export async function createRequestHash(keys: Record) { - const msgBuffer = new TextEncoder().encode(stringify(keys)); - const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map((b) => ('00' + b.toString(16)).slice(-2)).join(''); + return new Sha256().update(stringify(keys), 'utf8').digest('hex'); } diff --git a/src/plugins/data/public/ui/query_string_input/_query_bar.scss b/src/plugins/data/public/ui/query_string_input/_query_bar.scss index 4e12f11668734..479414c458466 100644 --- a/src/plugins/data/public/ui/query_string_input/_query_bar.scss +++ b/src/plugins/data/public/ui/query_string_input/_query_bar.scss @@ -46,6 +46,8 @@ @include kbnThemeStyle('v8') { padding-bottom: $euiSizeS + 1px; + // Firefox adds margin to textarea + margin: 0; &.kbnQueryBar__textarea--hasPrepend { border-top-left-radius: 0; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index a69df282cd568..e66eaab672e1c 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -55,7 +55,6 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { RecursiveReadonly } from '@kbn/utility-types'; import { RequestAdapter } from 'src/plugins/inspector/common'; import { RequestHandlerContext } from 'src/core/server'; -import * as Rx from 'rxjs'; import { SavedObject } from 'kibana/server'; import { SavedObject as SavedObject_2 } from 'src/core/server'; import { SavedObjectsClientContract } from 'src/core/server'; diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index e74046e9dc1ec..4fbeac80b0972 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -83,7 +83,7 @@ const indexPattern = ({ indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; -indexPattern.formatField = (hit: Record, fieldName: string) => { +indexPattern.formatField = (hit: Record, fieldName: string) => { return fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; }; diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.ts similarity index 54% rename from src/plugins/discover/public/application/angular/context/api/_stubs.js rename to src/plugins/discover/public/application/angular/context/api/_stubs.ts index 6930e96a0d411..241d0a621f245 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.js +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.ts @@ -9,8 +9,17 @@ import sinon from 'sinon'; import moment from 'moment'; +import { IndexPatternsContract } from '../../../../../../data/public'; +import { EsHitRecordList } from './context'; + +type SortHit = { + [key in string]: number; // timeField name +} & { + sort: [number, number]; +}; + export function createIndexPatternsStub() { - return { + return ({ get: sinon.spy((indexPatternId) => Promise.resolve({ id: indexPatternId, @@ -18,62 +27,59 @@ export function createIndexPatternsStub() { popularizeField: () => {}, }) ), - }; + } as unknown) as IndexPatternsContract; } /** * A stubbed search source with a `fetch` method that returns all of `_stubHits`. */ -export function createSearchSourceStub(hits, timeField) { - const searchSourceStub = { +export function createSearchSourceStub(hits: EsHitRecordList, timeField?: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const searchSourceStub: any = { _stubHits: hits, _stubTimeField: timeField, - _createStubHit: (timestamp, tiebreaker = 0) => ({ + _createStubHit: (timestamp: number, tiebreaker = 0) => ({ [searchSourceStub._stubTimeField]: timestamp, sort: [timestamp, tiebreaker], }), + setParent: sinon.spy(() => searchSourceStub), + setField: sinon.spy(() => searchSourceStub), + removeField: sinon.spy(() => searchSourceStub), + getField: sinon.spy((key) => { + const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; + return previousSetCall ? previousSetCall.args[1] : null; + }), + fetch: sinon.spy(() => + Promise.resolve({ + hits: { + hits: searchSourceStub._stubHits, + total: searchSourceStub._stubHits.length, + }, + }) + ), }; - - searchSourceStub.setParent = sinon.spy(() => searchSourceStub); - searchSourceStub.setField = sinon.spy(() => searchSourceStub); - searchSourceStub.removeField = sinon.spy(() => searchSourceStub); - - searchSourceStub.getField = sinon.spy((key) => { - const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; - return previousSetCall ? previousSetCall.args[1] : null; - }); - - searchSourceStub.fetch = sinon.spy(() => - Promise.resolve({ - hits: { - hits: searchSourceStub._stubHits, - total: searchSourceStub._stubHits.length, - }, - }) - ); - return searchSourceStub; } /** * A stubbed search source with a `fetch` method that returns a filtered set of `_stubHits`. */ -export function createContextSearchSourceStub(hits, timeField = '@timestamp') { - const searchSourceStub = createSearchSourceStub(hits, timeField); +export function createContextSearchSourceStub(timeFieldName: string) { + const searchSourceStub = createSearchSourceStub([], timeFieldName); searchSourceStub.fetch = sinon.spy(() => { - const timeField = searchSourceStub._stubTimeField; + const timeField: keyof SortHit = searchSourceStub._stubTimeField; const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1]; const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField]; const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1]; const sortDirection = lastSort[0][timeField].order; const sortFunction = sortDirection === 'asc' - ? (first, second) => first[timeField] - second[timeField] - : (first, second) => second[timeField] - first[timeField]; + ? (first: SortHit, second: SortHit) => first[timeField] - second[timeField] + : (first: SortHit, second: SortHit) => second[timeField] - first[timeField]; const filteredHits = searchSourceStub._stubHits .filter( - (hit) => + (hit: SortHit) => moment(hit[timeField]).isSameOrAfter(timeRange.gte) && moment(hit[timeField]).isSameOrBefore(timeRange.lte) ) @@ -87,5 +93,5 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') { }); }); - return searchSourceStub; + return searchSourceStub as sinon.SinonStub; } diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.js b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts similarity index 73% rename from src/plugins/discover/public/application/angular/context/api/anchor.test.js rename to src/plugins/discover/public/application/angular/context/api/anchor.test.ts index 12b9b4ab28556..62c9a2a5e3b90 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts @@ -6,24 +6,31 @@ * Side Public License, v 1. */ +import { EsQuerySortValue, SortDirection } from '../../../../../../data/public'; import { createIndexPatternsStub, createSearchSourceStub } from './_stubs'; - -import { fetchAnchorProvider } from './anchor'; +import { AnchorHitRecord, fetchAnchorProvider } from './anchor'; describe('context app', function () { - describe('function fetchAnchor', function () { - let fetchAnchor; - let searchSourceStub; + let fetchAnchor: ( + indexPatternId: string, + anchorId: string, + sort: EsQuerySortValue[] + ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let searchSourceStub: any; + describe('function fetchAnchor', function () { beforeEach(() => { - searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); + searchSourceStub = createSearchSourceStub([ + { _id: 'hit1', fields: [], sort: [], _source: {} }, + ]); fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub); }); it('should use the `fetch` method of the SearchSource', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { expect(searchSourceStub.fetch.calledOnce).toBe(true); }); @@ -31,8 +38,8 @@ describe('context app', function () { it('should configure the SearchSource to not inherit from the implicit root', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setParentSpy = searchSourceStub.setParent; expect(setParentSpy.calledOnce).toBe(true); @@ -42,8 +49,8 @@ describe('context app', function () { it('should set the SearchSource index pattern', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setFieldSpy = searchSourceStub.setField; expect(setFieldSpy.firstCall.args[1].id).toEqual('INDEX_PATTERN_ID'); @@ -52,8 +59,8 @@ describe('context app', function () { it('should set the SearchSource version flag to true', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setVersionSpy = searchSourceStub.setField.withArgs('version'); expect(setVersionSpy.calledOnce).toBe(true); @@ -63,8 +70,8 @@ describe('context app', function () { it('should set the SearchSource size to 1', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setSizeSpy = searchSourceStub.setField.withArgs('size'); expect(setSizeSpy.calledOnce).toBe(true); @@ -74,8 +81,8 @@ describe('context app', function () { it('should set the SearchSource query to an ids query', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setQuerySpy = searchSourceStub.setField.withArgs('query'); expect(setQuerySpy.calledOnce).toBe(true); @@ -96,12 +103,15 @@ describe('context app', function () { it('should set the SearchSource sort order', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setSortSpy = searchSourceStub.setField.withArgs('sort'); expect(setSortSpy.calledOnce).toBe(true); - expect(setSortSpy.firstCall.args[1]).toEqual([{ '@timestamp': 'desc' }, { _doc: 'desc' }]); + expect(setSortSpy.firstCall.args[1]).toEqual([ + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, + ]); }); }); @@ -109,11 +119,11 @@ describe('context app', function () { searchSourceStub._stubHits = []; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then( () => { - expect().fail('expected the promise to be rejected'); + fail('expected the promise to be rejected'); }, (error) => { expect(error).toBeInstanceOf(Error); @@ -125,8 +135,8 @@ describe('context app', function () { searchSourceStub._stubHits = [{ property1: 'value1' }, { property2: 'value2' }]; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then((anchorDocument) => { expect(anchorDocument).toHaveProperty('property1', 'value1'); expect(anchorDocument).toHaveProperty('$$_isAnchor', true); @@ -135,11 +145,10 @@ describe('context app', function () { }); describe('useNewFields API', () => { - let fetchAnchor; - let searchSourceStub; - beforeEach(() => { - searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); + searchSourceStub = createSearchSourceStub([ + { _id: 'hit1', fields: [], sort: [], _source: {} }, + ]); fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true); }); @@ -147,8 +156,8 @@ describe('context app', function () { searchSourceStub._stubHits = [{ property1: 'value1' }, { property2: 'value2' }]; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setFieldsSpy = searchSourceStub.setField.withArgs('fields'); const removeFieldsSpy = searchSourceStub.removeField.withArgs('fieldsFromSource'); diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.js b/src/plugins/discover/public/application/angular/context/api/anchor.ts similarity index 61% rename from src/plugins/discover/public/application/angular/context/api/anchor.js rename to src/plugins/discover/public/application/angular/context/api/anchor.ts index 83b611cb0d648..da81ce525331a 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.ts @@ -6,11 +6,31 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi = false) { - return async function fetchAnchor(indexPatternId, anchorId, sort) { +import { + ISearchSource, + IndexPatternsContract, + EsQuerySortValue, +} from '../../../../../../data/public'; +import { EsHitRecord } from './context'; + +export interface AnchorHitRecord extends EsHitRecord { + // eslint-disable-next-line @typescript-eslint/naming-convention + $$_isAnchor: boolean; +} + +export function fetchAnchorProvider( + indexPatterns: IndexPatternsContract, + searchSource: ISearchSource, + useNewFieldsApi: boolean = false +) { + return async function fetchAnchor( + indexPatternId: string, + anchorId: string, + sort: EsQuerySortValue[] + ): Promise { const indexPattern = await indexPatterns.get(indexPatternId); searchSource .setParent(undefined) @@ -36,7 +56,7 @@ export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi } const response = await searchSource.fetch(); - if (_.get(response, ['hits', 'total'], 0) < 1) { + if (get(response, ['hits', 'total'], 0) < 1) { throw new Error( i18n.translate('discover.context.failedToLoadAnchorDocumentErrorDescription', { defaultMessage: 'Failed to load anchor document.', @@ -45,8 +65,9 @@ export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi } return { - ..._.get(response, ['hits', 'hits', 0]), + ...get(response, ['hits', 'hits', 0]), + // eslint-disable-next-line @typescript-eslint/naming-convention $$_isAnchor: true, - }; + } as AnchorHitRecord; }; } diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts similarity index 77% rename from src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts index 9f5e62da398d2..dc097bc110e20 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts @@ -9,8 +9,11 @@ import moment from 'moment'; import { get, last } from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; -import { fetchContextProvider } from './context'; -import { setServices } from '../../../../kibana_services'; +import { EsHitRecordList, fetchContextProvider } from './context'; +import { setServices, SortDirection } from '../../../../kibana_services'; +import { AnchorHitRecord } from './anchor'; +import { Query } from '../../../../../../data/public'; +import { DiscoverServices } from '../../../../build_services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -18,15 +21,31 @@ const ANCHOR_TIMESTAMP_3 = new Date(MS_PER_DAY * 3).toJSON(); const ANCHOR_TIMESTAMP_1000 = new Date(MS_PER_DAY * 1000).toJSON(); const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); +interface Timestamp { + format: string; + gte?: string; + lte?: string; +} + describe('context app', function () { - describe('function fetchPredecessors', function () { - let fetchPredecessors; - let mockSearchSource; + let fetchPredecessors: ( + indexPatternId: string, + timeField: string, + sortDir: SortDirection, + timeValIso: string, + timeValNr: number, + tieBreakerField: string, + tieBreakerValue: number, + size: number + ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let mockSearchSource: any; + describe('function fetchPredecessors', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -34,7 +53,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchPredecessors = ( indexPatternId, @@ -44,7 +63,7 @@ describe('context app', function () { timeValNr, tieBreakerField, tieBreakerValue, - size + size = 10 ) => { const anchor = { _source: { @@ -56,7 +75,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'predecessors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -78,14 +97,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); }); @@ -103,17 +121,16 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 6, - [] - ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + 6 + ).then((hits: EsHitRecordList) => { + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: string) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); @@ -123,7 +140,7 @@ describe('context app', function () { // should have started at the given time expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have ended with a half-open interval - expect(Object.keys(last(intervals))).toEqual(['format', 'gte']); + expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'gte']); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); @@ -141,24 +158,23 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_1000, MS_PER_DAY * 1000, '_doc', 0, - 3, - [] - ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => - get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) - ); + 3 + ).then((hits: EsHitRecordList) => { + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: string) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => { + return get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']); + }); // should have started at the given time expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString()); // should have stopped before reaching MS_PER_DAY * 1700 - expect(moment(last(intervals).lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); + expect(moment(last(intervals)?.lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); }); @@ -168,14 +184,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { expect(hits).toEqual([]); }); }); @@ -184,13 +199,12 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then(() => { const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); @@ -202,13 +216,12 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP, MS_PER_DAY, '_doc', 0, - 3, - [] + 3 ).then(() => { expect( mockSearchSource.setField.calledWith('sort', [ @@ -221,13 +234,10 @@ describe('context app', function () { }); describe('function fetchPredecessors with useNewFieldsApi set', function () { - let fetchPredecessors; - let mockSearchSource; - beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -235,7 +245,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchPredecessors = ( indexPatternId, @@ -245,7 +255,7 @@ describe('context app', function () { timeValNr, tieBreakerField, tieBreakerValue, - size + size = 10 ) => { const anchor = { _source: { @@ -257,7 +267,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( 'predecessors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -279,14 +289,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); expect(mockSearchSource.fetch.calledOnce).toBe(true); diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts similarity index 79% rename from src/plugins/discover/public/application/angular/context/api/context.successors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.successors.test.ts index 4936c937aa2fa..f8fc7eb343206 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts @@ -10,24 +10,42 @@ import moment from 'moment'; import { get, last } from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; -import { setServices } from '../../../../kibana_services'; - -import { fetchContextProvider } from './context'; +import { setServices, SortDirection } from '../../../../kibana_services'; +import { Query } from '../../../../../../data/public'; +import { EsHitRecordList, fetchContextProvider } from './context'; +import { AnchorHitRecord } from './anchor'; +import { DiscoverServices } from '../../../../build_services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); const ANCHOR_TIMESTAMP_3 = new Date(MS_PER_DAY * 3).toJSON(); const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); +interface Timestamp { + format: string; + gte?: string; + lte?: string; +} + describe('context app', function () { - describe('function fetchSuccessors', function () { - let fetchSuccessors; - let mockSearchSource; + let fetchSuccessors: ( + indexPatternId: string, + timeField: string, + sortDir: SortDirection, + timeValIso: string, + timeValNr: number, + tieBreakerField: string, + tieBreakerValue: number, + size: number + ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let mockSearchSource: any; + describe('function fetchSuccessors', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -35,7 +53,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchSuccessors = ( indexPatternId, @@ -57,7 +75,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'successors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -79,13 +97,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); @@ -104,17 +121,16 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 6, - [] + 6 ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: [string]) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); @@ -124,7 +140,7 @@ describe('context app', function () { // should have started at the given time expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have ended with a half-open interval - expect(Object.keys(last(intervals))).toEqual(['format', 'lte']); + expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'lte']); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); @@ -144,24 +160,23 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 4, - [] + 4 ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: [string]) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); // should have started at the given time expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have stopped before reaching MS_PER_DAY * 2200 - expect(moment(last(intervals).gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); + expect(moment(last(intervals)?.gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 4)); @@ -172,13 +187,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(hits).toEqual([]); }); @@ -188,13 +202,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then(() => { const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); @@ -206,18 +219,17 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP, MS_PER_DAY, '_doc', 0, - 3, - [] + 3 ).then(() => { expect( mockSearchSource.setField.calledWith('sort', [ - { '@timestamp': { order: 'desc', format: 'strict_date_optional_time' } }, - { _doc: 'desc' }, + { '@timestamp': { order: SortDirection.desc, format: 'strict_date_optional_time' } }, + { _doc: SortDirection.desc }, ]) ).toBe(true); }); @@ -225,13 +237,10 @@ describe('context app', function () { }); describe('function fetchSuccessors with useNewFieldsApi set', function () { - let fetchSuccessors; - let mockSearchSource; - beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -239,7 +248,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchSuccessors = ( indexPatternId, @@ -261,7 +270,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( 'successors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -283,13 +292,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts index 820e37d754ef2..4309b9ca4c391 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -14,11 +14,14 @@ import { generateIntervals } from './utils/generate_intervals'; import { getEsQuerySearchAfter } from './utils/get_es_query_search_after'; import { getEsQuerySort } from './utils/get_es_query_sort'; import { getServices } from '../../../../kibana_services'; +import { AnchorHitRecord } from './anchor'; export type SurrDocType = 'successors' | 'predecessors'; export interface EsHitRecord { + // eslint-disable-next-line @typescript-eslint/no-explicit-any fields: Record; sort: number[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any _source: Record; _id: string; } @@ -39,7 +42,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields * * @param {SurrDocType} type - `successors` or `predecessors` * @param {string} indexPatternId - * @param {EsHitRecord} anchor - anchor record + * @param {AnchorHitRecord} anchor - anchor record * @param {string} timeField - name of the timefield, that's sorted on * @param {string} tieBreakerField - name of the tie breaker, the 2nd sort field * @param {SortDirection} sortDir - direction of sorting @@ -50,13 +53,13 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields async function fetchSurroundingDocs( type: SurrDocType, indexPatternId: string, - anchor: EsHitRecord, + anchor: AnchorHitRecord, timeField: string, tieBreakerField: string, sortDir: SortDirection, size: number, filters: Filter[] - ) { + ): Promise { if (typeof anchor !== 'object' || anchor === null || !size) { return []; } diff --git a/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts index 1f745ab1b728e..fb0e58832a202 100644 --- a/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/get_es_query_search_after.ts @@ -28,11 +28,11 @@ export function getEsQuerySearchAfter( // already surrounding docs -> first or last record is used const afterTimeRecIdx = type === 'successors' && documents.length ? documents.length - 1 : 0; const afterTimeDoc = documents[afterTimeRecIdx]; - let afterTimeValue = afterTimeDoc.sort[0]; + let afterTimeValue: string | number = afterTimeDoc.sort[0]; if (nanoSeconds) { afterTimeValue = useNewFieldsApi - ? afterTimeDoc.fields[timeFieldName][0] - : afterTimeDoc._source[timeFieldName]; + ? (afterTimeDoc.fields[timeFieldName] as Array)[0] + : (afterTimeDoc._source[timeFieldName] as string | number); } return [afterTimeValue, afterTimeDoc.sort[1]]; } @@ -42,8 +42,8 @@ export function getEsQuerySearchAfter( searchAfter[0] = anchor.sort[0]; if (nanoSeconds) { searchAfter[0] = useNewFieldsApi - ? anchor.fields[timeFieldName][0] - : anchor._source[timeFieldName]; + ? (anchor.fields[timeFieldName] as Array)[0] + : (anchor._source[timeFieldName] as string | number); } searchAfter[1] = anchor.sort[1]; return searchAfter; diff --git a/src/plugins/discover/public/application/angular/context/api/utils/sorting.ts b/src/plugins/discover/public/application/angular/context/api/utils/sorting.ts index 0290675489e54..c6f389ddca46a 100644 --- a/src/plugins/discover/public/application/angular/context/api/utils/sorting.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/sorting.ts @@ -27,7 +27,6 @@ export function getFirstSortableField(indexPattern: IndexPattern, fieldNames: st const sortableFields = fieldNames.filter( (fieldName) => META_FIELD_NAMES.includes(fieldName) || - // @ts-ignore (indexPattern.fields.getByName(fieldName) || { sortable: false }).sortable ); return sortableFields[0]; diff --git a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts index ddcd88a168788..dd8c874391fd4 100644 --- a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts +++ b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar_directive.ts @@ -9,6 +9,7 @@ import { getAngularModule } from '../../../../../kibana_services'; import { ActionBar } from './action_bar'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any getAngularModule().directive('contextActionBar', function (reactDirective: any) { return reactDirective(ActionBar); }); diff --git a/src/plugins/discover/public/application/angular/context/query/actions.js b/src/plugins/discover/public/application/angular/context/query/actions.tsx similarity index 63% rename from src/plugins/discover/public/application/angular/context/query/actions.js rename to src/plugins/discover/public/application/angular/context/query/actions.tsx index 493697ff38ff1..52c56d379d259 100644 --- a/src/plugins/discover/public/application/angular/context/query/actions.js +++ b/src/plugins/discover/public/application/angular/context/query/actions.tsx @@ -6,19 +6,30 @@ * Side Public License, v 1. */ -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; import React from 'react'; -import { getServices } from '../../../../kibana_services'; +import { fromPairs } from 'lodash'; +import { i18n } from '@kbn/i18n'; -import { fetchAnchorProvider } from '../api/anchor'; -import { fetchContextProvider } from '../api/context'; -import { getQueryParameterActions } from '../query_parameters'; -import { FAILURE_REASONS, LOADING_STATUS } from './index'; -import { MarkdownSimple } from '../../../../../../kibana_react/public'; +import { getServices } from '../../../../kibana_services'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; +import { MarkdownSimple, toMountPoint } from '../../../../../../kibana_react/public'; +import { AnchorHitRecord, fetchAnchorProvider } from '../api/anchor'; +import { EsHitRecord, EsHitRecordList, fetchContextProvider, SurrDocType } from '../api/context'; +import { getQueryParameterActions } from '../query_parameters'; +import { + ContextAppState, + FailureReason, + LoadingStatus, + LoadingStatusEntry, + LoadingStatusState, + QueryParameters, +} from '../../context_app_state'; + +interface DiscoverPromise extends PromiseConstructor { + try: (fn: () => Promise) => Promise; +} -export function QueryActionsProvider(Promise) { +export function QueryActionsProvider(Promise: DiscoverPromise) { const { filterManager, indexPatterns, data, uiSettings } = getServices(); const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); const fetchAnchor = fetchAnchorProvider( @@ -32,24 +43,27 @@ export function QueryActionsProvider(Promise) { indexPatterns ); - const setFailedStatus = (state) => (subject, details = {}) => + const setFailedStatus = (state: ContextAppState) => ( + subject: keyof LoadingStatusState, + details: LoadingStatusEntry = {} + ) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.FAILED, - reason: FAILURE_REASONS.UNKNOWN, + status: LoadingStatus.FAILED, + reason: FailureReason.UNKNOWN, ...details, }); - const setLoadedStatus = (state) => (subject) => + const setLoadedStatus = (state: ContextAppState) => (subject: keyof LoadingStatusState) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.LOADED, + status: LoadingStatus.LOADED, }); - const setLoadingStatus = (state) => (subject) => + const setLoadingStatus = (state: ContextAppState) => (subject: keyof LoadingStatusState) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.LOADING, + status: LoadingStatus.LOADING, }); - const fetchAnchorRow = (state) => () => { + const fetchAnchorRow = (state: ContextAppState) => () => { const { queryParameters: { indexPatternId, anchorId, sort, tieBreakerField }, } = state; @@ -57,7 +71,7 @@ export function QueryActionsProvider(Promise) { if (!tieBreakerField) { return Promise.reject( setFailedStatus(state)('anchor', { - reason: FAILURE_REASONS.INVALID_TIEBREAKER, + reason: FailureReason.INVALID_TIEBREAKER, }) ); } @@ -65,27 +79,27 @@ export function QueryActionsProvider(Promise) { setLoadingStatus(state)('anchor'); return Promise.try(() => - fetchAnchor(indexPatternId, anchorId, [_.fromPairs([sort]), { [tieBreakerField]: sort[1] }]) + fetchAnchor(indexPatternId, anchorId, [fromPairs([sort]), { [tieBreakerField]: sort[1] }]) ).then( - (anchorDocument) => { + (anchorDocument: AnchorHitRecord) => { setLoadedStatus(state)('anchor'); state.rows.anchor = anchorDocument; return anchorDocument; }, - (error) => { + (error: Error) => { setFailedStatus(state)('anchor', { error }); getServices().toastNotifications.addDanger({ title: i18n.translate('discover.context.unableToLoadAnchorDocumentDescription', { defaultMessage: 'Unable to load the anchor document', }), - text: {error.message}, + text: toMountPoint({error.message}), }); throw error; } ); }; - const fetchSurroundingRows = (type, state) => { + const fetchSurroundingRows = (type: SurrDocType, state: ContextAppState) => { const { queryParameters: { indexPatternId, sort, tieBreakerField }, rows: { anchor }, @@ -100,7 +114,7 @@ export function QueryActionsProvider(Promise) { if (!tieBreakerField) { return Promise.reject( setFailedStatus(state)(type, { - reason: FAILURE_REASONS.INVALID_TIEBREAKER, + reason: FailureReason.INVALID_TIEBREAKER, }) ); } @@ -120,54 +134,62 @@ export function QueryActionsProvider(Promise) { filters ) ).then( - (documents) => { + (documents: EsHitRecordList) => { setLoadedStatus(state)(type); state.rows[type] = documents; return documents; }, - (error) => { + (error: Error) => { setFailedStatus(state)(type, { error }); getServices().toastNotifications.addDanger({ title: i18n.translate('discover.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents', }), - text: {error.message}, + text: toMountPoint({error.message}), }); throw error; } ); }; - const fetchContextRows = (state) => () => + const fetchContextRows = (state: ContextAppState) => () => Promise.all([ fetchSurroundingRows('predecessors', state), fetchSurroundingRows('successors', state), ]); - const fetchAllRows = (state) => () => + const fetchAllRows = (state: ContextAppState) => () => Promise.try(fetchAnchorRow(state)).then(fetchContextRows(state)); - const fetchContextRowsWithNewQueryParameters = (state) => (queryParameters) => { + const fetchContextRowsWithNewQueryParameters = (state: ContextAppState) => ( + queryParameters: QueryParameters + ) => { setQueryParameters(state)(queryParameters); return fetchContextRows(state)(); }; - const fetchAllRowsWithNewQueryParameters = (state) => (queryParameters) => { + const fetchAllRowsWithNewQueryParameters = (state: ContextAppState) => ( + queryParameters: QueryParameters + ) => { setQueryParameters(state)(queryParameters); return fetchAllRows(state)(); }; - const fetchGivenPredecessorRows = (state) => (count) => { + const fetchGivenPredecessorRows = (state: ContextAppState) => (count: number) => { setPredecessorCount(state)(count); return fetchSurroundingRows('predecessors', state); }; - const fetchGivenSuccessorRows = (state) => (count) => { + const fetchGivenSuccessorRows = (state: ContextAppState) => (count: number) => { setSuccessorCount(state)(count); return fetchSurroundingRows('successors', state); }; - const setAllRows = (state) => (predecessorRows, anchorRow, successorRows) => + const setAllRows = (state: ContextAppState) => ( + predecessorRows: EsHitRecordList, + anchorRow: EsHitRecord, + successorRows: EsHitRecordList + ) => (state.rows.all = [ ...(predecessorRows || []), ...(anchorRow ? [anchorRow] : []), diff --git a/src/plugins/discover/public/application/angular/context/query/index.js b/src/plugins/discover/public/application/angular/context/query/index.ts similarity index 83% rename from src/plugins/discover/public/application/angular/context/query/index.js rename to src/plugins/discover/public/application/angular/context/query/index.ts index a718cdb237774..70e3dc1600472 100644 --- a/src/plugins/discover/public/application/angular/context/query/index.js +++ b/src/plugins/discover/public/application/angular/context/query/index.ts @@ -7,5 +7,4 @@ */ export { QueryActionsProvider } from './actions'; -export { FAILURE_REASONS, LOADING_STATUS } from '../../../components/context_app/constants'; export { createInitialLoadingStatusState } from './state'; diff --git a/src/plugins/discover/public/application/angular/context/query/state.ts b/src/plugins/discover/public/application/angular/context/query/state.ts new file mode 100644 index 0000000000000..fefadf9009185 --- /dev/null +++ b/src/plugins/discover/public/application/angular/context/query/state.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LoadingStatus, LoadingStatusState } from '../../context_app_state'; + +export function createInitialLoadingStatusState(): LoadingStatusState { + return { + anchor: LoadingStatus.UNINITIALIZED, + predecessors: LoadingStatus.UNINITIALIZED, + successors: LoadingStatus.UNINITIALIZED, + }; +} diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js b/src/plugins/discover/public/application/angular/context/query_parameters/actions.js deleted file mode 100644 index b308196cb8b04..0000000000000 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import _ from 'lodash'; -import { esFilters } from '../../../../../../data/public'; -import { popularizeField } from '../../../helpers/popularize_field'; - -import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; - -export function getQueryParameterActions(filterManager, indexPatterns) { - const setPredecessorCount = (state) => (predecessorCount) => - (state.queryParameters.predecessorCount = clamp( - MIN_CONTEXT_SIZE, - MAX_CONTEXT_SIZE, - predecessorCount - )); - - const setSuccessorCount = (state) => (successorCount) => - (state.queryParameters.successorCount = clamp( - MIN_CONTEXT_SIZE, - MAX_CONTEXT_SIZE, - successorCount - )); - - const setQueryParameters = (state) => (queryParameters) => - Object.assign(state.queryParameters, _.pick(queryParameters, QUERY_PARAMETER_KEYS)); - - const updateFilters = () => (filters) => { - filterManager.setFilters(filters); - }; - - const addFilter = (state) => async (field, values, operation) => { - const indexPatternId = state.queryParameters.indexPatternId; - const newFilters = esFilters.generateFilters( - filterManager, - field, - values, - operation, - indexPatternId - ); - filterManager.addFilters(newFilters); - if (indexPatterns) { - const indexPattern = await indexPatterns.get(indexPatternId); - await popularizeField(indexPattern, field.name, indexPatterns); - } - }; - - return { - addFilter, - updateFilters, - setPredecessorCount, - setQueryParameters, - setSuccessorCount, - }; -} - -function clamp(minimum, maximum, value) { - return Math.max(Math.min(maximum, value), minimum); -} diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts index 774a29721c426..b54f11e9e6706 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts @@ -6,20 +6,13 @@ * Side Public License, v 1. */ -// @ts-ignore import { getQueryParameterActions } from './actions'; -import { FilterManager } from '../../../../../../data/public'; +import { FilterManager, SortDirection } from '../../../../../../data/public'; import { coreMock } from '../../../../../../../core/public/mocks'; +import { ContextAppState, LoadingStatus, QueryParameters } from '../../context_app_state'; const setupMock = coreMock.createSetup(); -let state: { - queryParameters: { - defaultStepSize: number; - indexPatternId: string; - predecessorCount: number; - successorCount: number; - }; -}; +let state: ContextAppState; let filterManager: FilterManager; let filterManagerSpy: jest.SpyInstance; @@ -33,7 +26,25 @@ beforeEach(() => { indexPatternId: 'INDEX_PATTERN_ID', predecessorCount: 10, successorCount: 10, + anchorId: '', + columns: [], + filters: [], + sort: ['field', SortDirection.asc], + tieBreakerField: '', + }, + loadingStatus: { + anchor: LoadingStatus.UNINITIALIZED, + predecessors: LoadingStatus.UNINITIALIZED, + successors: LoadingStatus.UNINITIALIZED, }, + rows: { + all: [], + // eslint-disable-next-line @typescript-eslint/naming-convention + anchor: { $$_isAnchor: true, fields: [], sort: [], _source: [], _id: '' }, + predecessors: [], + successors: [], + }, + useNewFieldsApi: true, }; }); @@ -105,6 +116,7 @@ describe('context query_parameter actions', function () { const newState = { ...state, queryParameters: { + ...state.queryParameters, additionalParameter: 'ADDITIONAL_PARAMETER', }, }; @@ -113,11 +125,12 @@ describe('context query_parameter actions', function () { anchorId: 'ANCHOR_ID', columns: ['column'], defaultStepSize: 3, - filters: ['filter'], + filters: [], indexPatternId: 'INDEX_PATTERN', predecessorCount: 100, successorCount: 100, - sort: ['field'], + sort: ['field', SortDirection.asc], + tieBreakerField: '', }); expect(actualState).toEqual({ @@ -125,20 +138,21 @@ describe('context query_parameter actions', function () { anchorId: 'ANCHOR_ID', columns: ['column'], defaultStepSize: 3, - filters: ['filter'], + filters: [], indexPatternId: 'INDEX_PATTERN', predecessorCount: 100, successorCount: 100, - sort: ['field'], + sort: ['field', SortDirection.asc], + tieBreakerField: '', }); }); it('should ignore invalid properties', function () { const newState = { ...state }; - setQueryParameters(newState)({ + setQueryParameters(newState)(({ additionalParameter: 'ADDITIONAL_PARAMETER', - }); + } as unknown) as QueryParameters); expect(state.queryParameters).toEqual(newState.queryParameters); }); diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts new file mode 100644 index 0000000000000..02eab422b68a4 --- /dev/null +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pick } from 'lodash'; + +import { + IndexPatternsContract, + FilterManager, + esFilters, + Filter, + IndexPatternField, +} from '../../../../../../data/public'; +import { popularizeField } from '../../../helpers/popularize_field'; +import { ContextAppState, QueryParameters } from '../../context_app_state'; +import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; + +export function getQueryParameterActions( + filterManager: FilterManager, + indexPatterns?: IndexPatternsContract +) { + const setPredecessorCount = (state: ContextAppState) => (predecessorCount: number) => { + return (state.queryParameters.predecessorCount = clamp( + MIN_CONTEXT_SIZE, + MAX_CONTEXT_SIZE, + predecessorCount + )); + }; + + const setSuccessorCount = (state: ContextAppState) => (successorCount: number) => { + return (state.queryParameters.successorCount = clamp( + MIN_CONTEXT_SIZE, + MAX_CONTEXT_SIZE, + successorCount + )); + }; + + const setQueryParameters = (state: ContextAppState) => (queryParameters: QueryParameters) => { + return Object.assign(state.queryParameters, pick(queryParameters, QUERY_PARAMETER_KEYS)); + }; + + const updateFilters = () => (filters: Filter[]) => { + filterManager.setFilters(filters); + }; + + const addFilter = (state: ContextAppState) => async ( + field: IndexPatternField | string, + values: unknown, + operation: string + ) => { + const indexPatternId = state.queryParameters.indexPatternId; + const newFilters = esFilters.generateFilters( + filterManager, + field, + values, + operation, + indexPatternId + ); + filterManager.addFilters(newFilters); + if (indexPatterns) { + const indexPattern = await indexPatterns.get(indexPatternId); + const fieldName = typeof field === 'string' ? field : field.name; + await popularizeField(indexPattern, fieldName, indexPatterns); + } + }; + + return { + addFilter, + updateFilters, + setPredecessorCount, + setQueryParameters, + setSuccessorCount, + }; +} + +function clamp(minimum: number, maximum: number, value: number) { + return Math.max(Math.min(maximum, value), minimum); +} diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/index.js b/src/plugins/discover/public/application/angular/context/query_parameters/index.ts similarity index 100% rename from src/plugins/discover/public/application/angular/context/query_parameters/index.js rename to src/plugins/discover/public/application/angular/context/query_parameters/index.ts diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index 04406338f49ab..a90904fa2ccea 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -21,12 +21,7 @@ import { getQueryParameterActions, QUERY_PARAMETER_KEYS, } from './context/query_parameters'; -import { - createInitialLoadingStatusState, - FAILURE_REASONS, - LOADING_STATUS, - QueryActionsProvider, -} from './context/query'; +import { createInitialLoadingStatusState, QueryActionsProvider } from './context/query'; import { callAfterBindingsWorkaround } from './context/helpers/call_after_bindings_workaround'; getAngularModule().directive('contextApp', function ContextApp() { @@ -69,11 +64,6 @@ function ContextAppController($scope, Private) { (action) => (...args) => action(this.state)(...args) ); - this.constants = { - FAILURE_REASONS, - LOADING_STATUS, - }; - $scope.$watchGroup( [ () => this.state.rows.predecessors, diff --git a/src/plugins/discover/public/application/angular/context_app_state.ts b/src/plugins/discover/public/application/angular/context_app_state.ts new file mode 100644 index 0000000000000..1593b2457019c --- /dev/null +++ b/src/plugins/discover/public/application/angular/context_app_state.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Filter } from '../../../../data/public'; +import { AnchorHitRecord } from './context/api/anchor'; +import { EsHitRecordList } from './context/api/context'; +import { SortDirection } from './context/api/utils/sorting'; + +export interface ContextAppState { + loadingStatus: LoadingStatusState; + queryParameters: QueryParameters; + rows: ContextRows; + useNewFieldsApi: boolean; +} + +export enum LoadingStatus { + FAILED = 'failed', + LOADED = 'loaded', + LOADING = 'loading', + UNINITIALIZED = 'uninitialized', +} +export enum FailureReason { + UNKNOWN = 'unknown', + INVALID_TIEBREAKER = 'invalid_tiebreaker', +} +export type LoadingStatusEntry = Partial<{ + status: LoadingStatus; + reason: FailureReason; + error: Error; +}>; + +export interface LoadingStatusState { + anchor: LoadingStatusEntry | LoadingStatus; + predecessors: LoadingStatusEntry | LoadingStatus; + successors: LoadingStatusEntry | LoadingStatus; +} + +export interface QueryParameters { + anchorId: string; + columns: string[]; + defaultStepSize: number; + filters: Filter[]; + indexPatternId: string; + predecessorCount: number; + successorCount: number; + sort: [string, SortDirection]; + tieBreakerField: string; +} + +interface ContextRows { + all: EsHitRecordList; + anchor: AnchorHitRecord; + predecessors: EsHitRecordList; + successors: EsHitRecordList; +} diff --git a/src/plugins/discover/public/application/angular/context_state.test.ts b/src/plugins/discover/public/application/angular/context_state.test.ts index b49a6546a993d..ed4a74c70112b 100644 --- a/src/plugins/discover/public/application/angular/context_state.test.ts +++ b/src/plugins/discover/public/application/angular/context_state.test.ts @@ -12,16 +12,17 @@ import { createBrowserHistory, History } from 'history'; import { FilterManager, Filter } from '../../../../data/public'; import { coreMock } from '../../../../../core/public/mocks'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../common'; + const setupMock = coreMock.createSetup(); describe('Test Discover Context State', () => { let history: History; - let state: any; + let state: ReturnType; const getCurrentUrl = () => history.createHref(history.location); beforeEach(async () => { history = createBrowserHistory(); history.push('/'); - state = await getState({ + state = getState({ defaultStepSize: '4', timeFieldName: 'time', history, diff --git a/src/plugins/discover/public/application/angular/context_state.ts b/src/plugins/discover/public/application/angular/context_state.ts index 0bae006ec1f6e..d60f2e655c4eb 100644 --- a/src/plugins/discover/public/application/angular/context_state.ts +++ b/src/plugins/discover/public/application/angular/context_state.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import { isEqual } from 'lodash'; import { History } from 'history'; import { NotificationsStart, IUiSettingsClient } from 'kibana/public'; import { createStateContainer, createKbnUrlStateStorage, syncStates, - BaseStateContainer, withNotifyOnErrors, + ReduxLikeStateContainer, } from '../../../../kibana_utils/public'; import { esFilters, FilterManager, Filter, Query } from '../../../../data/public'; import { handleSourceColumnState } from './helpers'; @@ -85,11 +85,11 @@ interface GetStateReturn { /** * Global state, the _g part of the URL */ - globalState: BaseStateContainer; + globalState: ReduxLikeStateContainer; /** * App state, the _a part of the URL */ - appState: BaseStateContainer; + appState: ReduxLikeStateContainer; /** * Start sync between state and URL */ @@ -247,7 +247,7 @@ function isEqualState(stateA: AppState | GlobalState, stateB: AppState | GlobalS const { filters: stateAFilters = [], ...stateAPartial } = stateA; const { filters: stateBFilters = [], ...stateBPartial } = stateB; return ( - _.isEqual(stateAPartial, stateBPartial) && + isEqual(stateAPartial, stateBPartial) && esFilters.compareFilters(stateAFilters, stateBFilters, esFilters.COMPARE_ALL_OPTIONS) ); } diff --git a/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts index a41f6e6259041..0c033fd066eab 100644 --- a/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts +++ b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts @@ -12,7 +12,7 @@ import 'angular-mocks'; import 'angular-sanitize'; import 'angular-route'; -// @ts-ignore +// @ts-expect-error import { createDebounceProviderTimeout } from './debounce'; import { coreMock } from '../../../../../../../core/public/mocks'; import { initializeInnerAngularModule } from '../../../../get_inner_angular'; @@ -21,6 +21,7 @@ import { dataPluginMock } from '../../../../../../data/public/mocks'; import { initAngularBootstrap } from '../../../../../../kibana_legacy/public'; describe('debounce service', function () { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let debounce: (fn: () => void, timeout: number, options?: any) => any; let $timeout: ITimeoutService; let spy: SinonSpy; diff --git a/src/plugins/discover/public/application/angular/doc.ts b/src/plugins/discover/public/application/angular/doc.ts index 0745e21378956..27af3a96bbc84 100644 --- a/src/plugins/discover/public/application/angular/doc.ts +++ b/src/plugins/discover/public/application/angular/doc.ts @@ -7,17 +7,17 @@ */ import { getAngularModule, getServices } from '../../kibana_services'; -// @ts-ignore import { getRootBreadcrumbs } from '../helpers/breadcrumbs'; import html from './doc.html'; import { Doc } from '../components/doc/doc'; interface LazyScope extends ng.IScope { - [key: string]: any; + [key: string]: unknown; } const { timefilter } = getServices(); const app = getAngularModule(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any app.directive('discoverDoc', function (reactDirective: any) { return reactDirective( Doc, @@ -31,6 +31,7 @@ app.directive('discoverDoc', function (reactDirective: any) { ); }); +// eslint-disable-next-line @typescript-eslint/no-explicit-any app.config(($routeProvider: any) => { $routeProvider .when('/doc/:indexPattern/:index/:type', { @@ -39,7 +40,7 @@ app.config(($routeProvider: any) => { // the new route, es 7 deprecated types, es 8 removed them .when('/doc/:indexPattern/:index', { // have to be written as function expression, because it's not compiled in dev mode - // eslint-disable-next-line object-shorthand + // eslint-disable-next-line @typescript-eslint/no-explicit-any, object-shorthand controller: function ($scope: LazyScope, $route: any) { timefilter.disableAutoRefreshSelector(); timefilter.disableTimeRangeSelector(); @@ -49,6 +50,7 @@ app.config(($routeProvider: any) => { $scope.indexPatternService = getServices().indexPatterns; }, template: html, + // eslint-disable-next-line @typescript-eslint/no-explicit-any k7Breadcrumbs: ($route: any) => [ ...getRootBreadcrumbs(), { diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts b/src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts index 8b1df567db6cb..180da83beb2af 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/index.ts @@ -9,10 +9,12 @@ import { ToolBarPagerText } from './tool_bar_pager_text'; import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function createToolBarPagerTextDirective(reactDirective: any) { return reactDirective(ToolBarPagerText); } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function createToolBarPagerButtonsDirective(reactDirective: any) { return reactDirective(ToolBarPagerButtons); } diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts index dd6017674e5dc..5e3a025d8c7ba 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts @@ -11,6 +11,7 @@ import { getServices } from '../../../../kibana_services'; import { SORT_DEFAULT_ORDER_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; import { UI_SETTINGS } from '../../../../../../data/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function createTableHeaderDirective(reactDirective: any) { const { uiSettings: config } = getServices(); diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx index 8519b1e81b0a4..57f7382bd98ca 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { IndexPattern } from '../../../../../kibana_services'; -// @ts-ignore import { TableHeaderColumn } from './table_header_column'; import { SortOrder, getDisplayedColumns } from './helpers'; import { getDefaultSort } from '../../lib/get_default_sort'; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index cf6b507edc070..58ddf1eb7ba25 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -32,6 +32,7 @@ export function noWhiteSpace(html: string): string { const MIN_LINE_LENGTH = 20; interface LazyScope extends ng.IScope { + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } @@ -94,6 +95,7 @@ export function createTableRowDirective($compile: ng.ICompileService) { createSummaryRow($scope.row); }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any $scope.inlineFilter = function inlineFilter($event: any, type: string) { const column = $($event.currentTarget).data().column; const field = $scope.indexPattern.fields.getByName(column); @@ -119,6 +121,7 @@ export function createTableRowDirective($compile: ng.ICompileService) { }; // create a tr element that lists the value for each *column* + // eslint-disable-next-line @typescript-eslint/no-explicit-any function createSummaryRow(row: any) { const indexPattern = $scope.indexPattern; $scope.flattenedRow = indexPattern.flattenHit(row); @@ -188,7 +191,7 @@ export function createTableRowDirective($compile: ng.ICompileService) { const $cell = $cells.eq(i); if ($cell.data('discover:html') === html) return; - const reuse = find($cells.slice(i + 1), function (cell: any) { + const reuse = find($cells.slice(i + 1), (cell) => { return $.data(cell, 'discover:html') === html; }); @@ -222,6 +225,7 @@ export function createTableRowDirective($compile: ng.ICompileService) { /** * Fill an element with the value of a field */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any function _displayField(row: any, fieldName: string, truncate = false) { const indexPattern = $scope.indexPattern; const text = indexPattern.formatField(row, fieldName); diff --git a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx index 0202f88e0e902..b8e8bf1fe7d6c 100644 --- a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx @@ -45,6 +45,7 @@ export type AngularScope = IScope & { renderProps?: DocTableLegacyProps }; export async function injectAngularElement( domNode: Element, template: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any renderProps: any, injector: auto.IInjectorService ) { @@ -64,6 +65,7 @@ export async function injectAngularElement( return newScope; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function getRenderFn(domNode: Element, props: any) { const directive = { template: ` { + $scope.$watch('hits', (hits: unknown[]) => { if (!hits) return; // Reset infinite scroll limit diff --git a/src/plugins/discover/public/application/angular/doc_table/infinite_scroll.ts b/src/plugins/discover/public/application/angular/doc_table/infinite_scroll.ts index 82fa646513753..f2377b61b5151 100644 --- a/src/plugins/discover/public/application/angular/doc_table/infinite_scroll.ts +++ b/src/plugins/discover/public/application/angular/doc_table/infinite_scroll.ts @@ -9,6 +9,7 @@ import $ from 'jquery'; interface LazyScope extends ng.IScope { + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } @@ -19,6 +20,7 @@ export function createInfiniteScrollDirective() { more: '=', }, link: ($scope: LazyScope, $element: JQuery) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let checkTimer: any; /** * depending on which version of Discover is displayed, different elements are scrolling diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts index c73656435fb58..f181d583f0211 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_default_sort.test.ts @@ -7,7 +7,7 @@ */ import { getDefaultSort } from './get_default_sort'; -// @ts-ignore +// @ts-expect-error import FixturesStubbedLogstashIndexPatternProvider from '../../../../__fixtures__/stubbed_logstash_index_pattern'; import { IndexPattern } from '../../../../kibana_services'; diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts index bd28987b4fdbd..19d629e14da66 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts @@ -7,7 +7,7 @@ */ import { getSort, getSortArray } from './get_sort'; -// @ts-ignore +// @ts-expect-error import FixturesStubbedLogstashIndexPatternProvider from '../../../../__fixtures__/stubbed_logstash_index_pattern'; import { IndexPattern } from '../../../../kibana_services'; diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts index f0a13557af9fd..dc7817d95dd38 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort_for_search_source.test.ts @@ -7,7 +7,7 @@ */ import { getSortForSearchSource } from './get_sort_for_search_source'; -// @ts-ignore +// @ts-expect-error import FixturesStubbedLogstashIndexPatternProvider from '../../../../__fixtures__/stubbed_logstash_index_pattern'; import { IndexPattern } from '../../../../kibana_services'; import { SortOrder } from '../components/table_header/helpers'; diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/pager/pager_factory.ts b/src/plugins/discover/public/application/angular/doc_table/lib/pager/pager_factory.ts index 964ac585248b8..7cd36d419969e 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/pager/pager_factory.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/pager/pager_factory.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -// @ts-ignore +// @ts-expect-error import { Pager } from './pager'; export function createPagerFactory() { diff --git a/src/plugins/discover/public/application/angular/doc_viewer.tsx b/src/plugins/discover/public/application/angular/doc_viewer.tsx index 0b4f3c3cf036a..2b51b68b2fb34 100644 --- a/src/plugins/discover/public/application/angular/doc_viewer.tsx +++ b/src/plugins/discover/public/application/angular/doc_viewer.tsx @@ -9,9 +9,10 @@ import React from 'react'; import { DocViewer } from '../components/doc_viewer/doc_viewer'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function createDocViewerDirective(reactDirective: any) { return reactDirective( - (props: any) => { + (props: React.ComponentProps) => { return ; }, [ diff --git a/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts index ca5cdbd808606..80fb8a570f78b 100644 --- a/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts +++ b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts @@ -11,6 +11,7 @@ import { stubbedSavedObjectIndexPattern } from '../../../__mocks__/stubbed_saved import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; import { fieldFormatsMock } from '../../../../../data/common/field_formats/mocks'; import { setServices } from '../../../kibana_services'; +import { DiscoverServices } from '../../../build_services'; describe('Row formatter', () => { const hit = { @@ -59,11 +60,11 @@ describe('Row formatter', () => { beforeEach(() => { // @ts-expect-error indexPattern.formatHit = formatHitMock; - setServices({ + setServices(({ uiSettings: { get: () => 100, }, - }); + } as unknown) as DiscoverServices); }); it('formats document properly', () => { @@ -73,11 +74,11 @@ describe('Row formatter', () => { }); it('limits number of rendered items', () => { - setServices({ + setServices(({ uiSettings: { get: () => 1, }, - }); + } as unknown) as DiscoverServices); expect(formatRow(hit, indexPattern).trim()).toMatchInlineSnapshot( `"
also:
with \\\\"quotes\\\\" or 'single qoutes'
"` ); diff --git a/src/plugins/discover/public/application/angular/helpers/row_formatter.tsx b/src/plugins/discover/public/application/angular/helpers/row_formatter.tsx index c5e64f38a3117..c410273cc7510 100644 --- a/src/plugins/discover/public/application/angular/helpers/row_formatter.tsx +++ b/src/plugins/discover/public/application/angular/helpers/row_formatter.tsx @@ -30,6 +30,7 @@ const TemplateComponent = ({ defPairs }: Props) => { ); }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const formatRow = (hit: Record, indexPattern: IndexPattern) => { const highlights = hit?.highlight ?? {}; // Keys are sorted in the hits object @@ -49,7 +50,9 @@ export const formatRow = (hit: Record, indexPattern: IndexPattern) }; export const formatTopLevelObject = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any row: Record, + // eslint-disable-next-line @typescript-eslint/no-explicit-any fields: Record, indexPattern: IndexPattern ) => { diff --git a/src/plugins/discover/public/application/angular/redirect.ts b/src/plugins/discover/public/application/angular/redirect.ts index a1717baf9226f..5014376ff13af 100644 --- a/src/plugins/discover/public/application/angular/redirect.ts +++ b/src/plugins/discover/public/application/angular/redirect.ts @@ -8,8 +8,10 @@ import { getAngularModule, getServices, getUrlTracker } from '../../kibana_services'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any getAngularModule().config(($routeProvider: any) => { $routeProvider.otherwise({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any resolveRedirectTo: ($rootScope: any) => { const path = window.location.hash.substr(1); getUrlTracker().restorePreviousUrl(); diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx index 5031f78c49fcc..55c2208105f13 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx @@ -15,7 +15,7 @@ import { DocTableLegacyProps, } from '../../angular/doc_table/create_doc_table_react'; import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns'; -import { LOADING_STATUS } from './constants'; +import { LoadingStatus } from '../../angular/context_app_state'; import { ActionBar, ActionBarProps } from '../../angular/context/components/action_bar/action_bar'; import { TopNavMenuProps } from '../../../../../navigation/public'; @@ -45,13 +45,13 @@ const PREDECESSOR_TYPE = 'predecessors'; const SUCCESSOR_TYPE = 'successors'; function isLoading(status: string) { - return status !== LOADING_STATUS.LOADED && status !== LOADING_STATUS.FAILED; + return status !== LoadingStatus.LOADED && status !== LoadingStatus.FAILED; } export function ContextAppLegacy(renderProps: ContextAppProps) { const status = renderProps.status; - const isLoaded = status === LOADING_STATUS.LOADED; - const isFailed = status === LOADING_STATUS.FAILED; + const isLoaded = status === LoadingStatus.LOADED; + const isFailed = status === LoadingStatus.FAILED; const actionBarProps = (type: string) => { const { @@ -114,7 +114,7 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { }; const loadingFeedback = () => { - if (status === LOADING_STATUS.UNINITIALIZED || status === LOADING_STATUS.LOADING) { + if (status === LoadingStatus.UNINITIALIZED || status === LoadingStatus.LOADING) { return ( diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts index 5b0ef60b6079a..fc64abfb51025 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts @@ -8,6 +8,7 @@ import { ContextAppLegacy } from './context_app_legacy'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function createContextAppLegacy(reactDirective: any) { return reactDirective(ContextAppLegacy, [ ['filter', { watchDepth: 'reference' }], diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx index 3edd8f2514e63..e4b5e3b95c3f3 100644 --- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx +++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx @@ -10,32 +10,31 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import { ContextErrorMessage } from './context_error_message'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; +import { FailureReason, LoadingStatus } from '../../angular/context_app_state'; import { findTestSubject } from '@elastic/eui/lib/test'; describe('loading spinner', function () { let component: ReactWrapper; it('ContextErrorMessage does not render on loading', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); }); it('ContextErrorMessage does not render on success loading', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); }); it('ContextErrorMessage renders just the title if the reason is not specifically handled', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); expect(findTestSubject(component, 'contextErrorMessageBody').text()).toBe(''); }); it('ContextErrorMessage renders the reason for unknown errors', () => { component = mountWithIntl( - + ); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); expect(findTestSubject(component, 'contextErrorMessageBody').length).toBe(1); diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx index 83cb6981d761e..85dc67e7029ee 100644 --- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx +++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx @@ -9,8 +9,7 @@ import React from 'react'; import { EuiCallOut, EuiText } from '@elastic/eui'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; +import { FailureReason, LoadingStatus } from '../../angular/context_app_state'; export interface ContextErrorMessageProps { /** @@ -24,7 +23,7 @@ export interface ContextErrorMessageProps { } export function ContextErrorMessage({ status, reason }: ContextErrorMessageProps) { - if (status !== LOADING_STATUS.FAILED) { + if (status !== LoadingStatus.FAILED) { return null; } return ( @@ -41,7 +40,7 @@ export function ContextErrorMessage({ status, reason }: ContextErrorMessageProps data-test-subj="contextErrorMessageTitle" > - {reason === FAILURE_REASONS.UNKNOWN && ( + {reason === FailureReason.UNKNOWN && (
} rowIndex={1} columnId={'extension'} @@ -59,6 +60,7 @@ describe('Discover cell actions ', function () { const component = mountWithIntl( } rowIndex={1} columnId={'extension'} diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx index d258c5d36ff75..fc3dd499f92e0 100644 --- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx @@ -115,7 +115,7 @@ export const getRenderCellValueFn = ( if (typeof rowFlattened[columnId] === 'object' && isDetails) { return ( } + json={rowFlattened[columnId] as Record} width={defaultMonacoEditorWidth} /> ); @@ -123,7 +123,8 @@ export const getRenderCellValueFn = ( if (field && field.type === '_source') { if (isDetails) { - return ; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return ; } const formatted = indexPattern.formatHit(row); diff --git a/src/plugins/discover/public/application/components/doc/doc.test.tsx b/src/plugins/discover/public/application/components/doc/doc.test.tsx index deaaa1853ae9d..7367315eae7fb 100644 --- a/src/plugins/discover/public/application/components/doc/doc.test.tsx +++ b/src/plugins/discover/public/application/components/doc/doc.test.tsx @@ -18,6 +18,7 @@ import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../. const mockSearchApi = jest.fn(); jest.mock('../../../kibana_services', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let registry: any[] = []; return { @@ -46,6 +47,7 @@ jest.mock('../../../kibana_services', () => { }, }), getDocViewsRegistry: () => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any addDocView(view: any) { registry.push(view); }, @@ -72,12 +74,14 @@ const waitForPromises = async () => * this works but logs ugly error messages until we're using React 16.9 * should be adapted when we upgrade */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any async function mountDoc(update = false, indexPatternGetter: any = null) { const indexPattern = { getComputedFields: () => [], }; const indexPatternService = { get: indexPatternGetter ? indexPatternGetter : jest.fn(() => Promise.resolve(indexPattern)), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; const props = { diff --git a/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx b/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx index b5cdc7db6c6c9..f3a6b274649f5 100644 --- a/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx +++ b/src/plugins/discover/public/application/components/doc/use_es_doc_search.test.tsx @@ -11,6 +11,7 @@ import { buildSearchBody, useEsDocSearch, ElasticRequestState } from './use_es_d import { DocProps } from './doc'; import { Observable } from 'rxjs'; import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../common'; +import { IndexPattern } from 'src/plugins/data/common'; const mockSearchResult = new Observable(); @@ -35,60 +36,60 @@ jest.mock('../../../kibana_services', () => ({ describe('Test of helper / hook', () => { test('buildSearchBody given useNewFieldsApi is false', () => { - const indexPattern = { + const indexPattern = ({ getComputedFields: () => ({ storedFields: [], scriptFields: [], docvalueFields: [] }), - } as any; + } as unknown) as IndexPattern; const actual = buildSearchBody('1', indexPattern, false); expect(actual).toMatchInlineSnapshot(` Object { - "_source": true, - "docvalue_fields": Array [], - "fields": undefined, - "query": Object { - "ids": Object { - "values": Array [ - "1", - ], + "body": Object { + "_source": true, + "fields": Array [], + "query": Object { + "ids": Object { + "values": Array [ + "1", + ], + }, }, + "script_fields": Array [], + "stored_fields": Array [], }, - "runtime_mappings": Object {}, - "script_fields": Array [], - "stored_fields": Array [], } `); }); test('buildSearchBody useNewFieldsApi is true', () => { - const indexPattern = { + const indexPattern = ({ getComputedFields: () => ({ storedFields: [], scriptFields: [], docvalueFields: [] }), - } as any; + } as unknown) as IndexPattern; const actual = buildSearchBody('1', indexPattern, true); expect(actual).toMatchInlineSnapshot(` Object { - "_source": false, - "docvalue_fields": Array [], - "fields": Array [ - Object { - "field": "*", - "include_unmapped": "true", - }, - ], - "query": Object { - "ids": Object { - "values": Array [ - "1", - ], + "body": Object { + "fields": Array [ + Object { + "field": "*", + "include_unmapped": "true", + }, + ], + "query": Object { + "ids": Object { + "values": Array [ + "1", + ], + }, }, + "runtime_mappings": Object {}, + "script_fields": Array [], + "stored_fields": Array [], }, - "runtime_mappings": Object {}, - "script_fields": Array [], - "stored_fields": Array [], } `); }); test('buildSearchBody with runtime fields', () => { - const indexPattern = { + const indexPattern = ({ getComputedFields: () => ({ storedFields: [], scriptFields: [], @@ -102,35 +103,35 @@ describe('Test of helper / hook', () => { }, }, }), - } as any; + } as unknown) as IndexPattern; const actual = buildSearchBody('1', indexPattern, true); expect(actual).toMatchInlineSnapshot(` Object { - "_source": false, - "docvalue_fields": Array [], - "fields": Array [ - Object { - "field": "*", - "include_unmapped": "true", - }, - ], - "query": Object { - "ids": Object { - "values": Array [ - "1", - ], + "body": Object { + "fields": Array [ + Object { + "field": "*", + "include_unmapped": "true", + }, + ], + "query": Object { + "ids": Object { + "values": Array [ + "1", + ], + }, }, - }, - "runtime_mappings": Object { - "myRuntimeField": Object { - "script": Object { - "source": "emit(10.0)", + "runtime_mappings": Object { + "myRuntimeField": Object { + "script": Object { + "source": "emit(10.0)", + }, + "type": "double", }, - "type": "double", }, + "script_fields": Array [], + "stored_fields": Array [], }, - "script_fields": Array [], - "stored_fields": Array [], } `); }); @@ -139,21 +140,22 @@ describe('Test of helper / hook', () => { const indexPattern = { getComputedFields: () => [], }; - const indexPatternService = { - get: jest.fn(() => Promise.resolve(indexPattern)), - } as any; - const props = { + const getMock = jest.fn(() => Promise.resolve(indexPattern)); + const indexPatternService = ({ + get: getMock, + } as unknown) as IndexPattern; + const props = ({ id: '1', index: 'index1', indexPatternId: 'xyz', indexPatternService, - } as DocProps; - let hook; + } as unknown) as DocProps; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let hook: any; await act(async () => { hook = renderHook((p: DocProps) => useEsDocSearch(p), { initialProps: props }); }); - // @ts-ignore expect(hook.result.current).toEqual([ElasticRequestState.Loading, null, indexPattern]); - expect(indexPatternService.get).toHaveBeenCalled(); + expect(getMock).toHaveBeenCalled(); }); }); diff --git a/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts b/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts index 0dcf828048a07..6b8e912bbffba 100644 --- a/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts +++ b/src/plugins/discover/public/application/components/doc/use_es_doc_search.ts @@ -7,11 +7,14 @@ */ import { useEffect, useState, useMemo } from 'react'; +import type { estypes } from '@elastic/elasticsearch'; import { IndexPattern, getServices } from '../../../kibana_services'; import { DocProps } from './doc'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common'; +type RequestBody = Pick; + export enum ElasticRequestState { Loading, NotFound, @@ -28,22 +31,32 @@ export function buildSearchBody( id: string, indexPattern: IndexPattern, useNewFieldsApi: boolean -): Record { +): RequestBody | undefined { const computedFields = indexPattern.getComputedFields(); - const runtimeFields = indexPattern.getComputedFields().runtimeFields; - return { - query: { - ids: { - values: [id], + const runtimeFields = computedFields.runtimeFields as estypes.RuntimeFields; + const request: RequestBody = { + body: { + query: { + ids: { + values: [id], + }, }, + stored_fields: computedFields.storedFields, + script_fields: computedFields.scriptFields, }, - stored_fields: computedFields.storedFields, - _source: !useNewFieldsApi, - fields: useNewFieldsApi ? [{ field: '*', include_unmapped: 'true' }] : undefined, - script_fields: computedFields.scriptFields, - docvalue_fields: computedFields.docvalueFields, - runtime_mappings: useNewFieldsApi && runtimeFields ? runtimeFields : {}, // needed for index pattern runtime fields in a single doc view }; + if (!request.body) { + return undefined; + } + if (useNewFieldsApi) { + // @ts-expect-error + request.body.fields = [{ field: '*', include_unmapped: 'true' }]; + request.body.runtime_mappings = runtimeFields ? runtimeFields : {}; + } else { + request.body._source = true; + } + request.body.fields = [...(request.body?.fields || []), ...(computedFields.docvalueFields || [])]; + return request; } /** @@ -71,7 +84,7 @@ export function useEsDocSearch({ .search({ params: { index, - body: buildSearchBody(id, indexPatternEntity, useNewFieldsApi), + body: buildSearchBody(id, indexPatternEntity, useNewFieldsApi)?.body, }, }) .toPromise(); diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx index 6afa7f89371f9..de0353a020a67 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx @@ -14,6 +14,7 @@ import { getDocViewsRegistry } from '../../../kibana_services'; import { DocViewRenderProps } from '../../doc_views/doc_views_types'; jest.mock('../../../kibana_services', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let registry: any[] = []; return { getServices: () => ({ @@ -22,6 +23,7 @@ jest.mock('../../../kibana_services', () => { }, }), getDocViewsRegistry: () => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any addDocView(view: any) { registry.push(view); }, @@ -36,6 +38,7 @@ jest.mock('../../../kibana_services', () => { }); beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any (getDocViewsRegistry() as any).resetRegistry(); jest.clearAllMocks(); }); @@ -44,6 +47,7 @@ test('Render with 3 different tabs', () => { const registry = getDocViewsRegistry(); registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() }); registry.addDocView({ order: 20, title: 'React component', component: () =>
test
}); + // @ts-expect-error This should be invalid and will throw an error when rendering registry.addDocView({ order: 30, title: 'Invalid doc view' }); const renderProps = { hit: {} } as DocViewRenderProps; diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx index 1ad6500771d48..4ca53d929eeab 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx @@ -16,11 +16,11 @@ import { getServices } from '../../../kibana_services'; import { KibanaContextProvider } from '../../../../../kibana_react/public'; interface Props { - component?: React.ComponentType; id: number; - render?: DocViewRenderFn; renderProps: DocViewRenderProps; title: string; + render?: DocViewRenderFn; + component?: React.ComponentType; } interface State { @@ -53,17 +53,11 @@ export class DocViewerTab extends React.Component { } render() { - const { component, render, renderProps, title } = this.props; + const { component: Component, render, renderProps, title } = this.props; const { hasError, error } = this.state; if (hasError && error) { return ; - } else if (!render && !component) { - return ( - - ); } if (render) { @@ -72,14 +66,20 @@ export class DocViewerTab extends React.Component { } // doc view is provided by a react component + if (Component) { + return ( + + + + + + ); + } - const Component = component as any; return ( - - - - - + ); } } diff --git a/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx index 50a29dde85891..b8427bb6bbdd2 100644 --- a/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx +++ b/src/plugins/discover/public/application/components/json_code_editor/json_code_editor.tsx @@ -22,7 +22,7 @@ const copyToClipboardLabel = i18n.translate('discover.json.copyToClipboardLabel' }); interface JsonCodeEditorProps { - json: Record; + json: Record; width?: string | number; hasLineNumbers?: boolean; } diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index 54a2de14e2e26..2041a8dfdc637 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import { DiscoverField } from './discover_field'; @@ -49,7 +49,7 @@ function getComponent({ }) { const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx index 0113213f70c88..f82154af33d1e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import { DiscoverFieldDetails } from './discover_field_details'; @@ -18,7 +18,7 @@ import { getStubIndexPattern } from '../../../../../data/public/test_utils'; const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx index 07baeddf034ef..73e906cb62f5f 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details_footer.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import { coreMock } from '../../../../../../core/public/mocks'; @@ -18,7 +18,7 @@ import { DiscoverFieldDetailsFooter } from './discover_field_details_footer'; const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx index ec680b062c9ba..145053de1f21c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx @@ -47,7 +47,7 @@ describe('DiscoverFieldSearch', () => { const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); act(() => { - // @ts-ignore + // @ts-expect-error (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); }); component.update(); @@ -66,7 +66,7 @@ describe('DiscoverFieldSearch', () => { // change value of aggregatable select const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); act(() => { - // @ts-ignore + // @ts-expect-error (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); }); component.update(); @@ -74,14 +74,14 @@ describe('DiscoverFieldSearch', () => { // change value of searchable select const searchableButtonGroup = findButtonGroup(component, 'searchable'); act(() => { - // @ts-ignore + // @ts-expect-error (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-true', null); }); component.update(); expect(badge.text()).toEqual('2'); // change value of searchable select act(() => { - // @ts-ignore + // @ts-expect-error (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-any', null); }); component.update(); @@ -114,7 +114,7 @@ describe('DiscoverFieldSearch', () => { const aggregtableButtonGroup = findButtonGroup(component, 'aggregatable'); const missingSwitch = findTestSubject(component, 'missingSwitch'); act(() => { - // @ts-ignore + // @ts-expect-error (aggregtableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); }); missingSwitch.simulate('click'); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx index 73de3b14f88f6..f6d577de564ad 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx @@ -14,7 +14,7 @@ import { ChangeIndexPattern } from './change_indexpattern'; import { SavedObject } from 'kibana/server'; import { DiscoverIndexPattern, DiscoverIndexPatternProps } from './discover_index_pattern'; import { EuiSelectable } from '@elastic/eui'; -import { IndexPattern } from 'src/plugins/data/public'; +import { IndexPattern, IndexPatternAttributes } from 'src/plugins/data/public'; import { configMock } from '../../../__mocks__/config'; import { indexPatternsMock } from '../../../__mocks__/index_patterns'; @@ -28,14 +28,14 @@ const indexPattern1 = { attributes: { title: 'test1 title', }, -} as SavedObject; +} as SavedObject; const indexPattern2 = { id: 'the-index-pattern-id', attributes: { title: 'test2 title', }, -} as SavedObject; +} as SavedObject; const defaultProps = { config: configMock, @@ -58,6 +58,7 @@ function getIndexPatternPickerOptions(instance: ShallowWrapper) { function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) { const options: Array<{ label: string; checked?: 'on' | 'off' }> = getIndexPatternPickerOptions( instance + // eslint-disable-next-line @typescript-eslint/no-explicit-any ).map((option: any) => option.label === selectedLabel ? { ...option, checked: 'on' } @@ -79,6 +80,7 @@ describe('DiscoverIndexPattern', () => { test('should list all index patterns', () => { const instance = shallow(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(getIndexPatternPickerOptions(instance)!.map((option: any) => option.label)).toEqual([ 'test1 title', 'test2 title', diff --git a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern_management.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern_management.test.tsx index 5a954270fdf58..6f9ff63d69782 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern_management.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern_management.test.tsx @@ -9,7 +9,7 @@ import { getStubIndexPattern } from '../../../../../data/public/index_patterns/index_pattern.stub'; import { coreMock } from '../../../../../../core/public/mocks'; import { DiscoverServices } from '../../../build_services'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; @@ -56,7 +56,7 @@ const mockServices = ({ describe('Discover IndexPattern Management', () => { const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx index 01541344be7e1..35d93095a1da3 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx @@ -9,9 +9,9 @@ import { each, cloneDeep } from 'lodash'; import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore +// @ts-expect-error import realHits from '../../../__fixtures__/real_hits.js'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; @@ -67,7 +67,7 @@ jest.mock('./lib/get_index_pattern_field_list', () => ({ function getCompProps(): DiscoverSidebarProps { const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.test.tsx index caec61cc501b9..caa0e436f4091 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_responsive.test.tsx @@ -9,9 +9,9 @@ import { each, cloneDeep } from 'lodash'; import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore +// @ts-expect-error import realHits from '../../../__fixtures__/real_hits.js'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../__fixtures__/logstash_fields'; import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; @@ -63,7 +63,7 @@ jest.mock('./lib/get_index_pattern_field_list', () => ({ function getCompProps(): DiscoverSidebarResponsiveProps { const indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts index faa31dde1bb80..2cdd99774c2a8 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts @@ -6,15 +6,17 @@ * Side Public License, v 1. */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import _ from 'lodash'; -// @ts-ignore +// @ts-expect-error import realHits from '../../../../__fixtures__/real_hits.js'; -// @ts-ignore +// @ts-expect-error import stubbedLogstashFields from '../../../../__fixtures__/logstash_fields'; import { coreMock } from '../../../../../../../core/public/mocks'; import { IndexPattern } from '../../../../../../data/public'; import { getStubIndexPattern } from '../../../../../../data/public/test_utils'; -// @ts-ignore +// @ts-expect-error import { fieldCalculator } from './field_calculator'; let indexPattern: IndexPattern; @@ -23,7 +25,7 @@ describe('fieldCalculator', function () { beforeEach(function () { indexPattern = getStubIndexPattern( 'logstash-*', - (cfg: any) => cfg, + (cfg: unknown) => cfg, 'time', stubbedLogstashFields(), coreMock.createSetup() diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 2cea3270e5166..1e35717d249f8 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -// @ts-ignore +// @ts-expect-error import { fieldCalculator } from './field_calculator'; import { IndexPattern, IndexPatternField } from '../../../../../../data/public'; import { ElasticSearchHit } from '../../../doc_views/doc_views_types'; diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts index 68099fb0c8e2a..e8eb07784cf9f 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts @@ -167,18 +167,11 @@ describe('group_fields', function () { aggregatable: true, readFromDocValues: false, }; - const fieldsToGroup = [category, currency, currencyKeyword]; + const fieldsToGroup = [category, currency, currencyKeyword] as IndexPatternField[]; const fieldFilterState = getDefaultFieldFilter(); - const actual = groupFields( - fieldsToGroup as any, - ['currency'], - 5, - fieldCounts, - fieldFilterState, - true - ); + const actual = groupFields(fieldsToGroup, ['currency'], 5, fieldCounts, fieldFilterState, true); expect(actual.popular).toEqual([category]); expect(actual.selected).toEqual([currency]); diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index 7539f29c1ec9d..e42288d52be8e 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -11,6 +11,7 @@ import { mount } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; import { DocViewTable } from './table'; import { indexPatterns, IndexPattern } from '../../../../../data/public'; +import { ElasticSearchHit } from '../../doc_views/doc_views_types'; const indexPattern = ({ fields: { @@ -87,7 +88,7 @@ describe('DocViewTable at Discover', () => { scripted: 123, _underscore: 123, }, - } as any; + } as ElasticSearchHit; const props = { hit, @@ -185,7 +186,7 @@ describe('DocViewTable at Discover Context', () => { Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \ Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut', }, - } as any; + } as ElasticSearchHit; const props = { hit, columns: ['extension'], @@ -312,7 +313,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { }, metaFields: ['_index', '_type', '_score', '_id'], flattenHit: jest.fn((hit) => { - const result = {} as Record; + const result = {} as Record; Object.keys(hit).forEach((key) => { if (key !== 'fields') { result[key] = hit[key]; @@ -325,7 +326,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { return result; }), formatHit: jest.fn((hit) => { - const result = {} as Record; + const result = {} as Record; Object.keys(hit).forEach((key) => { if (key !== 'fields') { result[key] = hit[key]; @@ -347,7 +348,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { _index: 'logstash-2014.09.09', _type: 'doc', _id: 'id123', - _score: null, + _score: 1.0, fields: { category: "Women's Clothing", 'category.keyword': "Women's Clothing", @@ -364,7 +365,6 @@ describe('DocViewTable at Discover Doc with Fields API', () => { onAddColumn: jest.fn(), onRemoveColumn: jest.fn(), }; - // @ts-ignore const component = mount(); it('renders multifield rows', () => { const categoryMultifieldRow = findTestSubject( diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx index 74836711373b2..0da1fdd92d2cc 100644 --- a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx +++ b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx @@ -79,10 +79,10 @@ describe('timechart header', function () { component = mountWithIntl(); const dropdown = findTestSubject(component, 'discoverIntervalSelect'); expect(dropdown.length).toBe(1); - // @ts-ignore + // @ts-expect-error const values = dropdown.find('option').map((option) => option.prop('value')); expect(values).toEqual(['auto', 'ms', 's']); - // @ts-ignore + // @ts-expect-error const labels = dropdown.find('option').map((option) => option.text()); expect(labels).toEqual(['Auto', 'Millisecond', 'Second']); }); diff --git a/src/plugins/discover/public/application/doc_views/doc_views_helpers.tsx b/src/plugins/discover/public/application/doc_views/doc_views_helpers.tsx index 7acb56ea5535c..a590a9c05eda4 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_helpers.tsx +++ b/src/plugins/discover/public/application/doc_views/doc_views_helpers.tsx @@ -32,6 +32,7 @@ export async function injectAngularElement( if (typeof Controller === 'function') { // when a controller is defined, expose the value it produces to the view as `$ctrl` // see: https://docs.angularjs.org/api/ng/provider/$compileProvider#component + // eslint-disable-next-line @typescript-eslint/no-explicit-any (newScope as any).$ctrl = $injector.instantiate(Controller, { $scope: newScope, }); diff --git a/src/plugins/discover/public/application/doc_views/doc_views_registry.ts b/src/plugins/discover/public/application/doc_views/doc_views_registry.ts index da36be1b53a78..ad341d07aae5a 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_registry.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_registry.ts @@ -25,7 +25,8 @@ export class DocViewsRegistry { const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw; if (docView.directive) { // convert angular directive to render function for backwards compatibility - docView.render = convertDirectiveToRenderFn(docView.directive, () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (docView.render as any) = convertDirectiveToRenderFn(docView.directive as any, () => { if (!this.angularInjectorGetter) { throw new Error('Angular was not initialized'); } diff --git a/src/plugins/discover/public/application/doc_views/doc_views_types.ts b/src/plugins/discover/public/application/doc_views/doc_views_types.ts index 02ac951f7f57c..58399f31e032f 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_types.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_types.ts @@ -12,7 +12,7 @@ import type { estypes } from '@elastic/elasticsearch'; import { IndexPattern } from '../../../../data/public'; export interface AngularDirective { - controller: (...injectedServices: any[]) => void; + controller: (...injectedServices: unknown[]) => void; template: string; } @@ -49,17 +49,34 @@ export type DocViewRenderFn = ( renderProps: DocViewRenderProps ) => () => void; -export interface DocViewInput { - component?: DocViewerComponent; - directive?: AngularDirective; +export interface BaseDocViewInput { order: number; - render?: DocViewRenderFn; shouldShow?: (hit: ElasticSearchHit) => boolean; title: string; } -export interface DocView extends DocViewInput { - shouldShow: (hit: ElasticSearchHit) => boolean; +export interface RenderDocViewInput extends BaseDocViewInput { + render: DocViewRenderFn; + component?: undefined; + directive?: undefined; } +interface ComponentDocViewInput extends BaseDocViewInput { + component: DocViewerComponent; + render?: undefined; + directive?: undefined; +} + +interface DirectiveDocViewInput extends BaseDocViewInput { + component?: undefined; + render?: undefined; + directive: ng.IDirective; +} + +export type DocViewInput = ComponentDocViewInput | RenderDocViewInput | DirectiveDocViewInput; + +export type DocView = DocViewInput & { + shouldShow: NonNullable; +}; + export type DocViewInputFn = () => DocViewInput; diff --git a/src/plugins/discover/public/application/helpers/breadcrumbs.ts b/src/plugins/discover/public/application/helpers/breadcrumbs.ts index 1a190b3882240..8a8d0e7027c65 100644 --- a/src/plugins/discover/public/application/helpers/breadcrumbs.ts +++ b/src/plugins/discover/public/application/helpers/breadcrumbs.ts @@ -21,6 +21,7 @@ export function getRootBreadcrumbs() { ]; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function getSavedSearchBreadcrumbs($route: any) { return [ ...getRootBreadcrumbs(), diff --git a/src/plugins/discover/public/application/helpers/calc_field_counts.ts b/src/plugins/discover/public/application/helpers/calc_field_counts.ts index 1f00faaf56c39..edeaa0b9bce52 100644 --- a/src/plugins/discover/public/application/helpers/calc_field_counts.ts +++ b/src/plugins/discover/public/application/helpers/calc_field_counts.ts @@ -14,7 +14,7 @@ import { IndexPattern } from '../../kibana_services'; */ export function calcFieldCounts( counts = {} as Record, - rows: Array>, + rows: Array>, indexPattern: IndexPattern ) { for (const hit of rows) { diff --git a/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts b/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts index fd92dc8259a1f..f87d5328046d8 100644 --- a/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts +++ b/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts @@ -16,7 +16,7 @@ import { Query } from 'src/plugins/data/public'; * @return Object */ -export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { +export function migrateLegacyQuery(query: Query | { [key: string]: unknown } | string): Query { // Lucene was the only option before, so language-less queries are all lucene if (!has(query, 'language')) { return { query, language: 'lucene' }; diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index cf95d5a85b9f2..19ee06aa51798 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -8,6 +8,7 @@ import { History } from 'history'; +import type { auto } from 'angular'; import { Capabilities, ChromeStart, @@ -57,7 +58,7 @@ export interface DiscoverServices { toastNotifications: ToastsStart; getSavedSearchById: (id: string) => Promise; getSavedSearchUrlById: (id: string) => Promise; - getEmbeddableInjector: any; + getEmbeddableInjector: () => Promise; uiSettings: IUiSettingsClient; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; indexPatternFieldEditor: IndexPatternFieldEditorStart; @@ -67,7 +68,7 @@ export async function buildServices( core: CoreStart, plugins: DiscoverStartPlugins, context: PluginInitializerContext, - getEmbeddableInjector: any + getEmbeddableInjector: () => Promise ): Promise { const services = { savedObjectsClient: core.savedObjects.client, diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index d78e53d99f6a2..c36312a87ce9a 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -150,7 +150,7 @@ function createLocalStorageModule() { } const createLocalStorageService = function (type: string) { - return function ($window: any) { + return function ($window: ng.IWindowService) { return new Storage($window[type]); }; }; diff --git a/src/plugins/discover/public/kibana_services.ts b/src/plugins/discover/public/kibana_services.ts index e4b0035ed0e03..c2ab4ae34c958 100644 --- a/src/plugins/discover/public/kibana_services.ts +++ b/src/plugins/discover/public/kibana_services.ts @@ -15,21 +15,24 @@ import { createGetterSetter } from '../../kibana_utils/public'; import { search } from '../../data/public'; import { DocViewsRegistry } from './application/doc_views/doc_views_registry'; -let angularModule: any = null; +let angularModule: ng.IModule | null = null; let services: DiscoverServices | null = null; let uiActions: UiActionsStart; /** * set bootstrapped inner angular module */ -export function setAngularModule(module: any) { +export function setAngularModule(module: ng.IModule) { angularModule = module; } /** * get boostrapped inner angular module */ -export function getAngularModule() { +export function getAngularModule(): ng.IModule { + if (!angularModule) { + throw new Error('Discover angular module not yet available'); + } return angularModule; } @@ -40,7 +43,7 @@ export function getServices(): DiscoverServices { return services; } -export function setServices(newServices: any) { +export function setServices(newServices: DiscoverServices) { services = newServices; } diff --git a/src/plugins/discover/public/mocks.ts b/src/plugins/discover/public/mocks.ts index bdad98a457a5c..0f57c5c0fa138 100644 --- a/src/plugins/discover/public/mocks.ts +++ b/src/plugins/discover/public/mocks.ts @@ -22,10 +22,10 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { - savedSearchLoader: {} as any, - urlGenerator: { + savedSearchLoader: {} as DiscoverStart['savedSearchLoader'], + urlGenerator: ({ createUrl: jest.fn(), - } as any, + } as unknown) as DiscoverStart['urlGenerator'], }; return startContract; }; diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 692704c92356e..3f55ab76372bc 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -164,7 +164,10 @@ export class DiscoverPlugin public initializeInnerAngular?: () => void; public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; - setup(core: CoreSetup, plugins: DiscoverSetupPlugins) { + setup( + core: CoreSetup, + plugins: DiscoverSetupPlugins + ): DiscoverSetup { const baseUrl = core.http.basePath.prepend('/app/discover'); if (plugins.share) { @@ -190,7 +193,8 @@ export class DiscoverPlugin defaultMessage: 'JSON', }), order: 20, - component: ({ hit }) => , + // eslint-disable-next-line @typescript-eslint/no-explicit-any + component: ({ hit }) => , }); const { diff --git a/src/plugins/discover/public/url_generator.test.ts b/src/plugins/discover/public/url_generator.test.ts index 2d29111fd3757..765e8b36cc1ea 100644 --- a/src/plugins/discover/public/url_generator.test.ts +++ b/src/plugins/discover/public/url_generator.test.ts @@ -31,7 +31,7 @@ const setup = async ({ useHash = false }: SetupParams = {}) => { }; beforeEach(() => { - // @ts-ignore + // @ts-expect-error hashedItemStore.storage = mockStorage; }); diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index b66c06db3e120..070f0253f17e0 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -47,5 +47,5 @@ export const searchSavedObjectType: SavedObjectsType = { version: { type: 'integer' }, }, }, - migrations: searchMigrations as any, + migrations: searchMigrations, }; diff --git a/src/plugins/discover/server/saved_objects/search_migrations.ts b/src/plugins/discover/server/saved_objects/search_migrations.ts index feaf91409797a..0c45db5cd779e 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.ts @@ -6,6 +6,9 @@ * Side Public License, v 1. */ +// TODO: This needs to be removed and properly typed +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { flow, get } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; diff --git a/src/plugins/inspector/public/views/requests/_requests.scss b/src/plugins/inspector/public/views/requests/_requests.scss index 273c9d0ccba2b..ac6414d33684c 100644 --- a/src/plugins/inspector/public/views/requests/_requests.scss +++ b/src/plugins/inspector/public/views/requests/_requests.scss @@ -12,3 +12,7 @@ .insRequestSelector__menuSpinner { margin-left: $euiSizeS; } + +.insRequestCodeViewer .react-monaco-editor-container { + flex-grow: 1; // Ensure the editor takes the full height of its flex container on Safari. +} diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index c5a2550723814..06d1cd290ffd5 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -440,4 +440,8 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'labs:canvas:useDataService': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 4dc1773ecfbe2..dfbe6bd3e0485 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -119,5 +119,6 @@ export interface UsageStats { 'banners:textColor': string; 'banners:backgroundColor': string; 'labs:canvas:enable_ui': boolean; + 'labs:canvas:useDataService': boolean; 'labs:presentation:timeToPresent': boolean; } diff --git a/src/plugins/presentation_util/common/labs.ts b/src/plugins/presentation_util/common/labs.ts index ce7855c516c8b..d551b733ecb8a 100644 --- a/src/plugins/presentation_util/common/labs.ts +++ b/src/plugins/presentation_util/common/labs.ts @@ -8,9 +8,10 @@ import { i18n } from '@kbn/i18n'; +export const USE_DATA_SERVICE = 'labs:canvas:useDataService'; export const TIME_TO_PRESENT = 'labs:presentation:timeToPresent'; -export const projectIDs = [TIME_TO_PRESENT] as const; +export const projectIDs = [TIME_TO_PRESENT, USE_DATA_SERVICE] as const; export const environmentNames = ['kibana', 'browser', 'session'] as const; export const solutionNames = ['canvas', 'dashboard', 'presentation'] as const; @@ -32,6 +33,22 @@ export const projects: { [ID in ProjectID]: ProjectConfig & { id: ID } } = { }), solutions: ['canvas'], }, + [USE_DATA_SERVICE]: { + id: USE_DATA_SERVICE, + isActive: true, + isDisplayed: true, + environments: ['kibana', 'browser', 'session'], + name: i18n.translate('presentationUtil.experiments.enableUseDataServiceExperimentName', { + defaultMessage: 'Use data service', + }), + description: i18n.translate( + 'presentationUtil.experiments.enableUseDataServiceExperimentDescription', + { + defaultMessage: 'An experiment of using the new data.search service for Canvas datasources', + } + ), + solutions: ['canvas'], + }, }; export type ProjectID = typeof projectIDs[number]; diff --git a/src/plugins/spaces_oss/common/types.ts b/src/plugins/spaces_oss/common/types.ts index a7e2257dd424c..b5c418cf3177e 100644 --- a/src/plugins/spaces_oss/common/types.ts +++ b/src/plugins/spaces_oss/common/types.ts @@ -6,13 +6,57 @@ * Side Public License, v 1. */ +/** + * A Space. + */ export interface Space { + /** + * The unique identifier for this space. + * The id becomes part of the "URL Identifier" of the space. + * + * Example: an id of `marketing` would result in the URL identifier of `/s/marketing`. + */ id: string; + + /** + * Display name for this space. + */ name: string; + + /** + * Optional description for this space. + */ description?: string; + + /** + * Optional color (hex code) for this space. + * If neither `color` nor `imageUrl` is specified, then a color will be automatically generated. + */ color?: string; + + /** + * Optional display initials for this space's avatar. Supports a maximum of 2 characters. + * If initials are not provided, then they will be derived from the space name automatically. + * + * Initials are not displayed if an `imageUrl` has been specified. + */ initials?: string; + + /** + * Optional base-64 encoded data image url to show as this space's avatar. + * This setting takes precedence over any configured `color` or `initials`. + */ + imageUrl?: string; + + /** + * The set of feature ids that should be hidden within this space. + */ disabledFeatures: string[]; + + /** + * Indicates that this space is reserved (system controlled). + * Reserved spaces cannot be created or deleted by end-users. + * @private + */ _reserved?: boolean; - imageUrl?: string; } diff --git a/src/plugins/spaces_oss/public/api.ts b/src/plugins/spaces_oss/public/api.ts index 3f562bcbed8e2..bd452c2fca00e 100644 --- a/src/plugins/spaces_oss/public/api.ts +++ b/src/plugins/spaces_oss/public/api.ts @@ -12,30 +12,37 @@ import type { Observable } from 'rxjs'; import type { Space } from '../common'; /** - * @public + * Client-side Spaces API. */ export interface SpacesApi { + /** + * Observable representing the currently active space. + * The details of the space can change without a full page reload (such as display name, color, etc.) + */ readonly activeSpace$: Observable; + + /** + * Retrieve the currently active space. + */ getActiveSpace(): Promise; + /** - * UI API to use to add spaces capabilities to an application + * UI components and services to add spaces capabilities to an application. */ ui: SpacesApiUi; } /** * Function that returns a promise for a lazy-loadable component. - * - * @public */ export type LazyComponentFn = (props: T) => ReactElement; /** - * @public + * UI components and services to add spaces capabilities to an application. */ export interface SpacesApiUi { /** - * Lazy-loadable {@link SpacesApiUiComponent | React components} to support the spaces feature. + * Lazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature. */ components: SpacesApiUiComponent; /** @@ -62,9 +69,7 @@ export interface SpacesApiUi { } /** - * React UI components to be used to display the spaces feature in any application. - * - * @public + * React UI components to be used to display the Spaces feature in any application. */ export interface SpacesApiUiComponent { /** @@ -111,7 +116,7 @@ export interface SpacesApiUiComponent { } /** - * @public + * Properties for the SpacesContext. */ export interface SpacesContextProps { /** @@ -121,7 +126,7 @@ export interface SpacesContextProps { } /** - * @public + * Properties for the ShareToSpaceFlyout. */ export interface ShareToSpaceFlyoutProps { /** @@ -179,7 +184,7 @@ export interface ShareToSpaceFlyoutProps { } /** - * @public + * Describes the target saved object during a share operation. */ export interface ShareToSpaceSavedObjectTarget { /** @@ -215,7 +220,7 @@ export interface ShareToSpaceSavedObjectTarget { } /** - * @public + * Properties for the SpaceList component. */ export interface SpaceListProps { /** @@ -240,7 +245,7 @@ export interface SpaceListProps { } /** - * @public + * Properties for the LegacyUrlConflict component. */ export interface LegacyUrlConflictProps { /** @@ -265,12 +270,18 @@ export interface LegacyUrlConflictProps { } /** - * @public + * Properties for the SpaceAvatar component. */ export interface SpaceAvatarProps { + /** The space to represent with an avatar. */ space: Partial; + + /** The size of the avatar. */ size?: 's' | 'm' | 'l' | 'xl'; + + /** Optional CSS class(es) to apply. */ className?: string; + /** * When enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers. * diff --git a/src/plugins/spaces_oss/public/index.ts b/src/plugins/spaces_oss/public/index.ts index 828ce6f4dec63..9c4d5fd17700c 100644 --- a/src/plugins/spaces_oss/public/index.ts +++ b/src/plugins/spaces_oss/public/index.ts @@ -8,14 +8,14 @@ import { SpacesOssPlugin } from './plugin'; -export { +export type { SpacesOssPluginSetup, SpacesOssPluginStart, SpacesAvailableStartContract, SpacesUnavailableStartContract, } from './types'; -export { +export type { LazyComponentFn, SpacesApi, SpacesApiUi, diff --git a/src/plugins/spaces_oss/public/types.ts b/src/plugins/spaces_oss/public/types.ts index 22501d3abd832..df20e9be6eaa1 100644 --- a/src/plugins/spaces_oss/public/types.ts +++ b/src/plugins/spaces_oss/public/types.ts @@ -8,21 +8,41 @@ import type { SpacesApi } from './api'; +/** + * OSS Spaces plugin start contract when the Spaces feature is enabled. + */ export interface SpacesAvailableStartContract extends SpacesApi { + /** Indicates if the Spaces feature is enabled. */ isSpacesAvailable: true; } +/** + * OSS Spaces plugin start contract when the Spaces feature is disabled. + * @deprecated The Spaces plugin will always be enabled starting in 8.0. + * @removeBy 8.0 + */ export interface SpacesUnavailableStartContract { + /** Indicates if the Spaces feature is enabled. */ isSpacesAvailable: false; } +/** + * OSS Spaces plugin setup contract. + */ export interface SpacesOssPluginSetup { /** * Register a provider for the Spaces API. * * Only one provider can be registered, subsequent calls to this method will fail. + * + * @param provider the API provider. + * + * @private designed to only be consumed by the `spaces` plugin. */ registerSpacesApi(provider: SpacesApi): void; } +/** + * OSS Spaces plugin start contract. + */ export type SpacesOssPluginStart = SpacesAvailableStartContract | SpacesUnavailableStartContract; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 76460a57ee442..7cd66dc8eef30 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -8342,6 +8342,12 @@ "_meta": { "description": "Non-default value of setting." } + }, + "labs:canvas:useDataService": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } } } }, diff --git a/src/plugins/usage_collection/public/components/index.ts b/src/plugins/usage_collection/public/components/index.ts index 0c2b8f45a1baf..23a28aff05616 100644 --- a/src/plugins/usage_collection/public/components/index.ts +++ b/src/plugins/usage_collection/public/components/index.ts @@ -7,3 +7,4 @@ */ export { TrackApplicationView } from './track_application_view'; +export type { TrackApplicationViewProps } from './track_application_view'; diff --git a/src/plugins/usage_collection/public/components/track_application_view/track_application_view.tsx b/src/plugins/usage_collection/public/components/track_application_view/track_application_view.tsx index 6c14dcae0c512..414ef600e8deb 100644 --- a/src/plugins/usage_collection/public/components/track_application_view/track_application_view.tsx +++ b/src/plugins/usage_collection/public/components/track_application_view/track_application_view.tsx @@ -17,7 +17,7 @@ export const ApplicationUsageContext = createContext = (props) => { diff --git a/src/plugins/usage_collection/public/components/track_application_view/types.ts b/src/plugins/usage_collection/public/components/track_application_view/types.ts index b7783785a53b7..4fd4e1de3c460 100644 --- a/src/plugins/usage_collection/public/components/track_application_view/types.ts +++ b/src/plugins/usage_collection/public/components/track_application_view/types.ts @@ -9,7 +9,7 @@ import { ReactNode } from 'react'; /** - * Props to provide to the {@Link TrackApplicationView} component. + * Props to provide to the {@link TrackApplicationView} component. * @public */ export interface TrackApplicationViewProps { diff --git a/src/plugins/usage_collection/public/index.ts b/src/plugins/usage_collection/public/index.ts index 9b009b1d9e264..d5ac73b7758f5 100644 --- a/src/plugins/usage_collection/public/index.ts +++ b/src/plugins/usage_collection/public/index.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { PluginInitializerContext } from '../../../core/public'; +import type { PluginInitializerContext } from 'src/core/public'; import { UsageCollectionPlugin } from './plugin'; -export { METRIC_TYPE } from '@kbn/analytics'; export type { UsageCollectionSetup, UsageCollectionStart } from './plugin'; export { TrackApplicationView } from './components'; +export type { TrackApplicationViewProps } from './components'; export function plugin(initializerContext: PluginInitializerContext) { return new UsageCollectionPlugin(initializerContext); diff --git a/src/plugins/usage_collection/public/plugin.tsx b/src/plugins/usage_collection/public/plugin.tsx index f50919eb8f622..450c8903e1a5e 100644 --- a/src/plugins/usage_collection/public/plugin.tsx +++ b/src/plugins/usage_collection/public/plugin.tsx @@ -7,6 +7,7 @@ */ import { Reporter, ApplicationUsageTracker } from '@kbn/analytics'; +import type { UiCounterMetricType } from '@kbn/analytics'; import type { Subscription } from 'rxjs'; import React from 'react'; import type { @@ -31,15 +32,63 @@ export type IApplicationUsageTracker = Pick< 'trackApplicationViewUsage' | 'flushTrackedView' | 'updateViewClickCounter' >; +/** Public's setup APIs exposed by the UsageCollection Service **/ export interface UsageCollectionSetup { + /** Component helpers to track usage collection in the UI **/ components: { + /** + * The context provider to wrap the application if planning to use + * {@link TrackApplicationView} somewhere inside the app. + * + * @example + * ```typescript jsx + * class MyPlugin implements Plugin { + * ... + * public setup(core: CoreSetup, plugins: { usageCollection?: UsageCollectionSetup }) { + * const ApplicationUsageTrackingProvider = plugins.usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; + * + * core.application.register({ + * id, + * title, + * ..., + * mount: async (params: AppMountParameters) => { + * ReactDOM.render( + * // Set the tracking context provider at the App level + * + * + * + * , + * element + * ); + * return () => ReactDOM.unmountComponentAtNode(element); + * }, + * }); + * } + * ... + * } + * ``` + */ ApplicationUsageTrackingProvider: React.FC; }; - reportUiCounter: Reporter['reportUiCounter']; + + /** Report whenever a UI event occurs for UI counters to report it **/ + reportUiCounter: ( + appName: string, + type: UiCounterMetricType, + eventNames: string | string[], + count?: number + ) => void; } +/** Public's start APIs exposed by the UsageCollection Service **/ export interface UsageCollectionStart { - reportUiCounter: Reporter['reportUiCounter']; + /** Report whenever a UI event occurs for UI counters to report it **/ + reportUiCounter: ( + appName: string, + type: UiCounterMetricType, + eventNames: string | string[], + count?: number + ) => void; } export function isUnauthenticated(http: HttpSetup) { diff --git a/src/plugins/usage_collection/server/collector/collector.test.ts b/src/plugins/usage_collection/server/collector/collector.test.ts index fa3966f4f2d3c..8c7971e52a4b9 100644 --- a/src/plugins/usage_collection/server/collector/collector.test.ts +++ b/src/plugins/usage_collection/server/collector/collector.test.ts @@ -20,19 +20,6 @@ describe('collector', () => { ); }); - it('should fail if init is not a function', () => { - expect( - () => - new Collector(logger, { - type: 'my_test_collector', - // @ts-expect-error - init: 1, - }) - ).toThrowError( - 'If init property is passed, Collector must be instantiated with a options.init as a function property' - ); - }); - it('should fail if fetch is not defined', () => { expect( () => diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index 22c91ac0c038d..21f8229718c87 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -6,144 +6,18 @@ * Side Public License, v 1. */ +import type { Logger } from 'src/core/server'; import type { - Logger, - ElasticsearchClient, - SavedObjectsClientContract, - KibanaRequest, -} from 'src/core/server'; - -export type AllowedSchemaNumberTypes = - | 'long' - | 'integer' - | 'short' - | 'byte' - | 'double' - | 'float' - | 'date'; -export type AllowedSchemaStringTypes = 'keyword' | 'text' | 'date'; -export type AllowedSchemaBooleanTypes = 'boolean'; - -export type AllowedSchemaTypes = - | AllowedSchemaNumberTypes - | AllowedSchemaStringTypes - | AllowedSchemaBooleanTypes; - -export interface SchemaField { - type: string; -} - -export type PossibleSchemaTypes = U extends string - ? AllowedSchemaStringTypes - : U extends number - ? AllowedSchemaNumberTypes - : U extends boolean - ? AllowedSchemaBooleanTypes - : // allow any schema type from the union if typescript is unable to resolve the exact U type - AllowedSchemaTypes; - -export type RecursiveMakeSchemaFrom = U extends object - ? MakeSchemaFrom - : { type: PossibleSchemaTypes; _meta?: { description: string } }; - -// Using Required to enforce all optional keys in the object -export type MakeSchemaFrom = { - [Key in keyof Required]: Required[Key] extends Array - ? { type: 'array'; items: RecursiveMakeSchemaFrom } - : RecursiveMakeSchemaFrom[Key]>; -}; - -/** - * The context for the `fetch` method: It includes the most commonly used clients in the collectors (ES and SO clients). - * Both are scoped based on the request and the context: - * - When users are requesting a sample of data, it is scoped to their role to avoid exposing data they shouldn't read - * - When building the telemetry data payload to report to the remote cluster, the requests are scoped to the `kibana` internal user - * - * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster. - */ -export type CollectorFetchContext = { - /** - * Request-scoped Elasticsearch client - * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster (more info: {@link CollectorFetchContext}) - */ - esClient: ElasticsearchClient; - /** - * Request-scoped Saved Objects client - * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster (more info: {@link CollectorFetchContext}) - */ - soClient: SavedObjectsClientContract; -} & (WithKibanaRequest extends true - ? { - /** - * The KibanaRequest that can be used to scope the requests: - * It is provided only when your custom clients need to be scoped. If not available, you should use the Internal Client. - * More information about when scoping is needed: {@link CollectorFetchContext} - * @remark You should only use this if you implement your collector to deal with both scenarios: when provided and, especially, when not provided. When telemetry payload is sent to the remote service the `kibanaRequest` will not be provided. - */ - kibanaRequest?: KibanaRequest; - } - : {}); - -export type CollectorFetchMethod< - WithKibanaRequest extends boolean | undefined, - TReturn, - ExtraOptions extends object = {} -> = ( - this: Collector & ExtraOptions, // Specify the context of `this` for this.log and others to become available - context: CollectorFetchContext -) => Promise | TReturn; - -export interface ICollectorOptionsFetchExtendedContext { - /** - * Set to `true` if your `fetch` method requires the `KibanaRequest` object to be added in its context {@link CollectorFetchContextWithRequest}. - * @remark You should fully understand acknowledge that by using the `KibanaRequest` in your collector, you need to ensure it should specially work without it because it won't be provided when building the telemetry payload actually sent to the remote telemetry service. - */ - kibanaRequest?: WithKibanaRequest; -} - -export type CollectorOptionsFetchExtendedContext< - WithKibanaRequest extends boolean -> = ICollectorOptionsFetchExtendedContext & - (WithKibanaRequest extends true // If enforced to true via Types, the config must be expected - ? Required, 'kibanaRequest'>> - : {}); - -export type CollectorOptions< - TFetchReturn = unknown, - WithKibanaRequest extends boolean = boolean, - ExtraOptions extends object = {} -> = { - /** - * Unique string identifier for the collector - */ - type: string; - init?: Function; - /** - * Method to return `true`/`false` or Promise(`true`/`false`) to confirm if the collector is ready for the `fetch` method to be called. - */ - isReady: () => Promise | boolean; - /** - * Schema definition of the output of the `fetch` method. - */ - schema?: MakeSchemaFrom; - /** - * The method that will collect and return the data in the final format. - * @param collectorFetchContext {@link CollectorFetchContext} - */ - fetch: CollectorFetchMethod; -} & ExtraOptions & - (WithKibanaRequest extends true // If enforced to true via Types, the config must be enforced - ? { - extendFetchContext: CollectorOptionsFetchExtendedContext; - } - : { - extendFetchContext?: CollectorOptionsFetchExtendedContext; - }); - -export class Collector { + CollectorFetchMethod, + CollectorOptions, + CollectorOptionsFetchExtendedContext, + ICollector, +} from './types'; + +export class Collector + implements ICollector { public readonly extendFetchContext: CollectorOptionsFetchExtendedContext; public readonly type: CollectorOptions['type']; - public readonly init?: CollectorOptions['init']; public readonly fetch: CollectorFetchMethod; public readonly isReady: CollectorOptions['isReady']; /** @@ -155,7 +29,6 @@ export class Collector { public readonly log: Logger, { type, - init, fetch, isReady, extendFetchContext = {}, @@ -167,11 +40,6 @@ export class Collector { if (type === undefined) { throw new Error('Collector must be instantiated with a options.type string property'); } - if (typeof init !== 'undefined' && typeof init !== 'function') { - throw new Error( - 'If init property is passed, Collector must be instantiated with a options.init as a function property' - ); - } if (typeof fetch !== 'function') { throw new Error('Collector must be instantiated with a options.fetch function property'); } @@ -179,7 +47,6 @@ export class Collector { Object.assign(this, options); // spread in other properties and mutate "this" this.type = type; - this.init = init; this.fetch = fetch; this.isReady = typeof isReady === 'function' ? isReady : () => true; this.extendFetchContext = extendFetchContext; diff --git a/src/plugins/usage_collection/server/collector/collector_set.test.ts b/src/plugins/usage_collection/server/collector/collector_set.test.ts index 5a617e2316dda..aa90d25598518 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.test.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.test.ts @@ -25,10 +25,8 @@ const loggerSpies = { describe('CollectorSet', () => { describe('registers a collector set and runs lifecycle events', () => { - let init: Function; let fetch: Function; beforeEach(() => { - init = noop; fetch = noop; loggerSpies.debug.mockRestore(); loggerSpies.warn.mockRestore(); @@ -42,7 +40,6 @@ describe('CollectorSet', () => { const registerPojo = () => { collectors.registerCollector({ type: 'type_collector_test', - init, // @ts-expect-error we are intentionally sending it wrong. fetch, }); diff --git a/src/plugins/usage_collection/server/collector/collector_set.ts b/src/plugins/usage_collection/server/collector/collector_set.ts index d42eb6644bbbe..d536cf3f2c89b 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.ts @@ -13,12 +13,13 @@ import type { SavedObjectsClientContract, KibanaRequest, } from 'src/core/server'; -import { Collector, CollectorOptions } from './collector'; +import { Collector } from './collector'; +import type { ICollector, CollectorOptions } from './types'; import { UsageCollector, UsageCollectorOptions } from './usage_collector'; // Needed for the general array containing all the collectors. We don't really care about their types here // eslint-disable-next-line @typescript-eslint/no-explicit-any -type AnyCollector = Collector; +type AnyCollector = ICollector; interface CollectorSetConfig { logger: Logger; @@ -85,11 +86,6 @@ export class CollectorSet { } this.collectors.set(collector.type, collector); - - if (collector.init) { - this.logger.debug(`Initializing ${collector.type} collector`); - collector.init(); - } }; public getCollectorByType = (type: string) => { diff --git a/src/plugins/usage_collection/server/collector/index.ts b/src/plugins/usage_collection/server/collector/index.ts index 594455f70fdf8..ca240a520ee24 100644 --- a/src/plugins/usage_collection/server/collector/index.ts +++ b/src/plugins/usage_collection/server/collector/index.ts @@ -7,14 +7,17 @@ */ export { CollectorSet } from './collector_set'; -export { Collector } from './collector'; export type { AllowedSchemaTypes, AllowedSchemaNumberTypes, - SchemaField, + AllowedSchemaBooleanTypes, + AllowedSchemaStringTypes, + RecursiveMakeSchemaFrom, MakeSchemaFrom, CollectorOptions, CollectorFetchContext, -} from './collector'; -export { UsageCollector } from './usage_collector'; + CollectorFetchMethod, + CollectorOptionsFetchExtendedContext, + ICollector as Collector, +} from './types'; export type { UsageCollectorOptions } from './usage_collector'; diff --git a/src/plugins/usage_collection/server/collector/types.ts b/src/plugins/usage_collection/server/collector/types.ts new file mode 100644 index 0000000000000..4258d5e4dd2e8 --- /dev/null +++ b/src/plugins/usage_collection/server/collector/types.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { + ElasticsearchClient, + KibanaRequest, + SavedObjectsClientContract, + Logger, +} from 'src/core/server'; + +/** Types matching number values **/ +export type AllowedSchemaNumberTypes = + | 'long' + | 'integer' + | 'short' + | 'byte' + | 'double' + | 'float' + | 'date'; +/** Types matching string values **/ +export type AllowedSchemaStringTypes = 'keyword' | 'text' | 'date'; +/** Types matching boolean values **/ +export type AllowedSchemaBooleanTypes = 'boolean'; + +/** + * Possible type values in the schema + */ +export type AllowedSchemaTypes = + | AllowedSchemaNumberTypes + | AllowedSchemaStringTypes + | AllowedSchemaBooleanTypes; + +/** + * Helper to ensure the declared types match the schema types + */ +export type PossibleSchemaTypes = U extends string + ? AllowedSchemaStringTypes + : U extends number + ? AllowedSchemaNumberTypes + : U extends boolean + ? AllowedSchemaBooleanTypes + : // allow any schema type from the union if typescript is unable to resolve the exact U type + AllowedSchemaTypes; + +/** + * Helper to find out whether to keep recursively looking or if we are on an end value + */ +export type RecursiveMakeSchemaFrom = U extends object + ? MakeSchemaFrom + : { type: PossibleSchemaTypes; _meta?: { description: string } }; + +/** + * The `schema` property in {@link CollectorOptions} must match the output of + * the `fetch` method. This type helps ensure that is correct + */ +export type MakeSchemaFrom = { + // Using Required to enforce all optional keys in the object + [Key in keyof Required]: Required[Key] extends Array + ? { type: 'array'; items: RecursiveMakeSchemaFrom } + : RecursiveMakeSchemaFrom[Key]>; +}; + +/** + * The context for the `fetch` method: It includes the most commonly used clients in the collectors (ES and SO clients). + * Both are scoped based on the request and the context: + * - When users are requesting a sample of data, it is scoped to their role to avoid exposing data they shouldn't read + * - When building the telemetry data payload to report to the remote cluster, the requests are scoped to the `kibana` internal user + * + * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster. + */ +export type CollectorFetchContext = { + /** + * Request-scoped Elasticsearch client + * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster (more info: {@link CollectorFetchContext}) + */ + esClient: ElasticsearchClient; + /** + * Request-scoped Saved Objects client + * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster (more info: {@link CollectorFetchContext}) + */ + soClient: SavedObjectsClientContract; +} & (WithKibanaRequest extends true + ? { + /** + * The KibanaRequest that can be used to scope the requests: + * It is provided only when your custom clients need to be scoped. If not available, you should use the Internal Client. + * More information about when scoping is needed: {@link CollectorFetchContext} + * @remark You should only use this if you implement your collector to deal with both scenarios: when provided and, especially, when not provided. When telemetry payload is sent to the remote service the `kibanaRequest` will not be provided. + */ + kibanaRequest?: KibanaRequest; + } + : {}); + +/** + * The fetch method has the context of the Collector itself + * (this has access to all the properties of the collector like the logger) + * and the the first parameter is {@link CollectorFetchContext}. + */ +export type CollectorFetchMethod< + WithKibanaRequest extends boolean | undefined, + TReturn, + ExtraOptions extends object = {} +> = ( + this: ICollector & ExtraOptions, // Specify the context of `this` for this.log and others to become available + context: CollectorFetchContext +) => Promise | TReturn; + +export interface ICollectorOptionsFetchExtendedContext { + /** + * Set to `true` if your `fetch` method requires the `KibanaRequest` object to be added in its context {@link CollectorFetchContextWithRequest}. + * @remark You should fully acknowledge that by using the `KibanaRequest` in your collector, you need to ensure it should specially work without it because it won't be provided when building the telemetry payload actually sent to the remote telemetry service. + */ + kibanaRequest?: WithKibanaRequest; +} + +/** + * The options to extend the context provided to the `fetch` method. + * @remark Only to be used in very rare scenarios when this is really needed. + */ +export type CollectorOptionsFetchExtendedContext< + WithKibanaRequest extends boolean +> = ICollectorOptionsFetchExtendedContext & + (WithKibanaRequest extends true // If enforced to true via Types, the config must be expected + ? Required, 'kibanaRequest'>> + : {}); + +/** + * Options to instantiate a collector + */ +export type CollectorOptions< + TFetchReturn = unknown, + WithKibanaRequest extends boolean = boolean, + ExtraOptions extends object = {} +> = { + /** + * Unique string identifier for the collector + */ + type: string; + /** + * Method to return `true`/`false` or Promise(`true`/`false`) to confirm if the collector is ready for the `fetch` method to be called. + */ + isReady: () => Promise | boolean; + /** + * Schema definition of the output of the `fetch` method. + */ + schema?: MakeSchemaFrom; + /** + * The method that will collect and return the data in the final format. + * @param collectorFetchContext {@link CollectorFetchContext} + */ + fetch: CollectorFetchMethod; +} & ExtraOptions & + (WithKibanaRequest extends true // If enforced to true via Types, the config must be enforced + ? { + /** {@link CollectorOptionsFetchExtendedContext} **/ + extendFetchContext: CollectorOptionsFetchExtendedContext; + } + : { + /** {@link CollectorOptionsFetchExtendedContext} **/ + extendFetchContext?: CollectorOptionsFetchExtendedContext; + }); + +/** + * Common interface for Usage and Stats Collectors + */ +export interface ICollector { + /** Logger **/ + readonly log: Logger; + /** + * The options to extend the context provided to the `fetch` method: {@link CollectorOptionsFetchExtendedContext}. + * @remark Only to be used in very rare scenarios when this is really needed. + */ + readonly extendFetchContext: CollectorOptionsFetchExtendedContext; + /** The registered type (aka name) of the collector **/ + readonly type: CollectorOptions['type']; + /** + * The actual logic that reports the Usage collection. + * It will be called on every collection request. + * Whatever is returned in this method will be passed through as-is under + * the {@link ICollector.type} key. + * + * @example + * { + * [type]: await fetch(context) + * } + */ + readonly fetch: CollectorFetchMethod; + /** + * Should return `true` when it's safe to call the `fetch` method. + */ + readonly isReady: CollectorOptions['isReady']; +} diff --git a/src/plugins/usage_collection/server/collector/usage_collector.ts b/src/plugins/usage_collection/server/collector/usage_collector.ts index 3af3a7bb65f84..15f7cd9c627fc 100644 --- a/src/plugins/usage_collection/server/collector/usage_collector.ts +++ b/src/plugins/usage_collection/server/collector/usage_collector.ts @@ -6,10 +6,13 @@ * Side Public License, v 1. */ -import { Logger } from 'src/core/server'; -import { Collector, CollectorOptions } from './collector'; +import type { Logger } from 'src/core/server'; +import type { CollectorOptions } from './types'; +import { Collector } from './collector'; -// Enforce the `schema` property for UsageCollectors +/** + * Same as {@link CollectorOptions} but with the `schema` property enforced + */ export type UsageCollectorOptions< TFetchReturn = unknown, WithKibanaRequest extends boolean = false, @@ -17,6 +20,9 @@ export type UsageCollectorOptions< > = CollectorOptions & Required, 'schema'>>; +/** + * @private Only used in fixtures as a type + */ export class UsageCollector extends Collector< TFetchReturn, ExtraOptions diff --git a/src/plugins/usage_collection/server/index.ts b/src/plugins/usage_collection/server/index.ts index b5441a8b7b34d..74fa77be9843c 100644 --- a/src/plugins/usage_collection/server/index.ts +++ b/src/plugins/usage_collection/server/index.ts @@ -9,28 +9,27 @@ import { PluginInitializerContext } from 'src/core/server'; import { UsageCollectionPlugin } from './plugin'; -export { Collector } from './collector'; export type { + Collector, AllowedSchemaTypes, MakeSchemaFrom, - SchemaField, CollectorOptions, UsageCollectorOptions, CollectorFetchContext, + CollectorFetchMethod, + CollectorOptionsFetchExtendedContext, } from './collector'; export type { UsageCountersSavedObject, UsageCountersSavedObjectAttributes, IncrementCounterParams, -} from './usage_counters'; - -export { - USAGE_COUNTERS_SAVED_OBJECT_TYPE, - serializeCounterKey, UsageCounter, + SerializeCounterParams, } from './usage_counters'; +export { USAGE_COUNTERS_SAVED_OBJECT_TYPE, serializeCounterKey } from './usage_counters'; + export type { UsageCollectionSetup } from './plugin'; export { config } from './config'; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/src/plugins/usage_collection/server/mocks.ts b/src/plugins/usage_collection/server/mocks.ts index b84fa0f0aab70..ab7e53a7ad69b 100644 --- a/src/plugins/usage_collection/server/mocks.ts +++ b/src/plugins/usage_collection/server/mocks.ts @@ -13,7 +13,8 @@ import { savedObjectsClientMock, } from '../../../../src/core/server/mocks'; -import { CollectorOptions, Collector, CollectorSet } from './collector'; +import { CollectorOptions, CollectorSet } from './collector'; +import { Collector } from './collector/collector'; import { UsageCollectionSetup, CollectorFetchContext } from './index'; export type { CollectorOptions }; diff --git a/src/plugins/usage_collection/server/plugin.ts b/src/plugins/usage_collection/server/plugin.ts index 37d7327aed662..1c537ccfbb22b 100644 --- a/src/plugins/usage_collection/server/plugin.ts +++ b/src/plugins/usage_collection/server/plugin.ts @@ -6,54 +6,100 @@ * Side Public License, v 1. */ -import { +import type { PluginInitializerContext, Logger, CoreSetup, CoreStart, ISavedObjectsRepository, Plugin, + ElasticsearchClient, + SavedObjectsClientContract, + KibanaRequest, } from 'src/core/server'; -import { ConfigType } from './config'; +import type { ConfigType } from './config'; import { CollectorSet } from './collector'; +import type { Collector, CollectorOptions, UsageCollectorOptions } from './collector'; import { setupRoutes } from './routes'; import { UsageCountersService } from './usage_counters'; -import type { UsageCountersServiceSetup } from './usage_counters'; +import type { UsageCounter } from './usage_counters'; +/** Server's setup APIs exposed by the UsageCollection Service **/ export interface UsageCollectionSetup { /** * Creates and registers a usage counter to collect daily aggregated plugin counter events */ - createUsageCounter: UsageCountersServiceSetup['createUsageCounter']; + createUsageCounter: (type: string) => UsageCounter; /** * Returns a usage counter by type */ - getUsageCounterByType: UsageCountersServiceSetup['getUsageCounterByType']; + getUsageCounterByType: (type: string) => UsageCounter | undefined; /** * Creates a usage collector to collect plugin telemetry data. - * registerCollector must be called to connect the created collecter with the service. + * registerCollector must be called to connect the created collector with the service. */ - makeUsageCollector: CollectorSet['makeUsageCollector']; + makeUsageCollector: < + TFetchReturn, + WithKibanaRequest extends boolean = false, + ExtraOptions extends object = {} + >( + options: UsageCollectorOptions + ) => Collector; /** * Register a usage collector or a stats collector. * Used to connect the created collector to telemetry. */ - registerCollector: CollectorSet['registerCollector']; + registerCollector: ( + collector: Collector + ) => void; /** * Returns a usage collector by type */ - getCollectorByType: CollectorSet['getCollectorByType']; - /* internal: telemetry use */ - areAllCollectorsReady: CollectorSet['areAllCollectorsReady']; - /* internal: telemetry use */ - bulkFetch: CollectorSet['bulkFetch']; - /* internal: telemetry use */ - toObject: CollectorSet['toObject']; - /* internal: monitoring use */ - toApiFieldNames: CollectorSet['toApiFieldNames']; - /* internal: telemtery and monitoring use */ - makeStatsCollector: CollectorSet['makeStatsCollector']; + getCollectorByType: ( + type: string + ) => Collector | undefined; + /** + * Returns if all the collectors are ready to fetch their reported usage. + * @internal: telemetry use + */ + areAllCollectorsReady: () => Promise; + /** + * Fetches the collection from all the registered collectors + * @internal: telemetry use + */ + bulkFetch: ( + esClient: ElasticsearchClient, + soClient: SavedObjectsClientContract, + kibanaRequest: KibanaRequest | undefined, // intentionally `| undefined` to enforce providing the parameter + collectors?: Map> + ) => Promise>; + /** + * Converts an array of fetched stats results into key/object + * @internal: telemetry use + */ + toObject: , T = unknown>( + statsData?: Array<{ type: string; result: T }> + ) => Result; + /** + * Rename fields to use API conventions + * @internal: monitoring use + */ + toApiFieldNames: ( + apiData: Record | unknown[] + ) => Record | unknown[]; + /** + * Creates a stats collector to collect plugin telemetry data. + * registerCollector must be called to connect the created collector with the service. + * @internal: telemetry and monitoring use + */ + makeStatsCollector: < + TFetchReturn, + WithKibanaRequest extends boolean, + ExtraOptions extends object = {} + >( + options: CollectorOptions + ) => Collector; } export class UsageCollectionPlugin implements Plugin { diff --git a/src/plugins/usage_collection/server/usage_counters/index.ts b/src/plugins/usage_collection/server/usage_counters/index.ts index dc1d1f5b43edf..cc1ee77ef3ea4 100644 --- a/src/plugins/usage_collection/server/usage_counters/index.ts +++ b/src/plugins/usage_collection/server/usage_counters/index.ts @@ -8,8 +8,8 @@ export type { UsageCountersServiceSetup } from './usage_counters_service'; export type { UsageCountersSavedObjectAttributes, UsageCountersSavedObject } from './saved_objects'; -export type { IncrementCounterParams } from './usage_counter'; +export type { IUsageCounter as UsageCounter, IncrementCounterParams } from './usage_counter'; export { UsageCountersService } from './usage_counters_service'; -export { UsageCounter } from './usage_counter'; +export type { SerializeCounterParams } from './saved_objects'; export { USAGE_COUNTERS_SAVED_OBJECT_TYPE, serializeCounterKey } from './saved_objects'; diff --git a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts index 6c585d756e8c1..34de2adac8086 100644 --- a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts +++ b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts @@ -6,24 +6,35 @@ * Side Public License, v 1. */ -import { +import type { SavedObject, SavedObjectsRepository, SavedObjectAttributes, SavedObjectsServiceSetup, } from 'kibana/server'; import moment from 'moment'; -import { CounterMetric } from './usage_counter'; +import type { CounterMetric } from './usage_counter'; +/** + * The attributes stored in the UsageCounters' SavedObjects + */ export interface UsageCountersSavedObjectAttributes extends SavedObjectAttributes { + /** The domain ID registered in the Usage Counter **/ domainId: string; + /** The counter name **/ counterName: string; + /** The counter type **/ counterType: string; + /** Number of times the event has occurred **/ count: number; } +/** + * The structure of the SavedObjects of type "usage-counters" + */ export type UsageCountersSavedObject = SavedObject; +/** The Saved Objects type for Usage Counters **/ export const USAGE_COUNTERS_SAVED_OBJECT_TYPE = 'usage-counters'; export const registerUsageCountersSavedObjectType = ( @@ -42,13 +53,26 @@ export const registerUsageCountersSavedObjectType = ( }); }; +/** + * Parameters to the `serializeCounterKey` method + * @internal used in kibana_usage_collectors + */ export interface SerializeCounterParams { + /** The domain ID registered in the UsageCounter **/ domainId: string; + /** The counter name **/ counterName: string; + /** The counter type **/ counterType: string; + /** The date to which serialize the key **/ date: moment.MomentInput; } +/** + * Generates a key based on the UsageCounter details + * @internal used in kibana_usage_collectors + * @param opts {@link SerializeCounterParams} + */ export const serializeCounterKey = ({ domainId, counterName, diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts index af00ad04149b7..b8057fdda8eb6 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts @@ -20,13 +20,32 @@ export interface UsageCounterDeps { counter$: Rx.Subject; } +/** + * Details about the counter to be incremented + */ export interface IncrementCounterParams { + /** The name of the counter **/ counterName: string; + /** The counter type ("count" by default) **/ counterType?: string; + /** Increment the counter by this number (1 if not specified) **/ incrementBy?: number; } -export class UsageCounter { +/** + * Usage Counter allows to keep track of any events that occur. + * By calling {@link IUsageCounter.incrementCounter} devs can notify this + * API whenever the event happens. + */ +export interface IUsageCounter { + /** + * Notifies the counter about a new event happening so it can increase the count internally. + * @param params {@link IncrementCounterParams} + */ + incrementCounter: (params: IncrementCounterParams) => void; +} + +export class UsageCounter implements IUsageCounter { private domainId: string; private counter$: Rx.Subject; diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index d31fed4639ffe..bfa1f79a6487a 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -215,6 +215,7 @@ export const panel = schema.object({ background_color_rules: schema.maybe(schema.arrayOf(backgroundColorRulesItems)), drilldown_url: stringOptional, drop_last_bucket: numberIntegerOptional, + ignore_daylight_time: schema.boolean(), filter: schema.maybe(queryObject), gauge_color_rules: schema.maybe(schema.arrayOf(gaugeColorRulesItems)), gauge_width: schema.nullable(schema.oneOf([stringOptionalNullable, numberOptional])), diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx b/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx index 4ac52a0a80c97..12ffe72135288 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx @@ -19,6 +19,7 @@ interface LastValueModeIndicatorProps { seriesData?: PanelData['data']; panelInterval: number; modelInterval: string; + ignoreDaylightTime: boolean; } const lastValueLabel = i18n.translate('visTypeTimeseries.lastValueModeIndicator.lastValue', { @@ -29,6 +30,7 @@ export const LastValueModeIndicator = ({ seriesData, panelInterval, modelInterval, + ignoreDaylightTime, }: LastValueModeIndicatorProps) => { if (!seriesData?.length) return {lastValueLabel}; @@ -40,7 +42,12 @@ export const LastValueModeIndicator = ({ return interval && `${interval.unitValue}${interval.unitString}`; }; - const formatter = createIntervalBasedFormatter(panelInterval, scaledDataFormat, dateFormat); + const formatter = createIntervalBasedFormatter( + panelInterval, + scaledDataFormat, + dateFormat, + ignoreDaylightTime + ); const lastBucketDate = formatter(seriesData[seriesData.length - 1][0]); const formattedPanelInterval = (isAutoInterval(modelInterval) || isGteInterval(modelInterval)) && getFormattedPanelInterval(); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts index 562aec31a0803..371a07af7b2e5 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts @@ -8,6 +8,8 @@ import moment from 'moment'; +const JANUARY_MOMENT_CONFIG = { M: 0, d: 1 }; + function getFormat(interval: number, rules: string[][] = []) { for (let i = rules.length - 1; i >= 0; i--) { const rule = rules[i]; @@ -20,9 +22,15 @@ function getFormat(interval: number, rules: string[][] = []) { export function createIntervalBasedFormatter( interval: number, rules: string[][], - dateFormat: string + dateFormat: string, + ignoreDaylightTime: boolean ) { - return (val: moment.MomentInput): string => { - return moment(val).format(getFormat(interval, rules) ?? dateFormat); + const fixedOffset = moment(JANUARY_MOMENT_CONFIG).utcOffset(); + return (val: moment.MomentInput) => { + const momentVal = moment(val); + if (ignoreDaylightTime) { + momentVal.utcOffset(fixedOffset); + } + return momentVal.format(getFormat(interval, rules) ?? dateFormat); }; } diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx index 86d3d50eb1f6a..a2e5ed448c93d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx @@ -127,6 +127,7 @@ export class TimeseriesPanelConfig extends Component< legend_position: 'right', show_grid: 1, tooltip_mode: 'show_all', + ignore_daylight_time: false, }; const model = { ...defaults, ...this.props.model }; const { selectedTab } = this.state; @@ -227,6 +228,22 @@ export class TimeseriesPanelConfig extends Component< /> + + + + + diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js b/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js index 950101103b3a5..d153f31801575 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js +++ b/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js @@ -23,7 +23,7 @@ export function SeriesConfigQueryBarWithIgnoreGlobalFilter({ const htmlId = htmlIdGenerator(); const yesNoOption = ( diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js index c15d3e0e4f6f8..29560c4bd9368 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js @@ -33,13 +33,14 @@ class TimeseriesVisualization extends Component { scaledDataFormat = this.props.getConfig('dateFormat:scaled'); dateFormat = this.props.getConfig('dateFormat'); - xAxisFormatter = (interval) => (val) => { + xAxisFormatter = (interval) => { const formatter = createIntervalBasedFormatter( interval, this.scaledDataFormat, - this.dateFormat + this.dateFormat, + this.props.model.ignore_daylight_time ); - return formatter(val); + return (val) => formatter(val); }; yAxisStackedByPercentFormatter = (val) => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx index 708892bbc681d..3c02deb177f9e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx @@ -13,7 +13,7 @@ import { TimeseriesVisParams } from '../../types'; interface YesNoProps { name: ParamName; - value: TimeseriesVisParams[ParamName]; + value: boolean | number | undefined; disabled?: boolean; 'data-test-subj'?: string; onChange: (partialModel: Partial) => void; diff --git a/src/plugins/vis_type_vega/public/data_model/search_api.ts b/src/plugins/vis_type_vega/public/data_model/search_api.ts index e25811fd67d69..c6aba6eccdc9f 100644 --- a/src/plugins/vis_type_vega/public/data_model/search_api.ts +++ b/src/plugins/vis_type_vega/public/data_model/search_api.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ -import { combineLatest } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; +import { combineLatest, from } from 'rxjs'; +import { map, tap, switchMap } from 'rxjs/operators'; import { CoreStart, IUiSettingsClient } from 'kibana/public'; +import { getData } from '../services'; import { getSearchParamsFromRequest, SearchRequest, @@ -19,6 +20,25 @@ import { search as dataPluginSearch } from '../../../data/public'; import { VegaInspectorAdapters } from '../vega_inspector'; import { RequestResponder } from '../../../inspector/public'; +const extendSearchParamsWithRuntimeFields = async ( + requestParams: ReturnType, + indexPatternString?: string +) => { + if (indexPatternString) { + const indexPattern = (await getData().indexPatterns.find(indexPatternString)).find( + (index) => index.title === indexPatternString + ); + const runtimeFields = indexPattern?.getComputedFields().runtimeFields; + + return { + ...requestParams, + body: { ...requestParams.body, runtime_mappings: runtimeFields }, + }; + } + + return requestParams; +}; + export interface SearchAPIDependencies { uiSettings: IUiSettingsClient; injectedMetadata: CoreStart['injectedMetadata']; @@ -40,7 +60,7 @@ export class SearchAPI { return combineLatest( searchRequests.map((request) => { const requestId = request.name; - const params = getSearchParamsFromRequest(request, { + const requestParams = getSearchParamsFromRequest(request, { getConfig: this.dependencies.uiSettings.get.bind(this.dependencies.uiSettings), }); @@ -49,18 +69,25 @@ export class SearchAPI { ...request, searchSessionId: this.searchSessionId, }); - requestResponders[requestId].json(params.body); + requestResponders[requestId].json(requestParams.body); } - return search - .search({ params }, { abortSignal: this.abortSignal, sessionId: this.searchSessionId }) - .pipe( - tap((data) => this.inspectSearchResult(data, requestResponders[requestId])), - map((data) => ({ - name: requestId, - rawResponse: data.rawResponse, - })) - ); + return from(extendSearchParamsWithRuntimeFields(requestParams, request.index)).pipe( + switchMap((params) => + search + .search( + { params }, + { abortSignal: this.abortSignal, sessionId: this.searchSessionId } + ) + .pipe( + tap((data) => this.inspectSearchResult(data, requestResponders[requestId])), + map((data) => ({ + name: requestId, + rawResponse: data.rawResponse, + })) + ) + ) + ); }) ); } diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 353273d1372e6..b7f2b064cf9c2 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -10,6 +10,7 @@ import $ from 'jquery'; import moment from 'moment'; import dateMath from '@elastic/datemath'; import { scheme, loader, logger, Warn, version as vegaVersion, expressionFunction } from 'vega'; +import { expressionInterpreter } from 'vega-interpreter'; import { version as vegaLiteVersion } from 'vega-lite'; import { Utils } from '../data_model/utils'; import { euiPaletteColorBlind } from '@elastic/eui'; @@ -166,6 +167,7 @@ export class VegaBaseView { createViewConfig() { const config = { + expr: expressionInterpreter, renderer: this._parser.renderer, }; diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_map_view/view.ts b/src/plugins/vis_type_vega/public/vega_view/vega_map_view/view.ts index 61ae1ce4e5d78..453e9596a2a4c 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_map_view/view.ts +++ b/src/plugins/vis_type_vega/public/vega_view/vega_map_view/view.ts @@ -183,7 +183,7 @@ export class VegaMapView extends VegaBaseView { protected async _initViewCustomizations() { const vegaView = new View( - parse(injectMapPropsIntoSpec(this._parser.spec)), + parse(injectMapPropsIntoSpec(this._parser.spec), undefined, { ast: true }), this._vegaViewConfig ); diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_view.js index 5b1e49a73343b..11b7ca125a4bd 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_view.js @@ -14,7 +14,7 @@ export class VegaView extends VegaBaseView { // In some cases, Vega may be initialized twice... TBD if (!this._$container) return; - const view = new View(parse(this._parser.spec), this._vegaViewConfig); + const view = new View(parse(this._parser.spec, undefined, { ast: true }), this._vegaViewConfig); if (this._parser.useResize) this.updateVegaSize(view); view.initialize(this._$container.get(0), this._$controls.get(0)); diff --git a/test/functional/apps/discover/_doc_table.ts b/test/functional/apps/discover/_doc_table.ts index 7cb33e6a7c2b8..df148a35aaf24 100644 --- a/test/functional/apps/discover/_doc_table.ts +++ b/test/functional/apps/discover/_doc_table.ts @@ -184,6 +184,35 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await docHeader.getVisibleText()).to.not.have.string(extraColumns[1]); }); }); + + it('should make the document table scrollable', async function () { + await PageObjects.discover.clearFieldSearchInput(); + const dscTable = await find.byCssSelector('.dscTable'); + const fieldNames = await PageObjects.discover.getAllFieldNames(); + const clientHeight = await dscTable.getAttribute('clientHeight'); + let fieldCounter = 0; + const checkScrollable = async () => { + const scrollWidth = await dscTable.getAttribute('scrollWidth'); + const clientWidth = await dscTable.getAttribute('clientWidth'); + log.debug(`scrollWidth: ${scrollWidth}, clientWidth: ${clientWidth}`); + return scrollWidth > clientWidth; + }; + const addColumn = async () => { + await PageObjects.discover.clickFieldListItemAdd(fieldNames[fieldCounter++]); + }; + + await addColumn(); + const isScrollable = await checkScrollable(); + expect(isScrollable).to.be(false); + + await retry.waitFor('container to be scrollable', async () => { + await addColumn(); + return await checkScrollable(); + }); + // so now we need to check if the horizontal scrollbar is displayed + const newClientHeight = await dscTable.getAttribute('clientHeight'); + expect(Number(clientHeight)).to.be.above(Number(newClientHeight)); + }); }); }); } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json index 96e6b7c0a19f1..6a416126d7f26 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json @@ -162,8 +162,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape", - "tree": "quadtree" + "dynamic": false, + "properties": {} }, "description": { "type": "text" @@ -456,4 +456,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json index d85125efd672a..43b851e817fa8 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json @@ -188,8 +188,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape", - "tree": "quadtree" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json index 00d349a27795d..1de768d290d35 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json @@ -188,8 +188,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape", - "tree": "quadtree" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json index a862731c13f7a..a9abae009cb59 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json @@ -193,8 +193,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape", - "tree": "quadtree" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json index d53e6c96e883e..a5478a5805d50 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json @@ -176,8 +176,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape", - "tree": "quadtree" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 8ca6c6e816aa5..ee50185db8c68 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -96,6 +96,12 @@ export function DataGridProvider({ getService, getPageObjects }: FtrProviderCont })` ); } + + public async getDocCount(): Promise { + const grid = await find.byCssSelector('[data-document-number]'); + return Number(await grid.getAttribute('data-document-number')); + } + public async getFields() { const cells = await find.allByCssSelector('.euiDataGridRowCell'); diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh index 2887a51f26283..b5fd7492d7961 100755 --- a/test/scripts/jenkins_xpack_build_kibana.sh +++ b/test/scripts/jenkins_xpack_build_kibana.sh @@ -25,7 +25,8 @@ node scripts/functional_tests --assert-none-excluded \ --include-tag ciGroup10 \ --include-tag ciGroup11 \ --include-tag ciGroup12 \ - --include-tag ciGroup13 + --include-tag ciGroup13 \ + --include-tag ciGroupDocker # Do not build kibana for code coverage run if [[ -z "$CODE_COVERAGE" ]] ; then @@ -42,7 +43,19 @@ if [[ -z "$CODE_COVERAGE" ]] ; then installDir="$KIBANA_DIR/install/kibana" mkdir -p "$installDir" tar -xzf "$linuxBuild" -C "$installDir" --strip=1 + cp "$linuxBuild" "$WORKSPACE/kibana-default.tar.gz" mkdir -p "$WORKSPACE/kibana-build-xpack" cp -pR install/kibana/. $WORKSPACE/kibana-build-xpack/ + + echo " -> Archive built plugins" + shopt -s globstar + tar -zcf \ + "$WORKSPACE/kibana-default-plugins.tar.gz" \ + x-pack/plugins/**/target/public \ + x-pack/test/**/target/public \ + examples/**/target/public \ + x-pack/examples/**/target/public \ + test/**/target/public + shopt -u globstar fi diff --git a/typings/elasticsearch/search.d.ts b/typings/elasticsearch/search.d.ts index c9bf3b1d8b7bc..6f427348a5286 100644 --- a/typings/elasticsearch/search.d.ts +++ b/typings/elasticsearch/search.d.ts @@ -49,7 +49,7 @@ type ValueTypeOfField = T extends Record type MaybeArray = T | T[]; -type Fields = MaybeArray; +type Fields = Required['body']['fields']; type DocValueFields = MaybeArray; export type SearchHit< @@ -58,7 +58,7 @@ export type SearchHit< TDocValueFields extends DocValueFields | undefined = undefined > = Omit & (TSource extends false ? {} : { _source: TSource }) & - (TFields extends estypes.Fields + (TFields extends Fields ? { fields: Partial, unknown[]>>; } @@ -77,7 +77,7 @@ type HitsOf< > = Array< SearchHit< TOptions extends { _source: false } ? undefined : TDocument, - TOptions extends { fields: estypes.Fields } ? TOptions['fields'] : undefined, + TOptions extends { fields: Fields } ? TOptions['fields'] : undefined, TOptions extends { docvalue_fields: DocValueFields } ? TOptions['docvalue_fields'] : undefined > >; diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 76ed71ebbf270..6cccdfaefecba 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -130,7 +130,7 @@ def functionalTestProcess(String name, String script) { def ossCiGroupProcess(ciGroup, withDelay = false) { return functionalTestProcess("ciGroup" + ciGroup) { - if (withDelay) { + if (withDelay && !(ciGroup instanceof String) && !(ciGroup instanceof GString)) { sleep((ciGroup-1)*30) // smooth out CPU spikes from ES startup } @@ -147,7 +147,7 @@ def ossCiGroupProcess(ciGroup, withDelay = false) { def xpackCiGroupProcess(ciGroup, withDelay = false) { return functionalTestProcess("xpack-ciGroup" + ciGroup) { - if (withDelay) { + if (withDelay && !(ciGroup instanceof String) && !(ciGroup instanceof GString)) { sleep((ciGroup-1)*30) // smooth out CPU spikes from ES startup } withEnv([ @@ -311,11 +311,36 @@ def buildOss(maxWorkers = '') { } } -def buildXpack(maxWorkers = '') { +def getBuildArtifactBucket() { + def dir = env.ghprbPullId ? "pr-${env.ghprbPullId}" : buildState.get('checkoutInfo').branch.replace("/", "__") + return "gs://ci-artifacts.kibana.dev/default-build/${dir}/${buildState.get('checkoutInfo').commit}" +} + +def buildXpack(maxWorkers = '', uploadArtifacts = false) { notifyOnError { withEnv(["KBN_OPTIMIZER_MAX_WORKERS=${maxWorkers}"]) { runbld("./test/scripts/jenkins_xpack_build_kibana.sh", "Build X-Pack Kibana") } + + if (uploadArtifacts) { + withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') { + bash(""" + cd "${env.WORKSPACE}" + gsutil -q -m cp 'kibana-default.tar.gz' '${getBuildArtifactBucket()}/' + gsutil -q -m cp 'kibana-default-plugins.tar.gz' '${getBuildArtifactBucket()}/' + """, "Upload Default Build artifacts to GCS") + } + } + } +} + +def downloadDefaultBuildArtifacts() { + withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') { + bash(""" + cd "${env.WORKSPACE}" + gsutil -q -m cp '${getBuildArtifactBucket()}/kibana-default.tar.gz' ./ + gsutil -q -m cp '${getBuildArtifactBucket()}/kibana-default-plugins.tar.gz' ./ + """, "Download Default Build artifacts from GCS") } } diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 7ed6de8094067..1d33fd1249681 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -49,6 +49,21 @@ def xpackCiGroups() { tasks(ciGroups.collect { kibanaPipeline.xpackCiGroupProcess(it, true) }) } +def xpackCiGroupDocker() { + task { + workers.ci(name: 'xpack-cigroups-docker', size: 'm', ramDisk: true) { + kibanaPipeline.downloadDefaultBuildArtifacts() + kibanaPipeline.bash(""" + cd '${env.WORKSPACE}' + mkdir -p kibana-build-xpack + tar -xzf kibana-default.tar.gz -C kibana-build-xpack --strip=1 + tar -xzf kibana-default-plugins.tar.gz -C kibana + """, "Extract Default Build artifacts") + kibanaPipeline.xpackCiGroupProcess('Docker', true)() + } + } +} + def functionalOss(Map params = [:]) { def config = params ?: [ serverIntegration: true, @@ -100,10 +115,11 @@ def functionalXpack(Map params = [:]) { ] task { - kibanaPipeline.buildXpack(10) + kibanaPipeline.buildXpack(10, true) if (config.ciGroups) { xpackCiGroups() + xpackCiGroupDocker() } if (config.firefox) { diff --git a/vars/workers.groovy b/vars/workers.groovy index 83d439934cbfa..e0c5ddb358d09 100644 --- a/vars/workers.groovy +++ b/vars/workers.groovy @@ -9,6 +9,8 @@ def label(size) { return 'docker && linux && immutable' case 's-highmem': return 'docker && tests-s' + case 'm': + return 'docker && linux && immutable && gobld/machineType:n2-standard-8' case 'm-highmem': return 'docker && linux && immutable && gobld/machineType:n1-highmem-8' case 'l': @@ -77,10 +79,12 @@ def base(Map params, Closure closure) { dir("kibana") { checkoutInfo = getCheckoutInfo() - // use `checkoutInfo` as a flag to indicate that we've already reported the pending commit status - if (buildState.get('shouldSetCommitStatus') && !buildState.has('checkoutInfo')) { + if (!buildState.has('checkoutInfo')) { buildState.set('checkoutInfo', checkoutInfo) - githubCommitStatus.onStart() + + if (buildState.get('shouldSetCommitStatus')) { + githubCommitStatus.onStart() + } } } diff --git a/x-pack/package.json b/x-pack/package.json index 129c8d86adecc..91caae7a976e4 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -27,7 +27,6 @@ "yarn": "^1.21.1" }, "devDependencies": { - "@kbn/es": "link:../packages/kbn-es", "@kbn/plugin-helpers": "link:../packages/kbn-plugin-helpers", "@kbn/storybook": "link:../packages/kbn-storybook", "@kbn/test": "link:../packages/kbn-test" diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index d4100537fa6b8..4cacba6dc880a 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -177,6 +177,35 @@ describe('execute()', () => { ); }); + test('throws when isMissingSecrets is true for connector', async () => { + const executeFn = createExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + isESOCanEncrypt: true, + actionTypeRegistry: actionTypeRegistryMock.create(), + preconfiguredActions: [], + }); + savedObjectsClient.get.mockResolvedValueOnce({ + id: '123', + type: 'action', + attributes: { + name: 'mock-action', + isMissingSecrets: true, + actionTypeId: 'mock-action', + }, + references: [], + }); + await expect( + executeFn(savedObjectsClient, { + id: '123', + params: { baz: false }, + spaceId: 'default', + apiKey: null, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to execute action because no secrets are defined for the \\"mock-action\\" connector."` + ); + }); + test('should ensure action type is enabled', async () => { const mockedActionTypeRegistry = actionTypeRegistryMock.create(); const executeFn = createExecutionEnqueuerFunction({ diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index 025b4d3107798..6421396e66bb2 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -46,12 +46,18 @@ export function createExecutionEnqueuerFunction({ ); } - const actionTypeId = await getActionTypeId( + const { actionTypeId, name, isMissingSecrets } = await getAction( unsecuredSavedObjectsClient, preconfiguredActions, id ); + if (isMissingSecrets) { + throw new Error( + `Unable to execute action because no secrets are defined for the "${name}" connector.` + ); + } + if (!actionTypeRegistry.isActionExecutable(id, actionTypeId, { notifyUsage: true })) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } @@ -91,18 +97,16 @@ function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorO : {}; } -async function getActionTypeId( +async function getAction( unsecuredSavedObjectsClient: SavedObjectsClientContract, preconfiguredActions: PreConfiguredAction[], actionId: string -): Promise { +): Promise { const pcAction = preconfiguredActions.find((action) => action.id === actionId); if (pcAction) { - return pcAction.actionTypeId; + return pcAction; } - const { - attributes: { actionTypeId }, - } = await unsecuredSavedObjectsClient.get('action', actionId); - return actionTypeId; + const { attributes } = await unsecuredSavedObjectsClient.get('action', actionId); + return attributes; } diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 2036ed6c7d343..e3396d542cb62 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -181,7 +181,6 @@ export class ActionsPlugin implements Plugin { - it('Return message with total imported connectors and the proper secrets need to update ', async () => { - const savedObjectConnectors = [ - { - type: 'action', - id: 'ed02cb70-a6ef-11eb-bd58-6b2eae02c6ef', - attributes: { - actionTypeId: '.server-log', - config: {}, - isMissingSecrets: false, - name: 'test', - }, - references: [], - migrationVersion: { action: '7.14.0' }, - coreMigrationVersion: '8.0.0', - updated_at: '2021-04-27T04:10:33.043Z', - version: 'WzcxLDFd', - namespaces: ['default'], - }, - { - type: 'action', - id: 'e8aa94e0-a6ef-11eb-bd58-6b2eae02c6ef', - attributes: { - actionTypeId: '.email', - config: [Object], - isMissingSecrets: true, - name: 'test', - }, - references: [], - migrationVersion: { action: '7.14.0' }, - coreMigrationVersion: '8.0.0', - updated_at: '2021-04-27T04:10:33.043Z', - version: 'WzcyLDFd', - namespaces: ['default'], - }, - ]; - const message = getImportResultMessage( - (savedObjectConnectors as unknown) as Array> - ); - expect(message).toBe('1 connector has secrets that require updates.'); - }); -}); diff --git a/x-pack/plugins/actions/server/saved_objects/get_import_warnings.test.ts b/x-pack/plugins/actions/server/saved_objects/get_import_warnings.test.ts new file mode 100644 index 0000000000000..7f635d6ec13a6 --- /dev/null +++ b/x-pack/plugins/actions/server/saved_objects/get_import_warnings.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { SavedObject } from 'kibana/server'; +import { RawAction } from '../types'; +import { getImportWarnings } from './get_import_warnings'; + +describe('getImportWarnings', () => { + it('return warning message with total imported connectors and the proper secrets need to update', () => { + const savedObjectConnectors = [ + { + type: 'action', + id: 'ed02cb70-a6ef-11eb-bd58-6b2eae02c6ef', + attributes: { + actionTypeId: '.server-log', + config: {}, + isMissingSecrets: false, + name: 'test', + }, + references: [], + migrationVersion: { action: '7.14.0' }, + coreMigrationVersion: '8.0.0', + updated_at: '2021-04-27T04:10:33.043Z', + version: 'WzcxLDFd', + namespaces: ['default'], + }, + { + type: 'action', + id: 'e8aa94e0-a6ef-11eb-bd58-6b2eae02c6ef', + attributes: { + actionTypeId: '.email', + config: {}, + isMissingSecrets: true, + name: 'test', + }, + references: [], + migrationVersion: { action: '7.14.0' }, + coreMigrationVersion: '8.0.0', + updated_at: '2021-04-27T04:10:33.043Z', + version: 'WzcyLDFd', + namespaces: ['default'], + }, + ]; + const warnings = getImportWarnings( + (savedObjectConnectors as unknown) as Array> + ); + expect(warnings[0].message).toBe('1 connector has secrets that require updates.'); + }); + + it('does not return the warning message if all of the imported connectors do not have secrets to update', () => { + const savedObjectConnectors = [ + { + type: 'action', + id: 'ed02cb70-a6ef-11eb-bd58-6b2eae02c6ef', + attributes: { + actionTypeId: '.server-log', + config: {}, + isMissingSecrets: false, + name: 'test', + }, + references: [], + migrationVersion: { action: '7.14.0' }, + coreMigrationVersion: '8.0.0', + updated_at: '2021-04-27T04:10:33.043Z', + version: 'WzcxLDFd', + namespaces: ['default'], + }, + { + type: 'action', + id: 'e8aa94e0-a6ef-11eb-bd58-6b2eae02c6ef', + attributes: { + actionTypeId: '.email', + config: { + hasAuth: false, + }, + isMissingSecrets: false, + name: 'test', + }, + references: [], + migrationVersion: { action: '7.14.0' }, + coreMigrationVersion: '8.0.0', + updated_at: '2021-04-27T04:10:33.043Z', + version: 'WzcyLDFd', + namespaces: ['default'], + }, + ]; + const warnings = getImportWarnings( + (savedObjectConnectors as unknown) as Array> + ); + expect(warnings.length).toBe(0); + }); +}); diff --git a/x-pack/plugins/actions/server/saved_objects/get_import_result_message.ts b/x-pack/plugins/actions/server/saved_objects/get_import_warnings.ts similarity index 57% rename from x-pack/plugins/actions/server/saved_objects/get_import_result_message.ts rename to x-pack/plugins/actions/server/saved_objects/get_import_warnings.ts index 3b88a750c7430..3be0a53e27c00 100644 --- a/x-pack/plugins/actions/server/saved_objects/get_import_result_message.ts +++ b/x-pack/plugins/actions/server/saved_objects/get_import_warnings.ts @@ -6,20 +6,33 @@ */ import { i18n } from '@kbn/i18n'; -import { SavedObject } from 'kibana/server'; +import { SavedObject, SavedObjectsImportWarning } from 'kibana/server'; import { RawAction } from '../types'; -export function getImportResultMessage(connectors: Array>) { +export function getImportWarnings( + connectors: Array> +): SavedObjectsImportWarning[] { const connectorsWithSecrets = connectors.filter( (connector) => connector.attributes.isMissingSecrets ); - return i18n.translate('xpack.actions.savedObjects.onImportText', { + if (connectorsWithSecrets.length === 0) { + return []; + } + const message = i18n.translate('xpack.actions.savedObjects.onImportText', { defaultMessage: '{connectorsWithSecretsLength} {connectorsWithSecretsLength, plural, one {connector has} other {connectors have}} secrets that require updates.', values: { connectorsWithSecretsLength: connectorsWithSecrets.length, }, }); + return [ + { + type: 'action_required', + message, + actionPath: '/app/management/insightsAndAlerting/triggersActions/connectors', + buttonLabel: GO_TO_CONNECTORS_BUTTON_LABLE, + } as SavedObjectsImportWarning, + ]; } export const GO_TO_CONNECTORS_BUTTON_LABLE = 'Go to connectors'; diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index 3c6a78a6f0866..604dd8054719b 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -5,12 +5,18 @@ * 2.0. */ -import { SavedObject, SavedObjectsServiceSetup } from 'kibana/server'; +import { + SavedObject, + SavedObjectsExportTransformContext, + SavedObjectsServiceSetup, +} from 'kibana/server'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; import mappings from './mappings.json'; import { getMigrations } from './migrations'; import { RawAction } from '../types'; -import { getImportResultMessage, GO_TO_CONNECTORS_BUTTON_LABLE } from './get_import_result_message'; +import { getImportWarnings } from './get_import_warnings'; +import { transformConnectorsForExport } from './transform_connectors_for_export'; +import { ActionTypeRegistry } from '../action_type_registry'; export const ACTION_SAVED_OBJECT_TYPE = 'action'; export const ALERT_SAVED_OBJECT_TYPE = 'alert'; @@ -18,7 +24,8 @@ export const ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE = 'action_task_params'; export function setupSavedObjects( savedObjects: SavedObjectsServiceSetup, - encryptedSavedObjects: EncryptedSavedObjectsPluginSetup + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, + actionTypeRegistry: ActionTypeRegistry ) { savedObjects.registerType({ name: ACTION_SAVED_OBJECT_TYPE, @@ -32,16 +39,15 @@ export function setupSavedObjects( getTitle(obj) { return `Connector: [${obj.attributes.name}]`; }, + onExport( + context: SavedObjectsExportTransformContext, + objects: Array> + ) { + return transformConnectorsForExport(objects, actionTypeRegistry); + }, onImport(connectors) { return { - warnings: [ - { - type: 'action_required', - message: getImportResultMessage(connectors as Array>), - actionPath: '/app/management/insightsAndAlerting/triggersActions/connectors', - buttonLabel: GO_TO_CONNECTORS_BUTTON_LABLE, - }, - ], + warnings: getImportWarnings(connectors as Array>), }; }, }, diff --git a/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts new file mode 100644 index 0000000000000..63fe7c0e32047 --- /dev/null +++ b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts @@ -0,0 +1,253 @@ +/* + * 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 { transformConnectorsForExport } from './transform_connectors_for_export'; +import { ActionTypeRegistry, ActionTypeRegistryOpts } from '../action_type_registry'; +import { loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { actionsConfigMock } from '../actions_config.mock'; +import { licensingMock } from '../../../licensing/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { taskManagerMock } from '../../../task_manager/server/mocks'; +import { ActionExecutor, TaskRunnerFactory } from '../lib'; +import { registerBuiltInActionTypes } from '../builtin_action_types'; + +describe('transform connector for export', () => { + const actionTypeRegistryParams: ActionTypeRegistryOpts = { + licensing: licensingMock.createSetup(), + taskManager: taskManagerMock.createSetup(), + taskRunnerFactory: new TaskRunnerFactory(new ActionExecutor({ isESOCanEncrypt: true })), + actionsConfigUtils: actionsConfigMock.create(), + licenseState: licenseStateMock.create(), + preconfiguredActions: [], + }; + const actionTypeRegistry: ActionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + + registerBuiltInActionTypes({ + logger: loggingSystemMock.create().get(), + actionTypeRegistry, + actionsConfigUtils: actionsConfigMock.create(), + }); + + const connectorsWithNoSecrets = [ + { + id: '1', + type: 'action', + attributes: { + actionTypeId: '.email', + name: 'email connector without auth', + isMissingSecrets: false, + config: { + hasAuth: false, + from: 'me@me.com', + host: 'host', + port: 22, + service: null, + secure: null, + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '2', + type: 'action', + attributes: { + actionTypeId: '.index', + name: 'index connector', + isMissingSecrets: false, + config: { + index: 'test-index', + refresh: false, + executionTimeField: null, + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '3', + type: 'action', + attributes: { + actionTypeId: '.server-log', + name: 'server log connector', + isMissingSecrets: false, + config: {}, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '4', + type: 'action', + attributes: { + actionTypeId: '.webhook', + name: 'webhook connector without auth', + isMissingSecrets: false, + config: { + method: 'post', + hasAuth: false, + url: 'https://webhook', + headers: {}, + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + ]; + const connectorsWithSecrets = [ + { + id: '1', + type: 'action', + attributes: { + actionTypeId: '.email', + name: 'email connector with auth', + isMissingSecrets: false, + config: { + hasAuth: true, + from: 'me@me.com', + host: 'host', + port: 22, + service: null, + secure: null, + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '2', + type: 'action', + attributes: { + actionTypeId: '.resilient', + name: 'resilient connector', + isMissingSecrets: false, + config: { + apiUrl: 'https://resilient', + orgId: 'origId', + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '3', + type: 'action', + attributes: { + actionTypeId: '.servicenow', + name: 'servicenow itsm connector', + isMissingSecrets: false, + config: { + apiUrl: 'https://servicenow', + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '4', + type: 'action', + attributes: { + actionTypeId: '.pagerduty', + name: 'pagerduty connector', + isMissingSecrets: false, + config: { + apiUrl: 'https://pagerduty', + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '5', + type: 'action', + attributes: { + actionTypeId: '.jira', + name: 'jira connector', + isMissingSecrets: false, + config: { + apiUrl: 'https://jira', + projectKey: 'foo', + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '6', + type: 'action', + attributes: { + actionTypeId: '.teams', + name: 'teams connector', + isMissingSecrets: false, + config: {}, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '7', + type: 'action', + attributes: { + actionTypeId: '.slack', + name: 'slack connector', + isMissingSecrets: false, + config: {}, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '8', + type: 'action', + attributes: { + actionTypeId: '.servicenow-sir', + name: 'servicenow sir connector', + isMissingSecrets: false, + config: { + apiUrl: 'https://servicenow-sir', + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + { + id: '8', + type: 'action', + attributes: { + actionTypeId: '.webhook', + name: 'webhook connector with auth', + isMissingSecrets: false, + config: { + method: 'post', + hasAuth: true, + url: 'https://webhook', + headers: {}, + }, + secrets: 'asbqw4tqbef', + }, + references: [], + }, + ]; + + it('should not change connectors without secrets', () => { + expect(transformConnectorsForExport(connectorsWithNoSecrets, actionTypeRegistry)).toEqual( + connectorsWithNoSecrets + ); + }); + + it('should remove secrets for connectors with secrets', () => { + expect(transformConnectorsForExport(connectorsWithSecrets, actionTypeRegistry)).toEqual( + connectorsWithSecrets.map((connector) => ({ + ...connector, + attributes: { + ...connector.attributes, + isMissingSecrets: true, + }, + })) + ); + }); +}); diff --git a/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.ts b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.ts new file mode 100644 index 0000000000000..050fa20b7fdab --- /dev/null +++ b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.ts @@ -0,0 +1,50 @@ +/* + * 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 { SavedObject } from 'kibana/server'; +import { ActionTypeRegistry } from '../action_type_registry'; +import { validateSecrets } from '../lib'; +import { RawAction, ActionType } from '../types'; + +export function transformConnectorsForExport( + connectors: SavedObject[], + actionTypeRegistry: ActionTypeRegistry +): Array> { + return connectors.map((c) => { + const connector = c as SavedObject; + return transformConnectorForExport( + connector, + actionTypeRegistry.get(connector.attributes.actionTypeId) + ); + }); +} + +function transformConnectorForExport( + connector: SavedObject, + actionType: ActionType +): SavedObject { + let isMissingSecrets = false; + try { + // If connector requires secrets, this will throw an error + validateSecrets(actionType, {}); + + // If connector has optional (or no) secrets, set isMissingSecrets value to value of hasAuth + // If connector doesn't have hasAuth value, default to isMissingSecrets: false + isMissingSecrets = (connector?.attributes?.config?.hasAuth as boolean) ?? false; + } catch (err) { + isMissingSecrets = true; + } + + // Skip connectors + return { + ...connector, + attributes: { + ...connector.attributes, + isMissingSecrets, + }, + }; +} diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 1db990edef2a9..7bc965f0aa5c8 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -269,7 +269,7 @@ export class AlertsClient { throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - this.validateActions(alertType, data.actions); + await this.validateActions(alertType, data.actions); const createTime = Date.now(); const { references, actions } = await this.denormalizeActions(data.actions); @@ -728,7 +728,7 @@ export class AlertsClient { data.params, alertType.validate?.params ); - this.validateActions(alertType, data.actions); + await this.validateActions(alertType, data.actions); const { actions, references } = await this.denormalizeActions(data.actions); const username = await this.getUserName(); @@ -1434,10 +1434,36 @@ export class AlertsClient { }; } - private validateActions( + private async validateActions( alertType: UntypedNormalizedAlertType, actions: NormalizedAlertAction[] - ): void { + ): Promise { + if (actions.length === 0) { + return; + } + + // check for actions using connectors with missing secrets + const actionsClient = await this.getActionsClient(); + const actionIds = [...new Set(actions.map((action) => action.id))]; + const actionResults = (await actionsClient.getBulk(actionIds)) || []; + const actionsUsingConnectorsWithMissingSecrets = actionResults.filter( + (result) => result.isMissingSecrets + ); + + if (actionsUsingConnectorsWithMissingSecrets.length) { + throw Boom.badRequest( + i18n.translate('xpack.alerting.alertsClient.validateActions.misconfiguredConnector', { + defaultMessage: 'Invalid connectors: {groups}', + values: { + groups: actionsUsingConnectorsWithMissingSecrets + .map((connector) => connector.name) + .join(', '), + }, + }) + ); + } + + // check for actions with invalid action groups const { actionGroups: alertTypeActionGroups } = alertType; const usedAlertActionGroups = actions.map((action) => action.group); const availableAlertTypeActionGroups = new Set(map(alertTypeActionGroups, 'id')); diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index 6f493ced47371..21974cff5eb2f 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -93,9 +93,30 @@ function getMockData( describe('create()', () => { let alertsClient: AlertsClient; + let actionsClient: jest.Mocked; - beforeEach(() => { + beforeEach(async () => { alertsClient = new AlertsClient(alertsClientParams); + actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked; + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + ]); + alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); }); describe('authorization', () => { @@ -104,19 +125,6 @@ describe('create()', () => { bar: boolean; }> ): Promise { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -460,7 +468,6 @@ describe('create()', () => { "scheduledTaskId": "task-123", } `); - const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked; expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test', { notifyUsage: true }); }); @@ -557,6 +564,39 @@ describe('create()', () => { }, ], }); + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + { + id: '2', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -683,19 +723,6 @@ describe('create()', () => { test('creates a disabled alert', async () => { const data = getMockData({ enabled: false }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -761,19 +788,6 @@ describe('create()', () => { test('should trim alert name when creating API key', async () => { const data = getMockData({ name: ' my alert name ' }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1275,10 +1289,8 @@ describe('create()', () => { test('throws error if loading actions fails', async () => { const data = getMockData(); // Reset from default behaviour - const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked; actionsClient.getBulk.mockReset(); actionsClient.getBulk.mockRejectedValueOnce(new Error('Test Error')); - alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( `"Test Error"` ); @@ -1292,19 +1304,6 @@ describe('create()', () => { apiKeysEnabled: true, result: { id: '123', name: '123', api_key: 'abc' }, }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockRejectedValueOnce(new Error('Test failure')); const createdAt = new Date().toISOString(); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -1329,19 +1328,6 @@ describe('create()', () => { test('attempts to remove saved object if scheduling failed', async () => { const data = getMockData(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1386,19 +1372,6 @@ describe('create()', () => { test('returns task manager error if cleanup fails, logs to console', async () => { const data = getMockData(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1455,19 +1428,6 @@ describe('create()', () => { apiKeysEnabled: true, result: { id: '123', name: '123', api_key: 'abc' }, }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1579,19 +1539,6 @@ describe('create()', () => { test(`doesn't create API key for disabled alerts`, async () => { const data = getMockData({ enabled: false }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1722,4 +1669,32 @@ describe('create()', () => { `"Fail"` ); }); + + test('throws error when adding action using connector with missing secrets', async () => { + const data = getMockData(); + // Reset from default behaviour + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValueOnce([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: true, + name: 'email connector', + isPreconfigured: false, + }, + ]); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid connectors: email connector"` + ); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index cdbfbbac9f9a1..c5e30e29efb44 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -60,6 +60,7 @@ setGlobalDate(); describe('update()', () => { let alertsClient: AlertsClient; + let actionsClient: jest.Mocked; const existingAlert = { id: '1', type: 'alert', @@ -96,8 +97,28 @@ describe('update()', () => { }, }; - beforeEach(() => { + beforeEach(async () => { alertsClient = new AlertsClient(alertsClientParams); + actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked; + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + ]); + alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); alertTypeRegistry.get.mockReturnValue({ @@ -113,6 +134,39 @@ describe('update()', () => { }); test('updates given parameters', async () => { + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + { + id: '2', + actionTypeId: 'test2', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -342,25 +396,11 @@ describe('update()', () => { "version": "123", } `); - const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked; expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test', { notifyUsage: true }); expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test2', { notifyUsage: true }); }); it('calls the createApiKey function', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); alertsClientParams.createAPIKey.mockResolvedValueOnce({ apiKeysEnabled: true, result: { id: '123', name: '123', api_key: 'abc' }, @@ -530,19 +570,6 @@ describe('update()', () => { enabled: false, }, }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -784,19 +811,6 @@ describe('update()', () => { }); it('should trim alert name in the API key name', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -842,19 +856,6 @@ describe('update()', () => { }); it('swallows error when invalidate API key throws', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -914,28 +915,39 @@ describe('update()', () => { it('swallows error when getDecryptedAsInternalUser throws', async () => { encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, }, - { - id: '2', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test2', - }, - references: [], + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + { + id: '2', + actionTypeId: 'test2', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, }, - ], - }); + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -1040,19 +1052,6 @@ describe('update()', () => { apiKeysEnabled: true, result: { id: '234', name: '234', api_key: 'abc' }, }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); unsecuredSavedObjectsClient.create.mockRejectedValue(new Error('Fail')); await expect( alertsClient.update({ @@ -1101,19 +1100,6 @@ describe('update()', () => { async executor() {}, producer: 'alerts', }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ id: alertId, type: 'alert', @@ -1313,6 +1299,84 @@ describe('update()', () => { }); }); + test('throws error when updating action using connector with missing secrets', async () => { + // Reset from default behaviour + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValueOnce([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + }, + { + id: '2', + actionTypeId: 'tes2', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: true, + name: 'another connector', + isPreconfigured: false, + }, + ]); + + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Invalid connectors: another connector"`); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + describe('authorization', () => { beforeEach(() => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index bb4383083fedc..f4a1c0386b54c 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -5,11 +5,15 @@ * 2.0. */ -import { SavedObjectsServiceSetup } from 'kibana/server'; +import { + SavedObject, + SavedObjectsExportTransformContext, + SavedObjectsServiceSetup, +} from 'kibana/server'; import mappings from './mappings.json'; import { getMigrations } from './migrations'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; - +import { transformRulesForExport } from './transform_rule_for_export'; export { partiallyUpdateAlert } from './partially_update_alert'; export const AlertAttributesExcludedFromAAD = [ @@ -43,6 +47,18 @@ export function setupSavedObjects( namespaceType: 'single', migrations: getMigrations(encryptedSavedObjects), mappings: mappings.alert, + management: { + importableAndExportable: true, + getTitle(obj) { + return `Rule: [${obj.attributes.name}]`; + }, + onExport( + context: SavedObjectsExportTransformContext, + objects: Array> + ) { + return transformRulesForExport(objects); + }, + }, }); savedObjects.registerType({ diff --git a/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts new file mode 100644 index 0000000000000..bf181e7299220 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts @@ -0,0 +1,91 @@ +/* + * 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 { transformRulesForExport } from './transform_rule_for_export'; + +describe('transform rule for export', () => { + const date = new Date().toISOString(); + const mockRules = [ + { + id: '1', + type: 'alert', + attributes: { + enabled: true, + name: 'rule-name', + tags: ['tag-1', 'tag-2'], + alertTypeId: '123', + consumer: 'alert-consumer', + schedule: { interval: '1m' }, + actions: [], + params: {}, + createdBy: 'me', + updatedBy: 'me', + createdAt: date, + updatedAt: date, + apiKey: '4tndskbuhewotw4klrhgjewrt9u', + apiKeyOwner: 'me', + throttle: null, + notifyWhen: 'onActionGroupChange', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'active', + lastExecutionDate: '2020-08-20T19:23:38Z', + error: null, + }, + scheduledTaskId: '2q5tjbf3q45twer', + }, + references: [], + }, + { + id: '2', + type: 'alert', + attributes: { + enabled: false, + name: 'disabled-rule', + tags: ['tag-1'], + alertTypeId: '456', + consumer: 'alert-consumer', + schedule: { interval: '1h' }, + actions: [], + params: {}, + createdBy: 'you', + updatedBy: 'you', + createdAt: date, + updatedAt: date, + apiKey: null, + apiKeyOwner: null, + throttle: null, + notifyWhen: 'onActionGroupChange', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'pending', + lastExecutionDate: '2020-08-20T19:23:38Z', + error: null, + }, + scheduledTaskId: null, + }, + references: [], + }, + ]; + + it('should disable rule and clear sensitive values', () => { + expect(transformRulesForExport(mockRules)).toEqual( + mockRules.map((rule) => ({ + ...rule, + attributes: { + ...rule.attributes, + enabled: false, + apiKey: null, + apiKeyOwner: null, + scheduledTaskId: null, + }, + })) + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.ts b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.ts new file mode 100644 index 0000000000000..c33bbceaf8363 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.ts @@ -0,0 +1,26 @@ +/* + * 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 { SavedObject } from 'kibana/server'; +import { RawAlert } from '../types'; + +export function transformRulesForExport(rules: SavedObject[]): Array> { + return rules.map((rule) => transformRuleForExport(rule as SavedObject)); +} + +function transformRuleForExport(rule: SavedObject): SavedObject { + return { + ...rule, + attributes: { + ...rule.attributes, + enabled: false, + apiKey: null, + apiKeyOwner: null, + scheduledTaskId: null, + }, + }; +} diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 8c3d22eeb2e7f..d3b0231f5cd55 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -547,6 +547,7 @@ export class TaskRunner< if (isAlertSavedObjectNotFoundError(err, alertId)) { this.logger.debug(message); } else { + this.logger.error(require('util').inspect(err, { depth: null })); this.logger.error(message); } return originalState; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts new file mode 100644 index 0000000000000..106c380b43207 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +describe('APM depp links', () => { + before(() => { + cy.loginAsReadOnlyUser(); + }); + it('navigates to apm links on search elastic', () => { + cy.visit('/'); + cy.get('[data-test-subj="nav-search-input"]').type('APM'); + cy.contains('APM'); + cy.contains('APM / Services'); + cy.contains('APM / Traces'); + cy.contains('APM / Service Map'); + + // navigates to home page + cy.contains('APM').click(); + cy.url().should('include', '/apm/services'); + + cy.get('[data-test-subj="nav-search-input"]').type('APM'); + // navigates to services page + cy.contains('APM / Services').click(); + cy.url().should('include', '/apm/services'); + + cy.get('[data-test-subj="nav-search-input"]').type('APM'); + // navigates to traces page + cy.contains('APM / Traces').click(); + cy.url().should('include', '/apm/traces'); + + cy.get('[data-test-subj="nav-search-input"]').type('APM'); + // navigates to service maps + cy.contains('APM / Service Map').click(); + cy.url().should('include', '/apm/service-map'); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts index 812fa8253a698..d25251f457e36 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts @@ -4,26 +4,60 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import url from 'url'; import archives_metadata from '../../fixtures/es_archiver/archives_metadata'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; + const { start, end } = archives_metadata['apm_8.0.0']; +const servicesPath = '/app/apm/services'; +const baseUrl = url.format({ + pathname: servicesPath, + query: { rangeFrom: start, rangeTo: end }, +}); + describe('Home page', () => { before(() => { esArchiverLoad('apm_8.0.0'); - cy.loginAsReadOnlyUser(); }); after(() => { esArchiverUnload('apm_8.0.0'); }); + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); it('Redirects to service page with rangeFrom and rangeTo added to the URL', () => { - const baseUrl = url.format({ - pathname: '/app/apm', - query: { rangeFrom: start, rangeTo: end }, - }); + cy.visit('/app/apm'); - cy.visit(baseUrl); + cy.url().should( + 'include', + 'app/apm/services?rangeFrom=now-15m&rangeTo=now' + ); cy.get('.euiTabs .euiTab-isSelected').contains('Services'); }); + + it('includes services with only metric documents', () => { + cy.visit( + `${baseUrl}&kuery=not%2520(processor.event%2520%253A%2522transaction%2522%2520)` + ); + cy.contains('opbeans-python'); + cy.contains('opbeans-java'); + cy.contains('opbeans-node'); + }); + + describe('navigations', () => { + it('navigates to service overview page with transaction type', () => { + const kuery = encodeURIComponent( + 'transaction.name : "taskManager markAvailableTasksAsClaimed"' + ); + cy.visit(`${baseUrl}&kuery=${kuery}`); + cy.contains('taskManager'); + cy.contains('kibana').click(); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'taskManager' + ); + }); + }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts new file mode 100644 index 0000000000000..d253a290f4a51 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts @@ -0,0 +1,145 @@ +/* + * 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 url from 'url'; +import archives_metadata from '../../../fixtures/es_archiver/archives_metadata'; +import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver'; + +const { start, end } = archives_metadata['apm_8.0.0']; + +const serviceOverviewPath = '/app/apm/services/kibana/overview'; +const baseUrl = url.format({ + pathname: serviceOverviewPath, + query: { rangeFrom: start, rangeTo: end }, +}); + +const apisToIntercept = [ + { + endpoint: '/api/apm/services/kibana/transactions/charts/latency', + as: 'latencyChartRequest', + }, + { + endpoint: '/api/apm/services/kibana/throughput', + as: 'throughputChartRequest', + }, + { + endpoint: '/api/apm/services/kibana/transactions/charts/error_rate', + as: 'errorRateChartRequest', + }, + { + endpoint: + '/api/apm/services/kibana/transactions/groups/detailed_statistics', + as: 'transactionGroupsDetailedRequest', + }, + { + endpoint: + '/api/apm/services/kibana/service_overview_instances/detailed_statistics', + as: 'instancesDetailedRequest', + }, + { + endpoint: + '/api/apm/services/kibana/service_overview_instances/main_statistics', + as: 'instancesMainStatisticsRequest', + }, + { + endpoint: '/api/apm/services/kibana/error_groups/main_statistics', + as: 'errorGroupsMainStatisticsRequest', + }, + { + endpoint: '/api/apm/services/kibana/transaction/charts/breakdown', + as: 'transactonBreakdownRequest', + }, + { + endpoint: '/api/apm/services/kibana/transactions/groups/main_statistics', + as: 'transactionsGroupsMainStatisticsRequest', + }, +]; + +describe('Service overview - header filters', () => { + before(() => { + esArchiverLoad('apm_8.0.0'); + }); + after(() => { + esArchiverUnload('apm_8.0.0'); + }); + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + describe('Filtering by transaction type', () => { + it('changes url when selecting different value', () => { + cy.visit(baseUrl); + cy.contains('Kibana'); + cy.url().should('not.include', 'transactionType'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'request' + ); + cy.get('[data-test-subj="headerFilterTransactionType"]').select( + 'taskManager' + ); + cy.url().should('include', 'transactionType=taskManager'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'taskManager' + ); + }); + + it('calls APIs with correct transaction type', () => { + apisToIntercept.map(({ endpoint, as }) => { + cy.intercept('GET', endpoint).as(as); + }); + cy.visit(baseUrl); + cy.contains('Kibana'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'request' + ); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: apisToIntercept.map(({ as }) => `@${as}`), + value: 'transactionType=request', + }); + + cy.get('[data-test-subj="headerFilterTransactionType"]').select( + 'taskManager' + ); + cy.url().should('include', 'transactionType=taskManager'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'taskManager' + ); + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: apisToIntercept.map(({ as }) => `@${as}`), + value: 'transactionType=taskManager', + }); + }); + }); + + describe('Filtering by kuerybar', () => { + it('filters by transaction.name', () => { + cy.visit( + url.format({ + pathname: '/app/apm/services/opbeans-java/overview', + query: { rangeFrom: start, rangeTo: end }, + }) + ); + cy.contains('opbeans-java'); + cy.get('[data-test-subj="headerFilterKuerybar"]').type('transaction.n'); + cy.contains('transaction.name'); + cy.get('[data-test-subj="suggestionContainer"]') + .find('li') + .first() + .click(); + cy.get('[data-test-subj="headerFilterKuerybar"]').type(':'); + cy.get('[data-test-subj="suggestionContainer"]') + .find('li') + .first() + .click(); + cy.get('[data-test-subj="suggestionContainer"]').realPress('{enter}'); + cy.url().should('include', '&kuery=transaction.name'); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts new file mode 100644 index 0000000000000..2d76dfe977ef7 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts @@ -0,0 +1,114 @@ +/* + * 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 url from 'url'; +import archives_metadata from '../../../fixtures/es_archiver/archives_metadata'; +import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver'; + +const { start, end } = archives_metadata['apm_8.0.0']; + +const serviceOverviewPath = '/app/apm/services/opbeans-java/overview'; +const baseUrl = url.format({ + pathname: serviceOverviewPath, + query: { rangeFrom: start, rangeTo: end }, +}); + +const apisToIntercept = [ + { + endpoint: + '/api/apm/services/opbeans-java/service_overview_instances/main_statistics', + as: 'instancesMainRequest', + }, + { + endpoint: + '/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics', + as: 'instancesDetailsRequest', + }, + { + endpoint: + '/api/apm/services/opbeans-java/service_overview_instances/details/02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c', + as: 'instanceDetailsRequest', + }, + { + endpoint: + '/api/apm/services/opbeans-java/service_overview_instances/details/02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c', + as: 'instanceDetailsRequest', + }, +]; + +describe('Instances table', () => { + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + describe('when data is not loaded', () => { + it('shows empty message', () => { + cy.visit(baseUrl); + cy.contains('opbeans-java'); + cy.get('[data-test-subj="serviceInstancesTableContainer"]').contains( + 'No items found' + ); + }); + }); + + describe('when data is loaded', () => { + before(() => { + esArchiverLoad('apm_8.0.0'); + }); + after(() => { + esArchiverUnload('apm_8.0.0'); + }); + const serviceNodeName = + '02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c'; + it('has data in the table', () => { + cy.visit(baseUrl); + cy.contains('opbeans-java'); + cy.contains(serviceNodeName); + }); + it('shows instance details', () => { + apisToIntercept.map(({ endpoint, as }) => { + cy.intercept('GET', endpoint).as(as); + }); + + cy.visit(baseUrl); + cy.contains('opbeans-java'); + + cy.wait('@instancesMainRequest'); + cy.contains(serviceNodeName); + + cy.wait('@instancesDetailsRequest'); + cy.get( + `[data-test-subj="instanceDetailsButton_${serviceNodeName}"]` + ).realClick(); + cy.get('[data-test-subj="loadingSpinner"]').should('be.visible'); + cy.wait('@instanceDetailsRequest').then(() => { + cy.contains('Service'); + }); + }); + it('shows actions available', () => { + apisToIntercept.map(({ endpoint, as }) => { + cy.intercept('GET', endpoint).as(as); + }); + + cy.visit(baseUrl); + cy.contains('opbeans-java'); + + cy.wait('@instancesMainRequest'); + cy.contains(serviceNodeName); + + cy.wait('@instancesDetailsRequest'); + cy.get( + `[data-test-subj="instanceActionsButton_${serviceNodeName}"]` + ).realClick(); + cy.contains('Pod logs'); + cy.contains('Pod metrics'); + cy.contains('Container logs'); + cy.contains('Container metrics'); + cy.contains('Filter overview by instance'); + cy.contains('Metrics'); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts new file mode 100644 index 0000000000000..c3b4d979829fa --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts @@ -0,0 +1,67 @@ +/* + * 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 url from 'url'; +import archives_metadata from '../../../fixtures/es_archiver/archives_metadata'; +import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver'; + +const { start, end } = archives_metadata['apm_8.0.0']; + +const serviceOverviewPath = '/app/apm/services/opbeans-node/overview'; +const baseUrl = url.format({ + pathname: serviceOverviewPath, + query: { rangeFrom: start, rangeTo: end }, +}); + +describe('Service Overview', () => { + before(() => { + esArchiverLoad('apm_8.0.0'); + }); + after(() => { + esArchiverUnload('apm_8.0.0'); + }); + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + it('persists transaction type selected when clicking on Transactions tab', () => { + cy.visit(baseUrl); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'request' + ); + cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + + cy.get('[data-test-subj="tab_transactions"]').click(); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + }); + + it('persists transaction type selected when clicking on View Transactions link', () => { + cy.visit(baseUrl); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'request' + ); + cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + + cy.contains('View transactions').click(); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts new file mode 100644 index 0000000000000..fc17d1975d631 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts @@ -0,0 +1,48 @@ +/* + * 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 url from 'url'; +import archives_metadata from '../../../fixtures/es_archiver/archives_metadata'; +import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver'; + +const { start, end } = archives_metadata['apm_8.0.0']; + +const serviceOverviewPath = '/app/apm/services/opbeans-node/transactions'; +const baseUrl = url.format({ + pathname: serviceOverviewPath, + query: { rangeFrom: start, rangeTo: end }, +}); + +describe('Transactions Overview', () => { + before(() => { + esArchiverLoad('apm_8.0.0'); + }); + after(() => { + esArchiverUnload('apm_8.0.0'); + }); + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + it('persists transaction type selected when clicking on Overview tab', () => { + cy.visit(baseUrl); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'request' + ); + cy.get('[data-test-subj="headerFilterTransactionType"]').select('Worker'); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + + cy.get('[data-test-subj="tab_overview"]').click(); + cy.get('[data-test-subj="headerFilterTransactionType"]').should( + 'have.value', + 'Worker' + ); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 37e498619bdd4..31eab9507ef5e 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -5,6 +5,7 @@ * 2.0. */ import 'cypress-real-events/support'; +import { Interception } from 'cypress/types/net-stubbing'; Cypress.Commands.add('loginAsReadOnlyUser', () => { cy.loginAs({ username: 'apm_read_user', password: 'changeme' }); @@ -39,3 +40,24 @@ Cypress.Commands.add('changeTimeRange', (value: string) => { cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); cy.contains(value).click(); }); + +Cypress.Commands.add( + 'expectAPIsToHaveBeenCalledWith', + ({ + apisIntercepted, + value, + }: { + apisIntercepted: string[]; + value: string; + }) => { + cy.wait(apisIntercepted).then((interceptions) => { + if (Array.isArray(interceptions)) { + interceptions.map((interception) => { + expect(interception.request.url).include(value); + }); + } else { + expect((interceptions as Interception).request.url).include(value); + } + }); + } +); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts index fff38c9c6d18b..b47e664e0a0f8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts @@ -11,5 +11,9 @@ declare namespace Cypress { loginAsSuperUser(): void; loginAs(params: { username: string; password: string }): void; changeTimeRange(value: string): void; + expectAPIsToHaveBeenCalledWith(params: { + apisIntercepted: string[]; + value: string; + }): void; } } diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 28e4a7b36e740..76d544c3bc6f5 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -10,7 +10,8 @@ "triggersActionsUi", "embeddable", "infra", - "observability" + "observability", + "ruleRegistry" ], "optionalPlugins": [ "spaces", @@ -26,8 +27,13 @@ ], "server": true, "ui": true, - "configPath": ["xpack", "apm"], - "extraPublicDirs": ["public/style/variables"], + "configPath": [ + "xpack", + "apm" + ], + "extraPublicDirs": [ + "public/style/variables" + ], "requiredBundles": [ "home", "kibanaReact", diff --git a/x-pack/plugins/apm/public/application/application.test.tsx b/x-pack/plugins/apm/public/application/application.test.tsx index e6415f76c60dc..4ec654a6c0bfd 100644 --- a/x-pack/plugins/apm/public/application/application.test.tsx +++ b/x-pack/plugins/apm/public/application/application.test.tsx @@ -39,7 +39,11 @@ describe('renderApp', () => { }); it('renders the app', () => { - const { core, config, apmRuleRegistry } = mockApmPluginContextValue; + const { + core, + config, + observabilityRuleTypeRegistry, + } = mockApmPluginContextValue; const plugins = { licensing: { license$: new Observable() }, triggersActionsUi: { actionTypeRegistry: {}, alertTypeRegistry: {} }, @@ -92,7 +96,7 @@ describe('renderApp', () => { appMountParameters: params as any, pluginsStart: startDeps as any, config, - apmRuleRegistry, + observabilityRuleTypeRegistry, }); }); diff --git a/x-pack/plugins/apm/public/application/csmApp.tsx b/x-pack/plugins/apm/public/application/csmApp.tsx index 17905074cfec1..11a2777f47f6a 100644 --- a/x-pack/plugins/apm/public/application/csmApp.tsx +++ b/x-pack/plugins/apm/public/application/csmApp.tsx @@ -12,6 +12,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Route, Router } from 'react-router-dom'; import { DefaultTheme, ThemeProvider } from 'styled-components'; +import type { ObservabilityRuleTypeRegistry } from '../../../observability/public'; import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; import { KibanaContextProvider, @@ -26,11 +27,7 @@ import { ApmPluginContext } from '../context/apm_plugin/apm_plugin_context'; import { UrlParamsProvider } from '../context/url_params_context/url_params_context'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { ConfigSchema } from '../index'; -import { - ApmPluginSetupDeps, - ApmPluginStartDeps, - ApmRuleRegistry, -} from '../plugin'; +import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { px, units } from '../style/variables'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; @@ -77,14 +74,14 @@ export function CsmAppRoot({ deps, config, corePlugins: { embeddable, maps }, - apmRuleRegistry, + observabilityRuleTypeRegistry, }: { appMountParameters: AppMountParameters; core: CoreStart; deps: ApmPluginSetupDeps; config: ConfigSchema; corePlugins: ApmPluginStartDeps; - apmRuleRegistry: ApmRuleRegistry; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; }) { const { history } = appMountParameters; const i18nCore = core.i18n; @@ -94,7 +91,7 @@ export function CsmAppRoot({ config, core, plugins, - apmRuleRegistry, + observabilityRuleTypeRegistry, }; return ( @@ -125,14 +122,14 @@ export const renderApp = ({ appMountParameters, config, corePlugins, - apmRuleRegistry, + observabilityRuleTypeRegistry, }: { core: CoreStart; deps: ApmPluginSetupDeps; appMountParameters: AppMountParameters; config: ConfigSchema; corePlugins: ApmPluginStartDeps; - apmRuleRegistry: ApmRuleRegistry; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; }) => { const { element } = appMountParameters; @@ -151,7 +148,7 @@ export const renderApp = ({ deps={deps} config={config} corePlugins={corePlugins} - apmRuleRegistry={apmRuleRegistry} + observabilityRuleTypeRegistry={observabilityRuleTypeRegistry} />, element ); diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index acb55a02599f1..e2a0bdb6b48b1 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -13,6 +13,7 @@ import ReactDOM from 'react-dom'; import { Route, Router, Switch } from 'react-router-dom'; import 'react-vis/dist/style.css'; import { DefaultTheme, ThemeProvider } from 'styled-components'; +import type { ObservabilityRuleTypeRegistry } from '../../../observability/public'; import { euiStyled } from '../../../../../src/plugins/kibana_react/common'; import { ConfigSchema } from '../'; import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; @@ -30,11 +31,7 @@ import { import { LicenseProvider } from '../context/license/license_context'; import { UrlParamsProvider } from '../context/url_params_context/url_params_context'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; -import { - ApmPluginSetupDeps, - ApmPluginStartDeps, - ApmRuleRegistry, -} from '../plugin'; +import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; import { setHelpExtension } from '../setHelpExtension'; @@ -112,14 +109,14 @@ export const renderApp = ({ appMountParameters, config, pluginsStart, - apmRuleRegistry, + observabilityRuleTypeRegistry, }: { coreStart: CoreStart; pluginsSetup: ApmPluginSetupDeps; appMountParameters: AppMountParameters; config: ConfigSchema; pluginsStart: ApmPluginStartDeps; - apmRuleRegistry: ApmRuleRegistry; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; }) => { const { element } = appMountParameters; const apmPluginContextValue = { @@ -127,7 +124,7 @@ export const renderApp = ({ config, core: coreStart, plugins: pluginsSetup, - apmRuleRegistry, + observabilityRuleTypeRegistry, }; // render APM feedback link in global help menu diff --git a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts index 98c8b99411bc3..6467d4c3ee61c 100644 --- a/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts +++ b/x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts @@ -8,9 +8,19 @@ import { i18n } from '@kbn/i18n'; import { lazy } from 'react'; import { stringify } from 'querystring'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_SEVERITY_LEVEL, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import type { ObservabilityRuleTypeRegistry } from '../../../../observability/public'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import { AlertType } from '../../../common/alert_types'; -import type { ApmRuleRegistry } from '../../plugin'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_TYPE, +} from '../../../common/elasticsearch_fieldnames'; const format = ({ pathname, @@ -22,28 +32,32 @@ const format = ({ return `${pathname}?${stringify(query)}`; }; -export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { - apmRuleRegistry.registerType({ +export function registerApmAlerts( + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry +) { + observabilityRuleTypeRegistry.register({ id: AlertType.ErrorCount, description: i18n.translate('xpack.apm.alertTypes.errorCount.description', { defaultMessage: 'Alert when the number of errors in a service exceeds a defined threshold.', }), - format: ({ alert }) => { + format: ({ fields }) => { return { reason: i18n.translate('xpack.apm.alertTypes.errorCount.reason', { defaultMessage: `Error count is greater than {threshold} (current value is {measured}) for {serviceName}`, values: { - threshold: alert['kibana.observability.evaluation.threshold'], - measured: alert['kibana.observability.evaluation.value'], - serviceName: alert['service.name']!, + threshold: fields[ALERT_EVALUATION_THRESHOLD], + measured: fields[ALERT_EVALUATION_VALUE], + serviceName: String(fields[SERVICE_NAME][0]), }, }), link: format({ - pathname: `/app/apm/services/${alert['service.name']!}/errors`, + pathname: `/app/apm/services/${String( + fields[SERVICE_NAME][0] + )}/errors`, query: { - ...(alert['service.environment'] - ? { environment: alert['service.environment'] } + ...(fields[SERVICE_ENVIRONMENT]?.[0] + ? { environment: String(fields[SERVICE_ENVIRONMENT][0]) } : { environment: ENVIRONMENT_ALL.value }), }, }), @@ -71,7 +85,7 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { ), }); - apmRuleRegistry.registerType({ + observabilityRuleTypeRegistry.register({ id: AlertType.TransactionDuration, description: i18n.translate( 'xpack.apm.alertTypes.transactionDuration.description', @@ -80,28 +94,24 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { 'Alert when the latency of a specific transaction type in a service exceeds a defined threshold.', } ), - format: ({ alert, formatters: { asDuration } }) => ({ + format: ({ fields, formatters: { asDuration } }) => ({ reason: i18n.translate( 'xpack.apm.alertTypes.transactionDuration.reason', { defaultMessage: `Latency is above {threshold} (current value is {measured}) for {serviceName}`, values: { - threshold: asDuration( - alert['kibana.observability.evaluation.threshold'] - ), - measured: asDuration( - alert['kibana.observability.evaluation.value'] - ), - serviceName: alert['service.name']!, + threshold: asDuration(fields[ALERT_EVALUATION_THRESHOLD]), + measured: asDuration(fields[ALERT_EVALUATION_VALUE]), + serviceName: String(fields[SERVICE_NAME][0]), }, } ), link: format({ - pathname: `/app/apm/services/${alert['service.name']!}`, + pathname: `/app/apm/services/${fields[SERVICE_NAME][0]!}`, query: { - transactionType: alert['transaction.type']!, - ...(alert['service.environment'] - ? { environment: alert['service.environment'] } + transactionType: fields[TRANSACTION_TYPE][0]!, + ...(fields[SERVICE_ENVIRONMENT]?.[0] + ? { environment: String(fields[SERVICE_ENVIRONMENT][0]) } : { environment: ENVIRONMENT_ALL.value }), }, }), @@ -131,7 +141,7 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { ), }); - apmRuleRegistry.registerType({ + observabilityRuleTypeRegistry.register({ id: AlertType.TransactionErrorRate, description: i18n.translate( 'xpack.apm.alertTypes.transactionErrorRate.description', @@ -140,30 +150,24 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { 'Alert when the rate of transaction errors in a service exceeds a defined threshold.', } ), - format: ({ alert, formatters: { asPercent } }) => ({ + format: ({ fields, formatters: { asPercent } }) => ({ reason: i18n.translate( 'xpack.apm.alertTypes.transactionErrorRate.reason', { defaultMessage: `Transaction error rate is greater than {threshold} (current value is {measured}) for {serviceName}`, values: { - threshold: asPercent( - alert['kibana.observability.evaluation.threshold'], - 100 - ), - measured: asPercent( - alert['kibana.observability.evaluation.value'], - 100 - ), - serviceName: alert['service.name']!, + threshold: asPercent(fields[ALERT_EVALUATION_THRESHOLD], 100), + measured: asPercent(fields[ALERT_EVALUATION_VALUE], 100), + serviceName: String(fields[SERVICE_NAME][0]), }, } ), link: format({ - pathname: `/app/apm/services/${alert['service.name']!}`, + pathname: `/app/apm/services/${String(fields[SERVICE_NAME][0]!)}`, query: { - transactionType: alert['transaction.type']!, - ...(alert['service.environment'] - ? { environment: alert['service.environment'] } + transactionType: String(fields[TRANSACTION_TYPE][0]!), + ...(fields[SERVICE_ENVIRONMENT]?.[0] + ? { environment: String(fields[SERVICE_ENVIRONMENT][0]) } : { environment: ENVIRONMENT_ALL.value }), }, }), @@ -193,7 +197,7 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { ), }); - apmRuleRegistry.registerType({ + observabilityRuleTypeRegistry.register({ id: AlertType.TransactionDurationAnomaly, description: i18n.translate( 'xpack.apm.alertTypes.transactionDurationAnomaly.description', @@ -201,24 +205,24 @@ export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) { defaultMessage: 'Alert when the latency of a service is abnormal.', } ), - format: ({ alert }) => ({ + format: ({ fields }) => ({ reason: i18n.translate( 'xpack.apm.alertTypes.transactionDurationAnomaly.reason', { defaultMessage: `{severityLevel} anomaly detected for {serviceName} (score was {measured})`, values: { - serviceName: alert['service.name'], - severityLevel: alert['kibana.rac.alert.severity.level'], - measured: alert['kibana.observability.evaluation.value'], + serviceName: String(fields[SERVICE_NAME][0]), + severityLevel: String(fields[ALERT_SEVERITY_LEVEL]), + measured: Number(fields[ALERT_EVALUATION_VALUE]), }, } ), link: format({ - pathname: `/app/apm/services/${alert['service.name']!}`, + pathname: `/app/apm/services/${String(fields[SERVICE_NAME][0])}`, query: { - transactionType: alert['transaction.type']!, - ...(alert['service.environment'] - ? { environment: alert['service.environment'] } + transactionType: String(fields[TRANSACTION_TYPE][0]), + ...(fields[SERVICE_ENVIRONMENT]?.[0] + ? { environment: String(fields[SERVICE_ENVIRONMENT][0]) } : { environment: ENVIRONMENT_ALL.value }), }, }), diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx index 19a567a3866bd..aa11eb06d853f 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx @@ -19,6 +19,7 @@ import { import { EuiTitle } from '@elastic/eui'; import d3 from 'd3'; import React from 'react'; +import { RULE_ID } from '../../../../../../rule_registry/common/technical_rule_data_field_names'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { asRelativeDateTimeRange } from '../../../../../common/utils/formatters'; @@ -115,7 +116,7 @@ export function ErrorDistribution({ distribution, title }: Props) { /> {getAlertAnnotations({ alerts: alerts?.filter( - (alert) => alert['rule.id'] === AlertType.ErrorCount + (alert) => alert[RULE_ID]?.[0] === AlertType.ErrorCount ), theme, })} diff --git a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index b1bcf561bed84..f13cce3fd9b40 100644 --- a/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -4,10 +4,6 @@ exports[`Home component should render services 1`] = ` !t.hidden) .map(({ href, key, text }) => ( - + {text} ))} diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx index 7607b6fd91392..47dfb64a8b7ef 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx @@ -8,6 +8,13 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import { parse, format } from 'url'; import { uniqBy } from 'lodash'; +import { parseTechnicalFields } from '../../../../../../rule_registry/common'; +import { + ALERT_ID, + ALERT_START, + RULE_ID, + RULE_NAME, +} from '../../../../../../rule_registry/common/technical_rule_data_field_names'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; @@ -20,7 +27,7 @@ interface AlertDetailProps { export function AlertDetails({ alerts }: AlertDetailProps) { const { - apmRuleRegistry, + observabilityRuleTypeRegistry, core: { http: { basePath: { prepend }, @@ -32,20 +39,23 @@ export function AlertDetails({ alerts }: AlertDetailProps) { urlParams: { rangeFrom, rangeTo }, } = useUrlParams(); - const collapsedAlerts = uniqBy( - alerts, - (alert) => alert['kibana.rac.alert.id']! + const collapsedAlerts = uniqBy(alerts, (alert) => alert[ALERT_ID]![0]!).map( + (alert) => { + return parseTechnicalFields(alert); + } ); return ( {collapsedAlerts.map((alert) => { - const ruleType = apmRuleRegistry.getTypeByRuleId(alert['rule.id']!); + const formatter = observabilityRuleTypeRegistry.getFormatter( + alert[RULE_ID]! + ); const formatted = { link: undefined, - reason: alert['rule.name'], - ...(ruleType?.format?.({ - alert, + reason: alert[RULE_NAME], + ...(formatter?.({ + fields: alert, formatters: { asDuration, asPercent }, }) ?? {}), }; @@ -55,7 +65,7 @@ export function AlertDetails({ alerts }: AlertDetailProps) { : undefined; return ( - + {parsedLink ? ( @@ -79,7 +89,7 @@ export function AlertDetails({ alerts }: AlertDetailProps) { diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx index 5287e6699aaee..5bd34c689b61f 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx @@ -97,7 +97,7 @@ export function getServiceColumns({ }), width: '40%', sortable: true, - render: (_, { serviceName, agentName }) => ( + render: (_, { serviceName, agentName, transactionType }) => ( )} - + {formatString(serviceName)} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx index 4da5ba5a4ae64..46747e18c44af 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx @@ -234,6 +234,7 @@ export function getColumns({ anchorPosition="leftCenter" button={ toggleRowActionMenu(instanceItem.serviceNodeName) @@ -257,6 +258,7 @@ export function getColumns({ render: (instanceItem: MainStatsServiceInstanceItem) => { return ( toggleRowDetails(instanceItem.serviceNodeName)} aria-label={ itemIdToExpandedRowMap[instanceItem.serviceNodeName] diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx index 909c879b433b0..98443fdf0d0ea 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx @@ -138,12 +138,13 @@ export function ServiceOverviewInstancesTable({ - + {i18n.translate( 'xpack.apm.serviceOverview.transactionsTableLinkText', diff --git a/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js index cbbf762fa341c..e5f0d866e254c 100644 --- a/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js @@ -72,7 +72,12 @@ class Suggestions extends Component { }); return ( - (this.parentNode = node)}>{suggestions} + (this.parentNode = node)} + > + {suggestions} + ); } } diff --git a/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js index 21524a877d4cc..f441497209c3a 100644 --- a/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js @@ -172,6 +172,7 @@ export class Typeahead extends Component { > @@ -159,6 +55,7 @@ exports[`TrustedAppsGrid renders correctly initially 1`] = ` exports[`TrustedAppsGrid renders correctly when failed loading data for the first time 1`] = ` .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -183,9 +80,6 @@ exports[`TrustedAppsGrid renders correctly when failed loading data for the firs
-
@@ -211,108 +105,6 @@ exports[`TrustedAppsGrid renders correctly when failed loading data for the firs
-
-
-
-
-
-
- -
-
-
-
- -
-
-
@@ -321,6 +113,7 @@ exports[`TrustedAppsGrid renders correctly when failed loading data for the firs exports[`TrustedAppsGrid renders correctly when failed loading data for the second time 1`] = ` .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -345,9 +138,6 @@ exports[`TrustedAppsGrid renders correctly when failed loading data for the seco
-
@@ -605,6 +395,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -629,9 +420,6 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
-
@@ -3542,6 +3330,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` exports[`TrustedAppsGrid renders correctly when loading data for the first time 1`] = ` .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -3564,14 +3353,11 @@ exports[`TrustedAppsGrid renders correctly when loading data for the first time class="c1" >
-
@@ -3591,108 +3377,6 @@ exports[`TrustedAppsGrid renders correctly when loading data for the first time
-
-
-
-
-
-
- -
-
-
-
- -
-
-
@@ -3718,6 +3402,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -3740,14 +3425,11 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="c1" >
-
@@ -6675,6 +6357,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -6699,9 +6382,6 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
-
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index fc031a63b84b1..54f803f5af317 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -18,8 +18,8 @@ export { OS_TITLES } from '../../../common/translations'; export const ABOUT_TRUSTED_APPS = i18n.translate('xpack.securitySolution.trustedapps.aboutInfo', { defaultMessage: - 'Add a trusted application to improve performance or alleviate conflicts with other applications ' + - 'running on your hosts. Trusted applications will be applied to hosts running Endpoint Security.', + 'Add a trusted application to improve performance or alleviate conflicts with other applications running on ' + + 'your hosts. Trusted applications are applied to hosts running the Endpoint Security integration on their agents.', }); export const CONDITION_FIELD_TITLE: { [K in ConditionEntryField]: string } = { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index f9fc5f32aa63a..5efe8cfc16185 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -45,7 +45,9 @@ const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as j describe('When on the Trusted Apps Page', () => { const expectedAboutInfo = - 'Add a trusted application to improve performance or alleviate conflicts with other applications running on your hosts. Trusted applications will be applied to hosts running Endpoint Security.'; + 'Add a trusted application to improve performance or alleviate conflicts with other ' + + 'applications running on your hosts. Trusted applications are applied to hosts running the Endpoint Security ' + + 'integration on their agents.'; const generator = new EndpointDocGenerator('policy-list'); diff --git a/x-pack/plugins/security_solution/public/management/state/async_resource_builders.ts b/x-pack/plugins/security_solution/public/management/state/async_resource_builders.ts new file mode 100644 index 0000000000000..f7de2f68aecd9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/state/async_resource_builders.ts @@ -0,0 +1,45 @@ +/* + * 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 { + FailedResourceState, + LoadedResourceState, + LoadingResourceState, + StaleResourceState, + UninitialisedResourceState, +} from './async_resource_state'; + +export const createUninitialisedResourceState = (): UninitialisedResourceState => { + return { type: 'UninitialisedResourceState' }; +}; + +export const createLoadingResourceState = ( + previousState: StaleResourceState +): LoadingResourceState => { + return { + type: 'LoadingResourceState', + previousState, + }; +}; + +export const createLoadedResourceState = (data: Data): LoadedResourceState => { + return { + type: 'LoadedResourceState', + data, + }; +}; + +export const createFailedResourceState = ( + error: Error, + lastLoadedState?: LoadedResourceState +): FailedResourceState => { + return { + type: 'FailedResourceState', + error, + lastLoadedState, + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/state/index.ts b/x-pack/plugins/security_solution/public/management/state/index.ts new file mode 100644 index 0000000000000..dbaad8d266886 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/state/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './async_resource_state'; +export * from './async_resource_builders'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/helpers.ts index 84dcef327736b..6ee39d9852056 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/helpers.ts @@ -13,8 +13,15 @@ import { INDICATOR_MATCH_SUBFIELDS } from '../../../../../../../common/cti/const import { Ecs } from '../../../../../../../common/ecs'; import { ThreatIndicatorEcs } from '../../../../../../../common/ecs/threat'; -const getIndicatorEcs = (data: Ecs): ThreatIndicatorEcs[] => - get(data, INDICATOR_DESTINATION_PATH) ?? []; +const getIndicatorEcs = (data: Ecs): ThreatIndicatorEcs[] => { + const threatData = get(data, INDICATOR_DESTINATION_PATH); + if (threatData == null) { + return []; + } else if (!Array.isArray(threatData)) { + return [threatData]; + } + return threatData; +}; export const hasThreatMatchValue = (data: Ecs): boolean => getIndicatorEcs(data).some((indicator) => diff --git a/x-pack/plugins/security_solution/scripts/beat_docs/build.js b/x-pack/plugins/security_solution/scripts/beat_docs/build.js index 15b1ec7c84d81..b8bcedda9356a 100644 --- a/x-pack/plugins/security_solution/scripts/beat_docs/build.js +++ b/x-pack/plugins/security_solution/scripts/beat_docs/build.js @@ -26,35 +26,32 @@ const zlib = require('zlib'); const OUTPUT_DIRECTORY = resolve('scripts', 'beat_docs'); const OUTPUT_SERVER_DIRECTORY = resolve('server', 'utils', 'beat_schema'); +const BEATS_VERSION = '7.12.0'; const beats = [ { - filePath: `${OUTPUT_DIRECTORY}/auditbeat-7.9.0-darwin-x86_64.tar.gz`, + filePath: `${OUTPUT_DIRECTORY}/auditbeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, index: 'auditbeat-*', - outputDir: `${OUTPUT_DIRECTORY}/auditbeat-7.9.0-darwin-x86_64`, - url: - 'https://artifacts.elastic.co/downloads/beats/auditbeat/auditbeat-7.9.0-darwin-x86_64.tar.gz', + outputDir: `${OUTPUT_DIRECTORY}/auditbeat-${BEATS_VERSION}-darwin-x86_64`, + url: `https://artifacts.elastic.co/downloads/beats/auditbeat/auditbeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, }, { - filePath: `${OUTPUT_DIRECTORY}/filebeat-7.9.0-darwin-x86_64.tar.gz`, + filePath: `${OUTPUT_DIRECTORY}/filebeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, index: 'filebeat-*', - outputDir: `${OUTPUT_DIRECTORY}/filebeat-7.9.0-darwin-x86_64`, - url: - 'https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.9.0-darwin-x86_64.tar.gz', + outputDir: `${OUTPUT_DIRECTORY}/filebeat-${BEATS_VERSION}-darwin-x86_64`, + url: `https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, }, { - filePath: `${OUTPUT_DIRECTORY}/packetbeat-7.9.0-darwin-x86_64.tar.gz`, + filePath: `${OUTPUT_DIRECTORY}/packetbeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, index: 'packetbeat-*', - outputDir: `${OUTPUT_DIRECTORY}/packetbeat-7.9.0-darwin-x86_64`, - url: - 'https://artifacts.elastic.co/downloads/beats/packetbeat/packetbeat-7.9.0-darwin-x86_64.tar.gz', + outputDir: `${OUTPUT_DIRECTORY}/packetbeat-${BEATS_VERSION}-darwin-x86_64`, + url: `https://artifacts.elastic.co/downloads/beats/packetbeat/packetbeat-${BEATS_VERSION}-darwin-x86_64.tar.gz`, }, { - filePath: `${OUTPUT_DIRECTORY}/winlogbeat-7.9.0-windows-x86_64.zip`, + filePath: `${OUTPUT_DIRECTORY}/winlogbeat-${BEATS_VERSION}-windows-x86_64.zip`, index: 'winlogbeat-*', outputDir: `${OUTPUT_DIRECTORY}`, - url: - 'https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-7.9.0-windows-x86_64.zip', + url: `https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-${BEATS_VERSION}-windows-x86_64.zip`, }, ]; @@ -141,13 +138,13 @@ const manageZipFields = async (beat, filePath, beatFields) => { await extract(filePath, { dir: beat.outputDir }); console.log('building fields', beat.index); const obj = yaml.load( - fs.readFileSync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64/fields.yml`, { + fs.readFileSync(`${beat.outputDir}/winlogbeat-${BEATS_VERSION}-windows-x86_64/fields.yml`, { encoding: 'utf-8', }) ); const eBeatFields = convertSchemaToHash(obj, beatFields); console.log('deleting files', beat.index); - rimraf.sync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64`); + rimraf.sync(`${beat.outputDir}/winlogbeat-${BEATS_VERSION}-windows-x86_64`); rimraf.sync(beat.filePath); return eBeatFields; @@ -221,7 +218,7 @@ async function main() { * 2.0. */ - import { BeatFields } from '../../../common/search_strategy/security_solution/beat_fields'; + import { BeatFields } from '../../../common/search_strategy/index_fields'; /* eslint-disable @typescript-eslint/naming-convention */ export const fieldsBeat: BeatFields = diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 56ce810a89412..b883b7b3462e7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -68,8 +68,8 @@ export const updateRulesRoute = (router: SecuritySolutionPluginRouter, ml: Setup alertsClient, savedObjectsClient, enabled: request.body.enabled ?? true, - actions: request.body.actions, - throttle: request.body.throttle, + actions: request.body.actions ?? [], + throttle: request.body.throttle ?? 'no_actions', name: request.body.name, }); const ruleStatuses = await ruleStatusClient.find({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json index 376293c399a76..0287565be0d7c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_cloudtrail_logging_created.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudTrail Log Created", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:CreateTrail and event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.action:CreateTrail and event.outcome:success", "references": [ "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_CreateTrail.html", "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/create-trail.html" @@ -51,5 +51,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json new file mode 100644 index 0000000000000..778b7ecc30192 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/collection_microsoft_365_new_inbox_rule.json @@ -0,0 +1,64 @@ +{ + "author": [ + "Elastic", + "Gary Blackwell", + "Austin Songer" + ], + "description": "Identifies when a new Inbox rule is created in Microsoft 365. Inbox rules process messages in the Inbox based on conditions and take actions, such as moving a message to a specified folder or deleting a message. Adequate permissions are required on the mailbox to create an Inbox rule.", + "false_positives": [ + "An inbox rule may be created by a system or network administrator. Verify that the configuration change was expected. Exceptions can be added to this rule to filter expected behavior." + ], + "from": "now-30m", + "index": [ + "filebeat-*", + "logs-o365*" + ], + "language": "kuery", + "license": "Elastic License v2", + "name": "Microsoft 365 New Inbox Rule Created", + "note": "The Microsoft 365 Fleet integration or Filebeat module must be enabled to use this rule.", + "query": "event.dataset:o365.audit and event.provider:Exchange and event.category:web and event.action:\"New-InboxRule\" and event.outcome:success", + "references": [ + "https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/responding-to-a-compromised-email-account?view=o365-worldwide", + "https://docs.microsoft.com/en-us/powershell/module/exchange/new-inboxrule?view=exchange-ps", + "https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/detect-and-remediate-outlook-rules-forms-attack?view=o365-worldwide" + ], + "risk_score": 21, + "rule_id": "ec8efb0c-604d-42fa-ac46-ed1cfbc38f78", + "severity": "low", + "tags": [ + "Elastic", + "Cloud", + "Microsoft 365", + "Continuous Monitoring", + "SecOps", + "Configuration Audit" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0009", + "name": "Collection", + "reference": "https://attack.mitre.org/tactics/TA0009/" + }, + "technique": [ + { + "id": "T1114", + "name": "Email Collection", + "reference": "https://attack.mitre.org/techniques/T1114/", + "subtechnique": [ + { + "id": "T1114.003", + "name": "Email Forwarding Rule", + "reference": "https://attack.mitre.org/techniques/T1114/003/" + } + ] + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "query", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_common_webservices.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_common_webservices.json index e2c521ca8d4dc..0d80e78c556b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_common_webservices.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_common_webservices.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Connection to Commonly Abused Web Services", - "query": "network where network.protocol == \"dns\" and\n /* Add new WebSvc domains here */\n dns.question.name :\n (\n \"*.githubusercontent.*\",\n \"*.pastebin.*\",\n \"*drive.google.*\",\n \"*docs.live.*\",\n \"*api.dropboxapi.*\",\n \"*dropboxusercontent.*\",\n \"*onedrive.*\",\n \"*4shared.*\",\n \"*.file.io\",\n \"*filebin.net\",\n \"*slack-files.com\",\n \"*ghostbin.*\",\n \"*ngrok.*\",\n \"*portmap.*\",\n \"*serveo.net\",\n \"*localtunnel.me\",\n \"*pagekite.me\",\n \"*localxpose.io\",\n \"*notabug.org\"\n ) and\n /* Insert noisy false positives here */\n not process.name :\n (\n \"MicrosoftEdgeCP.exe\",\n \"MicrosoftEdge.exe\",\n \"iexplore.exe\",\n \"chrome.exe\",\n \"msedge.exe\",\n \"opera.exe\",\n \"firefox.exe\",\n \"Dropbox.exe\",\n \"slack.exe\",\n \"svchost.exe\",\n \"thunderbird.exe\",\n \"outlook.exe\",\n \"OneDrive.exe\"\n )\n", + "query": "network where network.protocol == \"dns\" and\n process.name != null and user.id not in (\"S-1-5-18\", \"S-1-5-19\", \"S-1-5-20\") and\n /* Add new WebSvc domains here */\n dns.question.name :\n (\n \"raw.githubusercontent.*\",\n \"*.pastebin.*\",\n \"*drive.google.*\",\n \"*docs.live.*\",\n \"*api.dropboxapi.*\",\n \"*dropboxusercontent.*\",\n \"*onedrive.*\",\n \"*4shared.*\",\n \"*.file.io\",\n \"*filebin.net\",\n \"*slack-files.com\",\n \"*ghostbin.*\",\n \"*ngrok.*\",\n \"*portmap.*\",\n \"*serveo.net\",\n \"*localtunnel.me\",\n \"*pagekite.me\",\n \"*localxpose.io\",\n \"*notabug.org\",\n \"rawcdn.githack.*\",\n \"paste.nrecom.net\",\n \"zerobin.net\",\n \"controlc.com\",\n \"requestbin.net\"\n ) and\n /* Insert noisy false positives here */\n not process.executable :\n (\n \"?:\\\\Program Files\\\\*.exe\",\n \"?:\\\\Program Files (x86)\\\\*.exe\",\n \"?:\\\\Windows\\\\System32\\\\WWAHost.exe\",\n \"?:\\\\Windows\\\\System32\\\\smartscreen.exe\",\n \"?:\\\\Windows\\\\System32\\\\MicrosoftEdgeCP.exe\",\n \"?:\\\\ProgramData\\\\Microsoft\\\\Windows Defender\\\\Platform\\\\*\\\\MsMpEng.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Programs\\\\Fiddler\\\\Fiddler.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Programs\\\\Microsoft VS Code\\\\Code.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Microsoft\\\\OneDrive\\\\OneDrive.exe\",\n \"?:\\\\Windows\\\\system32\\\\mobsync.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\mobsync.exe\"\n )\n", "risk_score": 21, "rule_id": "66883649-f908-4a5b-a1e0-54090a1d3a32", "severity": "low", @@ -42,5 +42,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ftp_file_transfer_protocol_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ftp_file_transfer_protocol_activity_to_the_internet.json deleted file mode 100644 index 526df5012899b..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ftp_file_transfer_protocol_activity_to_the_internet.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects events that may indicate the use of FTP network connections to the Internet. The File Transfer Protocol (FTP) has been around in its current form since the 1980s. It can be a common and efficient procedure on your network to send and receive files. Because of this, adversaries will also often use this protocol to exfiltrate data from your network or download new tools. Additionally, FTP is a plain-text protocol which, if intercepted, may expose usernames and passwords. FTP activity involving servers subject to regulations or compliance standards may be unauthorized.", - "false_positives": [ - "FTP servers should be excluded from this rule as this is expected behavior. Some business workflows may use FTP for data exchange. These workflows often have expected characteristics such as users, sources, and destinations. FTP activity involving an unusual source or destination may be more suspicious. FTP activity involving a production server that has no known associated FTP workflow or business requirement is often suspicious." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "FTP (File Transfer Protocol) Activity to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:(20 or 21) or event.dataset:zeek.ftp) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 21, - "rule_id": "87ec6396-9ac4-4706-bcf0-2ebb22002f43", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0010", - "name": "Exfiltration", - "reference": "https://attack.mitre.org/tactics/TA0010/" - }, - "technique": [ - { - "id": "T1048", - "name": "Exfiltration Over Alternative Protocol", - "reference": "https://attack.mitre.org/techniques/T1048/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_iexplore_via_com.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_iexplore_via_com.json index cc586c80778e3..2cfbbc1c5e101 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_iexplore_via_com.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_iexplore_via_com.json @@ -3,6 +3,9 @@ "Elastic" ], "description": "Identifies instances of Internet Explorer (iexplore.exe) being started via the Component Object Model (COM) making unusual network connections. Adversaries could abuse Internet Explorer via COM to avoid suspicious processes making network connections and bypass host-based firewall restrictions.", + "false_positives": [ + "Processes such as MS Office using IEproxy to render HTML content." + ], "from": "now-9m", "index": [ "winlogbeat-*", @@ -12,7 +15,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Potential Command and Control via Internet Explorer", - "query": "sequence by host.id, process.entity_id with maxspan = 1s\n [process where event.type == \"start\" and process.parent.name : \"iexplore.exe\" and process.parent.args : \"-Embedding\"]\n /* IE started via COM in normal conditions makes few connections, mainly to Microsoft and OCSP related domains, add FPs here */\n [network where network.protocol == \"dns\" and process.name : \"iexplore.exe\" and\n not dns.question.name :\n (\n \"*.microsoft.com\",\n \"*.digicert.com\",\n \"*.msocsp.com\",\n \"*.windowsupdate.com\",\n \"*.bing.com\",\n \"*.identrust.com\"\n )\n ]\n", + "query": "sequence by host.id, user.id with maxspan = 5s\n [library where dll.name : \"IEProxy.dll\" and process.name : (\"rundll32.exe\", \"regsvr32.exe\")]\n [process where event.type == \"start\" and process.parent.name : \"iexplore.exe\" and process.parent.args : \"-Embedding\"]\n /* IE started via COM in normal conditions makes few connections, mainly to Microsoft and OCSP related domains, add FPs here */\n [network where network.protocol == \"dns\" and process.name : \"iexplore.exe\" and\n not dns.question.name :\n (\n \"*.microsoft.com\",\n \"*.digicert.com\",\n \"*.msocsp.com\",\n \"*.windowsupdate.com\",\n \"*.bing.com\",\n \"*.identrust.com\",\n \"*.sharepoint.com\",\n \"*.office365.com\",\n \"*.office.com\"\n )\n ]\n", "risk_score": 47, "rule_id": "acd611f3-2b93-47b3-a0a3-7723bcc46f6d", "severity": "medium", @@ -41,5 +44,5 @@ } ], "type": "eql", - "version": 2 + "version": 3 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_irc_internet_relay_chat_protocol_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_irc_internet_relay_chat_protocol_activity_to_the_internet.json deleted file mode 100644 index be1dcc5dfd1ba..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_irc_internet_relay_chat_protocol_activity_to_the_internet.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects events that use common ports for Internet Relay Chat (IRC) to the Internet. IRC is a common protocol that can be used for chat and file transfers. This protocol is also a good candidate for remote control of malware and data transfers to and from a network.", - "false_positives": [ - "IRC activity may be normal behavior for developers and engineers but is unusual for non-engineering end users. IRC activity involving an unusual source or destination may be more suspicious. IRC activity involving a production server is often suspicious. Because these ports are in the ephemeral range, this rule may false under certain conditions, such as when a NAT-ed web server replies to a client which has used a port in the range by coincidence. In this case, these servers can be excluded. Some legacy applications may use these ports, but this is very uncommon and usually only appears in local traffic using private IPs, which does not match this rule's conditions." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "IRC (Internet Relay Chat) Protocol Activity to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:(6667 or 6697) or event.dataset:zeek.irc) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 47, - "rule_id": "c6474c34-4953-447a-903e-9fcb7b6661aa", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0010", - "name": "Exfiltration", - "reference": "https://attack.mitre.org/tactics/TA0010/" - }, - "technique": [ - { - "id": "T1048", - "name": "Exfiltration Over Alternative Protocol", - "reference": "https://attack.mitre.org/techniques/T1048/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_port_8000_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_port_8000_activity_to_the_internet.json deleted file mode 100644 index 8ba395fea25eb..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_port_8000_activity_to_the_internet.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "TCP Port 8000 is commonly used for development environments of web server software. It generally should not be exposed directly to the Internet. If you are running software like this on the Internet, you should consider placing it behind a reverse proxy.", - "false_positives": [ - "Because this port is in the ephemeral range, this rule may false under certain conditions, such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded. Some applications may use this port but this is very uncommon and usually appears in local traffic using private IPs, which this rule does not match. Some cloud environments, particularly development environments, may use this port when VPNs or direct connects are not in use and cloud instances are accessed across the Internet." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "TCP Port 8000 Activity to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and destination.port:8000 and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 21, - "rule_id": "08d5d7e2-740f-44d8-aeda-e41f4263efaf", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_pptp_point_to_point_tunneling_protocol_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_pptp_point_to_point_tunneling_protocol_activity.json deleted file mode 100644 index 8180275e4c456..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_pptp_point_to_point_tunneling_protocol_activity.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule 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.", - "false_positives": [ - "Some networks may utilize PPTP protocols but this is uncommon as more modern VPN technologies are available. Usage that is unfamiliar to local network administrators can be unexpected and suspicious. Torrenting applications may use this port. Because this port is in the ephemeral range, this rule may false under certain conditions, such as when an application server replies to a client that used this port by coincidence. This is uncommon but such servers can be excluded." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "PPTP (Point to Point Tunneling Protocol) Activity", - "query": "event.category:(network or network_traffic) and network.transport:tcp and destination.port:1723", - "risk_score": 21, - "rule_id": "d2053495-8fe7-4168-b3df-dad844046be3", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_proxy_port_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_proxy_port_activity_to_the_internet.json deleted file mode 100644 index b970c00fb7c4a..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_proxy_port_activity_to_the_internet.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects events that may describe network events of proxy use to the Internet. It includes popular HTTP proxy ports and SOCKS proxy ports. Typically, environments will use an internal IP address for a proxy server. It can also be used to circumvent network controls and detection mechanisms.", - "false_positives": [ - "Some proxied applications may use these ports but this usually occurs in local traffic using private IPs which this rule does not match. Proxies are widely used as a security technology but in enterprise environments this is usually local traffic which this rule does not match. If desired, internet proxy services using these ports can be added to allowlists. Some screen recording applications may use these ports. Proxy port activity involving an unusual source or destination may be more suspicious. Some cloud environments may use this port when VPNs or direct connects are not in use and cloud instances are accessed across the Internet. Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Proxy Port Activity to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:(1080 or 3128 or 8080) or event.dataset:zeek.socks) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 47, - "rule_id": "ad0e5e75-dd89-4875-8d0a-dfdc1828b5f3", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_desktopimgdownldr.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_desktopimgdownldr.json index 4c982df62ed8c..3adeb4f71808a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_desktopimgdownldr.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_desktopimgdownldr.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Remote File Download via Desktopimgdownldr Utility", - "query": "event.category:process and event.type:(start or process_started) and (process.name:desktopimgdownldr.exe or process.pe.original_file_name:desktopimgdownldr.exe) and process.args:/lockscreenurl\\:http*", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"desktopimgdownldr.exe\" or process.pe.original_file_name == \"desktopimgdownldr.exe\") and\n process.args : \"/lockscreenurl:http*\"\n", "references": [ "https://labs.sentinelone.com/living-off-windows-land-a-new-native-file-downldr/" ], @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_mpcmdrun.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_mpcmdrun.json index c88c0c1f48da1..bbda3c0a4f49b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_mpcmdrun.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_remote_file_copy_mpcmdrun.json @@ -9,11 +9,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Remote File Download via MpCmdRun", "note": "### Investigating Remote File Download via MpCmdRun\nVerify details such as the parent process, URL reputation, and downloaded file details. Additionally, `MpCmdRun` logs this information in the Appdata Temp folder in `MpCmdRun.log`.", - "query": "event.category:process and event.type:(start or process_started) and (process.name:MpCmdRun.exe or process.pe.original_file_name:MpCmdRun.exe) and process.args:((\"-DownloadFile\" or \"-downloadfile\") and \"-url\" and \"-path\")", + "query": "process where event.type == \"start\" and\n (process.name : \"MpCmdRun.exe\" or process.pe.original_file_name == \"MpCmdRun.exe\") and\n process.args : \"-DownloadFile\" and process.args : \"-url\" and process.args : \"-path\"\n", "references": [ "https://twitter.com/mohammadaskar2/status/1301263551638761477", "https://www.bleepingcomputer.com/news/microsoft/microsoft-defender-can-ironically-be-used-to-download-malware/" @@ -46,6 +46,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_smtp_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_smtp_to_the_internet.json deleted file mode 100644 index 528485a984252..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_smtp_to_the_internet.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects events that may describe SMTP traffic from internal hosts to a host across the Internet. In an enterprise network, there is typically a dedicated internal host that performs this function. It is also frequently abused by threat actors for command and control, or data exfiltration.", - "false_positives": [ - "NATed servers that process email traffic may false and should be excluded from this rule as this is expected behavior for them. Consumer and personal devices may send email traffic to remote Internet destinations. In this case, such devices or networks can be excluded from this rule if this is expected behavior." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "SMTP to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:(25 or 465 or 587) or event.dataset:zeek.smtp) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 21, - "rule_id": "67a9beba-830d-4035-bfe8-40b7e28f8ac4", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0010", - "name": "Exfiltration", - "reference": "https://attack.mitre.org/tactics/TA0010/" - }, - "technique": [ - { - "id": "T1048", - "name": "Exfiltration Over Alternative Protocol", - "reference": "https://attack.mitre.org/techniques/T1048/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sql_server_port_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sql_server_port_activity_to_the_internet.json deleted file mode 100644 index e3e237107257c..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sql_server_port_activity_to_the_internet.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects events that may describe database traffic (MS SQL, Oracle, MySQL, and Postgresql) across the Internet. Databases should almost never be directly exposed to the Internet, as they are frequently targeted by threat actors to gain initial access to network resources.", - "false_positives": [ - "Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used a port in the range by coincidence. In this case, such servers can be excluded if desired. Some cloud environments may use this port when VPNs or direct connects are not in use and database instances are accessed directly across the Internet." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "SQL Traffic to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:(1433 or 1521 or 3306 or 5432) or event.dataset:zeek.mysql) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 47, - "rule_id": "139c7458-566a-410c-a5cd-f80238d6a5cd", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_from_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_from_the_internet.json deleted file mode 100644 index 74a1bda6c7077..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_from_the_internet.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects network events that may indicate the use of SSH traffic from the Internet. SSH is commonly used by system administrators to remotely control a system using the command line shell. If it is exposed to the Internet, it should be done with strong security controls as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", - "false_positives": [ - "Some network security policies allow SSH directly from the Internet but usage that is unfamiliar to server or network owners can be unexpected and suspicious. SSH services may be exposed directly to the Internet in some networks such as cloud environments. In such cases, only SSH gateways, bastions or jump servers may be expected expose SSH directly to the Internet and can be exempted from this rule. SSH may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "SSH (Secure Shell) from the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:22 or event.dataset:zeek.ssh) and not source.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" ) and destination.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 )", - "risk_score": 47, - "rule_id": "ea0784f0-a4d7-4fea-ae86-4baaf27a6f17", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0008", - "name": "Lateral Movement", - "reference": "https://attack.mitre.org/tactics/TA0008/" - }, - "technique": [ - { - "id": "T1021", - "name": "Remote Services", - "reference": "https://attack.mitre.org/techniques/T1021/" - } - ] - }, - { - "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/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_to_the_internet.json deleted file mode 100644 index 9c13293e5719f..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_ssh_secure_shell_to_the_internet.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects network events that may indicate the use of SSH traffic from the Internet. SSH is commonly used by system administrators to remotely control a system using the command line shell. If it is exposed to the Internet, it should be done with strong security controls as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", - "false_positives": [ - "SSH connections may be made directly to Internet destinations in order to access Linux cloud server instances but such connections are usually made only by engineers. In such cases, only SSH gateways, bastions or jump servers may be expected Internet destinations and can be exempted from this rule. SSH may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "SSH (Secure Shell) to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:22 or event.dataset:zeek.ssh) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 21, - "rule_id": "6f1500bc-62d7-4eb9-8601-7485e87da2f4", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sunburst_c2_activity_detected.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sunburst_c2_activity_detected.json index 6c4437b11b88e..1aede9216de12 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sunburst_c2_activity_detected.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_sunburst_c2_activity_detected.json @@ -7,11 +7,11 @@ "index": [ "logs-endpoint.events.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "SUNBURST Command and Control Activity", "note": "The SUNBURST malware attempts to hide within the Orion Improvement Program (OIP) network traffic. As this rule detects post-exploitation network traffic, investigations into this should be prioritized.", - "query": "event.category:network and event.type:protocol and network.protocol:http and process.name:( ConfigurationWizard.exe or NetFlowService.exe or NetflowDatabaseMaintenance.exe or SolarWinds.Administration.exe or SolarWinds.BusinessLayerHost.exe or SolarWinds.BusinessLayerHostx64.exe or SolarWinds.Collector.Service.exe or SolarwindsDiagnostics.exe) and http.request.body.content:(( (*/swip/Upload.ashx* and (POST* or PUT*)) or (*/swip/SystemDescription* and (GET* or HEAD*)) or (*/swip/Events* and (GET* or HEAD*))) and not *solarwinds.com*)", + "query": "network where event.type == \"protocol\" and network.protocol == \"http\" and\n process.name : (\"ConfigurationWizard.exe\",\n \"NetFlowService.exe\",\n \"NetflowDatabaseMaintenance.exe\",\n \"SolarWinds.Administration.exe\",\n \"SolarWinds.BusinessLayerHost.exe\",\n \"SolarWinds.BusinessLayerHostx64.exe\",\n \"SolarWinds.Collector.Service.exe\",\n \"SolarwindsDiagnostics.exe\") and\n (http.request.body.content : \"*/swip/Upload.ashx*\" and http.request.body.content : (\"POST*\", \"PUT*\")) or\n (http.request.body.content : (\"*/swip/SystemDescription*\", \"*/swip/Events*\") and http.request.body.content : (\"GET*\", \"HEAD*\")) and\n not http.request.body.content : \"*solarwinds.com*\"\n", "references": [ "https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html" ], @@ -72,6 +72,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_teamviewer_remote_file_copy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_teamviewer_remote_file_copy.json index b8fdc02d24628..08d4df2556f6a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_teamviewer_remote_file_copy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_teamviewer_remote_file_copy.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Remote File Copy via TeamViewer", - "query": "event.category:file and event.type:creation and process.name:TeamViewer.exe and file.extension:(exe or dll or scr or com or bat or ps1 or vbs or vbe or js or wsh or hta)", + "query": "file where event.type == \"creation\" and process.name : \"TeamViewer.exe\" and\n file.extension : (\"exe\", \"dll\", \"scr\", \"com\", \"bat\", \"ps1\", \"vbs\", \"vbe\", \"js\", \"wsh\", \"hta\")\n", "references": [ "https://blog.menasec.net/2019/11/hunting-for-suspicious-use-of.html" ], @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tor_activity_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tor_activity_to_the_internet.json deleted file mode 100644 index 9f06808a3d9ba..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tor_activity_to_the_internet.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects network events that may indicate the use of Tor traffic to the Internet. Tor is a network protocol that sends traffic through a series of encrypted tunnels used to conceal a user's location and usage. Tor may be used by threat actors as an alternate communication pathway to conceal the actor's identity and avoid detection.", - "false_positives": [ - "Tor client activity is uncommon in managed enterprise networks but may be common in unmanaged or public networks where few security policies apply. Because these ports are in the ephemeral range, this rule may false under certain conditions such as when a NATed web server replies to a client which has used one of these ports by coincidence. In this case, such servers can be excluded if desired." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Tor Activity to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and destination.port:(9001 or 9030) and source.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16) and not destination.ip:(10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\")", - "risk_score": 47, - "rule_id": "7d2c38d7-ede7-4bdf-b140-445906e6c540", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Command and Control" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0011", - "name": "Command and Control", - "reference": "https://attack.mitre.org/tactics/TA0011/" - }, - "technique": [ - { - "id": "T1090", - "name": "Proxy", - "reference": "https://attack.mitre.org/techniques/T1090/", - "subtechnique": [ - { - "id": "T1090.003", - "name": "Multi-hop Proxy", - "reference": "https://attack.mitre.org/techniques/T1090/003/" - } - ] - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tunneling_via_earthworm.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tunneling_via_earthworm.json new file mode 100644 index 0000000000000..eafe28709a0d2 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/command_and_control_tunneling_via_earthworm.json @@ -0,0 +1,49 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies the execution of the EarthWorm tunneler. Adversaries may tunnel network communications to and from a victim system within a separate protocol to avoid detection and network filtering, or to enable access to otherwise unreachable systems.", + "from": "now-9m", + "index": [ + "auditbeat-*", + "logs-endpoint.events.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Potential Protocol Tunneling via EarthWorm", + "query": "process where event.type == \"start\" and\n process.args : \"-s\" and process.args : \"-d\" and process.args : \"rssocks\"\n", + "references": [ + "http://rootkiter.com/EarthWorm/", + "https://decoded.avast.io/luigicamastra/apt-group-targeting-governmental-agencies-in-east-asia/" + ], + "risk_score": 47, + "rule_id": "9f1c4ca3-44b5-481d-ba42-32dc215a2769", + "severity": "medium", + "tags": [ + "Elastic", + "Host", + "Linux", + "Threat Detection", + "Command and Control" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/" + }, + "technique": [ + { + "id": "T1572", + "name": "Protocol Tunneling", + "reference": "https://attack.mitre.org/techniques/T1572/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credential_dumping_msbuild.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credential_dumping_msbuild.json index 10d988075226c..da4689ed12d70 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credential_dumping_msbuild.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credential_dumping_msbuild.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Loading Windows Credential Libraries", - "query": "event.category:process and event.type:change and (process.pe.original_file_name:(vaultcli.dll or SAMLib.DLL) or dll.name:(vaultcli.dll or SAMLib.DLL)) and process.name: MSBuild.exe", + "query": "sequence by process.entity_id\n [process where event.type == \"start\" and (process.name : \"MSBuild.exe\" or process.pe.original_file_name == \"MSBuild.exe\")]\n [library where dll.name : (\"vaultcli.dll\", \"SAMLib.DLL\")]\n", "risk_score": 73, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae5", "severity": "high", @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credentials_keychains.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credentials_keychains.json index 8d4c6f7e0f605..9c8f2943c44d8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credentials_keychains.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_credentials_keychains.json @@ -11,7 +11,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Access to Keychain Credentials Directories", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.args :\n (\n \"/Users/*/Library/Keychains/*\",\n \"/Library/Keychains/*\",\n \"/Network/Library/Keychains/*\",\n \"System.keychain\",\n \"login.keychain-db\",\n \"login.keychain\"\n )\n", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.args :\n (\n \"/Users/*/Library/Keychains/*\",\n \"/Library/Keychains/*\",\n \"/Network/Library/Keychains/*\",\n \"System.keychain\",\n \"login.keychain-db\",\n \"login.keychain\"\n ) and\n not process.args : (\"find-certificate\",\n \"add-trusted-cert\",\n \"set-keychain-settings\",\n \"delete-certificate\",\n \"/Users/*/Library/Keychains/openvpn.keychain-db\",\n \"show-keychain-info\",\n \"lock-keychain\",\n \"set-key-partition-list\",\n \"import\",\n \"find-identity\") and\n not process.parent.executable : \"/Applications/OpenVPN Connect/OpenVPN Connect.app/Contents/MacOS/OpenVPN Connect\"\n", "references": [ "https://objective-see.com/blog/blog_0x25.html", "https://securelist.com/calisto-trojan-for-macos/86543/" @@ -52,5 +52,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_domain_backup_dpapi_private_keys.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_domain_backup_dpapi_private_keys.json index 159b51a2a9e0a..361b5f9e3e62e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_domain_backup_dpapi_private_keys.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_domain_backup_dpapi_private_keys.json @@ -9,11 +9,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Creation or Modification of Domain Backup DPAPI private key", "note": "### Domain DPAPI Backup keys are stored on domain controllers and can be dumped remotely with tools such as Mimikatz. The resulting .pvk private key can be used to decrypt ANY domain user masterkeys, which then can be used to decrypt any secrets protected by those keys.", - "query": "event.category:file and not event.type:deletion and file.name:(ntds_capi_*.pfx or ntds_capi_*.pvk)", + "query": "file where event.type != \"deletion\" and file.name : (\"ntds_capi_*.pfx\", \"ntds_capi_*.pvk\")\n", "references": [ "https://www.dsinternals.com/en/retrieving-dpapi-backup-keys-from-active-directory/", "https://www.harmj0y.net/blog/redteaming/operational-guidance-for-offensive-user-dpapi-abuse/" @@ -53,6 +53,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json index 122e15352f973..20674ebe4490e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_iam_user_addition_to_group.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS IAM User Addition to Group", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:AddUserToGroup and event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.action:AddUserToGroup and event.outcome:success", "references": [ "https://docs.aws.amazon.com/IAM/latest/APIReference/API_AddUserToGroup.html" ], @@ -59,5 +59,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_keychain_pwd_retrieval_security_cmd.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_keychain_pwd_retrieval_security_cmd.json index d3d3276936825..cacf0e1f3676c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_keychain_pwd_retrieval_security_cmd.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_keychain_pwd_retrieval_security_cmd.json @@ -4,17 +4,17 @@ ], "description": "Adversaries may collect keychain storage data from a system to in order to acquire credentials. Keychains are the built-in way for macOS to keep track of users' passwords and credentials for many services and features, including Wi-Fi and website passwords, secure notes, certificates, and Kerberos.", "false_positives": [ - "Trusted parent processes accessing their respective application passwords." + "Applications for password management." ], "from": "now-9m", "index": [ "auditbeat-*", "logs-endpoint.events.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Keychain Password Retrieval via Command Line", - "query": "event.category:process and event.type:(start or process_started) and process.name:security and process.args:(\"find-generic-password\" or \"find-internet-password\")", + "query": "process where event.type == \"start\" and\n process.name : \"security\" and process.args : \"-wa\" and process.args : (\"find-generic-password\", \"find-internet-password\") and\n process.args : (\"Chrome*\", \"Chromium\", \"Opera\", \"Safari*\", \"Brave\", \"Microsoft Edge\", \"Edge\", \"Firefox*\") and\n not process.parent.executable : \"/Applications/Keeper Password Manager.app/Contents/Frameworks/Keeper Password Manager Helper*/Contents/MacOS/Keeper Password Manager Helper*\"\n", "references": [ "https://www.netmeister.org/blog/keychain-passwords.html", "https://github.com/priyankchheda/chrome_password_grabber/blob/master/chrome.py", @@ -51,11 +51,23 @@ "reference": "https://attack.mitre.org/techniques/T1555/001/" } ] + }, + { + "id": "T1555", + "name": "Credentials from Password Stores", + "reference": "https://attack.mitre.org/techniques/T1555/", + "subtechnique": [ + { + "id": "T1555.003", + "name": "Credentials from Web Browsers", + "reference": "https://attack.mitre.org/techniques/T1555/003/" + } + ] } ] } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 1 + "type": "eql", + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_lsass_memdump_file_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_lsass_memdump_file_created.json index 220c6e3befbf5..36b614c628b19 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_lsass_memdump_file_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_lsass_memdump_file_created.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "LSASS Memory Dump Creation", - "query": "event.category:file and file.name:(lsass.DMP or lsass*.dmp or dumpert.dmp or Andrew.dmp or SQLDmpr*.mdmp or Coredump.dmp)", + "query": "file where file.name : (\"lsass*.dmp\", \"dumpert.dmp\", \"Andrew.dmp\", \"SQLDmpr*.mdmp\", \"Coredump.dmp\")\n", "references": [ "https://github.com/outflanknl/Dumpert", "https://github.com/hoangprod/AndrewSpecial" @@ -45,6 +45,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mimikatz_memssp_default_logs.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mimikatz_memssp_default_logs.json index 0193a4e59dd0d..9f9bd297e17c1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mimikatz_memssp_default_logs.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mimikatz_memssp_default_logs.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Mimikatz Memssp Log File Detected", - "query": "event.category:file and file.name:mimilsa.log and process.name:lsass.exe", + "query": "file where file.name : \"mimilsa.log\" and process.name : \"lsass.exe\"\n", "risk_score": 73, "rule_id": "ebb200e8-adf0-43f8-a0bb-4ee5b5d852c6", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mitm_localhost_webproxy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mitm_localhost_webproxy.json index 50429c3b0f169..e226df7a23da9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mitm_localhost_webproxy.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_mitm_localhost_webproxy.json @@ -14,7 +14,7 @@ "language": "kuery", "license": "Elastic License v2", "name": "WebProxy Settings Modification", - "query": "event.category:process and event.type:start and process.name:networksetup and process.args:(\"-setwebproxy\" or \"-setsecurewebproxy\" or \"-setautoproxyurl\")", + "query": "event.category : process and event.type : start and process.name : networksetup and process.args : ((\"-setwebproxy\" or \"-setsecurewebproxy\" or \"-setautoproxyurl\") and not (Bluetooth or off)) and not process.parent.executable : (\"/Library/PrivilegedHelperTools/com.80pct.FreedomHelper\" or \"/Applications/Fiddler Everywhere.app/Contents/Resources/app/out/WebServer/Fiddler.WebUi\" or \"/usr/libexec/xpcproxy\")", "references": [ "https://unit42.paloaltonetworks.com/mac-malware-steals-cryptocurrency-exchanges-cookies/", "https://objectivebythesea.com/v2/talks/OBTS_v2_Zohar.pdf" @@ -48,5 +48,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_persistence_network_logon_provider_modification.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_persistence_network_logon_provider_modification.json new file mode 100644 index 0000000000000..166ddf7c5592d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_persistence_network_logon_provider_modification.json @@ -0,0 +1,69 @@ +{ + "author": [ + "Elastic" + ], + "description": "Adversaries may register a rogue network logon provider module for persistence and/or credential access via intercepting the authentication credentials in clear text during user logon.", + "false_positives": [ + "Authorized third party network logon providers." + ], + "from": "now-9m", + "index": [ + "auditbeat-*", + "logs-endpoint.events.*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Network Logon Provider Registry Modification", + "query": "registry where registry.data.strings != null and\n registry.path : \"HKLM\\\\SYSTEM\\\\*ControlSet*\\\\Services\\\\*\\\\NetworkProvider\\\\ProviderPath\" and\n /* Excluding default NetworkProviders RDPNP, LanmanWorkstation and webclient. */\n not ( user.id : \"S-1-5-18\" and\n registry.data.strings in\n (\"%SystemRoot%\\\\System32\\\\ntlanman.dll\",\n \"%SystemRoot%\\\\System32\\\\drprov.dll\",\n \"%SystemRoot%\\\\System32\\\\davclnt.dll\")\n )\n", + "references": [ + "https://github.com/gtworek/PSBits/tree/master/PasswordStealing/NPPSpy", + "https://docs.microsoft.com/en-us/windows/win32/api/npapi/nf-npapi-nplogonnotify" + ], + "risk_score": 47, + "rule_id": "54c3d186-0461-4dc3-9b33-2dc5c7473936", + "severity": "medium", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Persistence", + "Credential Access" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0006", + "name": "Credential Access", + "reference": "https://attack.mitre.org/tactics/TA0006/" + }, + "technique": [ + { + "id": "T1556", + "name": "Modify Authentication Process", + "reference": "https://attack.mitre.org/techniques/T1556/" + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0003", + "name": "Persistence", + "reference": "https://attack.mitre.org/tactics/TA0003/" + }, + "technique": [ + { + "id": "T1543", + "name": "Create or Modify System Process", + "reference": "https://attack.mitre.org/techniques/T1543/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_tcpdump_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_tcpdump_activity.json deleted file mode 100644 index 4721a43d116b4..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/credential_access_tcpdump_activity.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "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." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Network Sniffing via Tcpdump", - "query": "event.category:process and event.type:(start or process_started) and process.name:tcpdump", - "risk_score": 21, - "rule_id": "7a137d76-ce3d-48e2-947d-2747796a78c0", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection", - "Credential Access" - ], - "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/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json index 2c505fa829b18..66d900975ff39 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Adding Hidden File Attribute via Attrib", - "query": "event.category:process and event.type:(start or process_started) and process.name:attrib.exe and process.args:+h", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"attrib.exe\" and process.args : \"+h\"\n", "risk_score": 21, "rule_id": "4630d948-40d4-4cef-ac69-4002e29bc3db", "severity": "low", @@ -57,6 +57,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_base64_encoding_or_decoding_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_base64_encoding_or_decoding_activity.json deleted file mode 100644 index 53610d0c4c89e..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_base64_encoding_or_decoding_activity.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Adversaries may encode/decode data in an attempt to evade detection by host- or network-based security controls.", - "false_positives": [ - "Automated tools such as Jenkins may encode or decode files as part of their normal behavior. These events can be filtered by the process executable or username values." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Base64 Encoding/Decoding Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:(base64 or base64plain or base64url or base64mime or base64pem)", - "risk_score": 21, - "rule_id": "97f22dab-84e8-409d-955e-dacd1d31670b", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection", - "Defense Evasion" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0005", - "name": "Defense Evasion", - "reference": "https://attack.mitre.org/tactics/TA0005/" - }, - "technique": [ - { - "id": "T1140", - "name": "Deobfuscate/Decode Files or Information", - "reference": "https://attack.mitre.org/techniques/T1140/" - }, - { - "id": "T1027", - "name": "Obfuscated Files or Information", - "reference": "https://attack.mitre.org/techniques/T1027/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_clearing_windows_event_logs.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_clearing_windows_event_logs.json index f187be2225f6d..79e059d68a52a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_clearing_windows_event_logs.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_clearing_windows_event_logs.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Clearing Windows Event Logs", - "query": "event.category:process and event.type:(process_started or start) and (process.name:\"wevtutil.exe\" or process.pe.original_file_name:\"wevtutil.exe\") and process.args:(\"/e:false\" or cl or \"clear-log\") or process.name:\"powershell.exe\" and process.args:\"Clear-EventLog\"", + "query": "process where event.type in (\"process_started\", \"start\") and\n (process.name : \"wevtutil.exe\" or process.pe.original_file_name == \"wevtutil.exe\") and\n process.args : (\"/e:false\", \"cl\", \"clear-log\") or\n process.name : \"powershell.exe\" and process.args : \"Clear-EventLog\"\n", "risk_score": 21, "rule_id": "d331bbe2-6db4-4941-80a5-8270db72eb61", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json index 305950feae54e..3ae05408ac81c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_deleted.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudTrail Log Deleted", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteTrail and event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.action:DeleteTrail and event.outcome:success", "references": [ "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_DeleteTrail.html", "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/delete-trail.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json index fec0423debe7d..75e3e4f542c7d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudtrail_logging_suspended.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudTrail Log Suspended", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:StopLogging and event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.action:StopLogging and event.outcome:success", "references": [ "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_StopLogging.html", "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/stop-logging.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json index 2d55f0b2e54fb..d05b043923482 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_cloudwatch_alarm_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudWatch Alarm Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteAlarms and event.dataset:aws.cloudtrail and event.provider:monitoring.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:monitoring.amazonaws.com and event.action:DeleteAlarms and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudwatch/delete-alarms.html", "https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_DeleteAlarms.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_code_injection_conhost.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_code_injection_conhost.json index 7b675a04573f8..ba4fe13622733 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_code_injection_conhost.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_code_injection_conhost.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious Process from Conhost", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:conhost.exe and not process.executable:(\"C:\\Windows\\splwow64.exe\" or \"C:\\Windows\\System32\\WerFault.exe\" or \"C:\\\\Windows\\System32\\conhost.exe\")", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"conhost.exe\" and\n not process.executable : (\"?:\\\\Windows\\\\splwow64.exe\", \"?:\\\\Windows\\\\System32\\\\WerFault.exe\", \"?:\\\\Windows\\\\System32\\\\conhost.exe\")\n", "references": [ "https://modexp.wordpress.com/2018/09/12/process-injection-user-data/", "https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES/blob/master/Defense%20Evasion/evasion_codeinj_odzhan_conhost_sysmon_10_1.evtx" @@ -45,6 +45,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json index 409d318d5c98a..77d8deff1c191 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_config_service_rule_deletion.json @@ -1,10 +1,11 @@ { "author": [ - "Elastic" + "Elastic", + "Austin Songer" ], - "description": "Identifies attempts to delete an AWS Config Service rule. An adversary may tamper with Config rules in order to reduce visibiltiy into the security posture of an account and / or its workload instances.", + "description": "Identifies attempts to delete an AWS Config Service resource. An adversary may tamper with Config services in order to reduce visibility into the security posture of an account and / or its workload instances.", "false_positives": [ - "Privileged IAM users with security responsibilities may be expected to make changes to the Config rules in order to align with local security policies and requirements. Automation, orchestration, and security tools may also make changes to the Config service, where they are used to automate setup or configuration of AWS accounts. Other kinds of user or service contexts do not commonly make changes to this service." + "Privileged IAM users with security responsibilities may be expected to make changes to the Config service in order to align with local security policies and requirements. Automation, orchestration, and security tools may also make changes to the Config service, where they are used to automate setup or configuration of AWS accounts. Other kinds of user or service contexts do not commonly make changes to this service." ], "from": "now-60m", "index": [ @@ -16,7 +17,7 @@ "license": "Elastic License v2", "name": "AWS Config Service Tampering", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.dataset: aws.cloudtrail and event.action: DeleteConfigRule and event.provider: config.amazonaws.com", + "query": "event.dataset:aws.cloudtrail and event.provider:config.amazonaws.com and event.action:(DeleteConfigRule or DeleteOrganizationConfigRule or DeleteConfigurationAggregator or DeleteConfigurationRecorder or DeleteConformancePack or DeleteOrganizationConformancePack or DeleteDeliveryChannel or DeleteRemediationConfiguration or DeleteRetentionConfiguration)", "references": [ "https://docs.aws.amazon.com/config/latest/developerguide/how-does-config-work.html", "https://docs.aws.amazon.com/config/latest/APIReference/API_Operations.html" @@ -58,5 +59,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json index 8ff04355d7294..7de02c3a3875d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_configuration_recorder_stopped.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS Configuration Recorder Stopped", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:StopConfigurationRecorder and event.dataset:aws.cloudtrail and event.provider:config.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:config.amazonaws.com and event.action:StopConfigurationRecorder and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configservice/stop-configuration-recorder.html", "https://docs.aws.amazon.com/config/latest/APIReference/API_StopConfigurationRecorder.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_delete_volume_usn_journal_with_fsutil.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_delete_volume_usn_journal_with_fsutil.json index df1609ea56c5e..caa6209fca745 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_delete_volume_usn_journal_with_fsutil.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_delete_volume_usn_journal_with_fsutil.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Delete Volume USN Journal with Fsutil", - "query": "event.category:process and event.type:(start or process_started) and process.name:fsutil.exe and process.args:(deletejournal and usn)", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"fsutil.exe\" or process.pe.original_file_name == \"fsutil.exe\") and \n process.args : \"deletejournal\" and process.args : \"usn\"\n", "risk_score": 21, "rule_id": "f675872f-6d85-40a3-b502-c0d2ef101e92", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_deleting_backup_catalogs_with_wbadmin.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_deleting_backup_catalogs_with_wbadmin.json index d54fbc08a21b8..5d1233ebfcb78 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_deleting_backup_catalogs_with_wbadmin.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_deleting_backup_catalogs_with_wbadmin.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Deleting Backup Catalogs with Wbadmin", - "query": "event.category:process and event.type:(start or process_started) and process.name:wbadmin.exe and process.args:(catalog and delete)", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"wbadmin.exe\" or process.pe.original_file_name == \"WBADMIN.EXE\") and\n process.args : \"catalog\" and process.args : \"delete\"\n", "risk_score": 21, "rule_id": "581add16-df76-42bb-af8e-c979bfb39a59", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_disable_windows_firewall_rules_with_netsh.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_disable_windows_firewall_rules_with_netsh.json index 7180871001ff6..00f18df34f864 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_disable_windows_firewall_rules_with_netsh.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_disable_windows_firewall_rules_with_netsh.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Disable Windows Firewall Rules via Netsh", - "query": "event.category:process and event.type:(start or process_started) and process.name:netsh.exe and process.args:(disable and firewall and set) or process.args:(advfirewall and off and state)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"netsh.exe\" and\n (process.args : \"disable\" and process.args : \"firewall\" and process.args : \"set\") or\n (process.args : \"advfirewall\" and process.args : \"off\" and process.args : \"state\")\n", "risk_score": 47, "rule_id": "4b438734-3793-4fda-bd42-ceeada0be8f9", "severity": "medium", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json index df36941662bf0..9a3eace06d5fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_flow_log_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS EC2 Flow Log Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteFlowLogs and event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.action:DeleteFlowLogs and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-flow-logs.html", "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteFlowLogs.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json index 656267753827b..79222e3ef4cfb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_ec2_network_acl_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS EC2 Network Access Control List Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(DeleteNetworkAcl or DeleteNetworkAclEntry) and event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.action:(DeleteNetworkAcl or DeleteNetworkAclEntry) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-network-acl.html", "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkAcl.html", @@ -60,5 +60,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_encoding_or_decoding_files_via_certutil.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_encoding_or_decoding_files_via_certutil.json index 65a59737ce4b9..9a4a8efd26f5b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_encoding_or_decoding_files_via_certutil.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_encoding_or_decoding_files_via_certutil.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Encoding or Decoding Files via CertUtil", - "query": "event.category:process and event.type:(start or process_started) and process.name:certutil.exe and process.args:(-decode or -encode or /decode or /encode)", + "query": "process where event.type == \"start\" and\n (process.name : \"certutil.exe\" or process.pe.original_file_name == \"CertUtil.exe\") and \n process.args : (\"?decode\", \"?encode\")\n", "risk_score": 47, "rule_id": "fd70c98a-c410-42dc-a2e3-761c71848acf", "severity": "medium", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_office_app.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_office_app.json index 07ad86929b6d2..d56c90552d457 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_office_app.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_office_app.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Started by an Office Application", - "query": "event.category:process and event.type:(start or process_started) and process.name:MSBuild.exe and process.parent.name:(eqnedt32.exe or excel.exe or fltldr.exe or msaccess.exe or mspub.exe or outlook.exe or powerpnt.exe or winword.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"MSBuild.exe\" and\n process.parent.name : (\"eqnedt32.exe\",\n \"excel.exe\",\n \"fltldr.exe\",\n \"msaccess.exe\",\n \"mspub.exe\",\n \"outlook.exe\",\n \"powerpnt.exe\",\n \"winword.exe\" )\n", "references": [ "https://blog.talosintelligence.com/2020/02/building-bypass-with-msbuild.html" ], @@ -56,6 +56,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_script.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_script.json index b5556559ec33e..3b640d8757b51 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_script.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_script.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Started by a Script Process", - "query": "event.category:process and event.type: start and process.name:MSBuild.exe and process.parent.name:(cmd.exe or powershell.exe or cscript.exe or wscript.exe)", + "query": "process where event.type == \"start\" and\n (process.name : \"MSBuild.exe\" or process.pe.original_file_name == \"MSBuild.exe\") and\n process.parent.name : (\"cmd.exe\", \"powershell.exe\", \"cscript.exe\", \"wscript.exe\", \"mshta.exe\")\n", "risk_score": 21, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae2", "severity": "low", @@ -53,6 +53,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_system_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_system_process.json index ee63d950f0b24..33094a88af313 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_system_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_by_system_process.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Started by a System Process", - "query": "event.category:process and event.type:(start or process_started) and process.name:MSBuild.exe and process.parent.name:(explorer.exe or wmiprvse.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"MSBuild.exe\" and\n process.parent.name : (\"explorer.exe\", \"wmiprvse.exe\")\n", "risk_score": 47, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae3", "severity": "medium", @@ -53,6 +53,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_renamed.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_renamed.json index a5980acd3bb17..43051cb8b27c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_renamed.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_renamed.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Using an Alternate Name", - "query": "event.category:process and event.type:(start or process_started) and process.pe.original_file_name:MSBuild.exe and not process.name: MSBuild.exe", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.pe.original_file_name == \"MSBuild.exe\" and\n not process.name : \"MSBuild.exe\"\n", "risk_score": 21, "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae4", "severity": "low", @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_unusal_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_unusal_process.json index 0ff3ad33ebb0b..38a5a99370662 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_unusal_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_msbuild_started_unusal_process.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Microsoft Build Engine Started an Unusual Process", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:MSBuild.exe and process.name:(csc.exe or iexplore.exe or powershell.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"MSBuild.exe\" and\n process.name : (\"csc.exe\", \"iexplore.exe\", \"powershell.exe\")\n", "references": [ "https://blog.talosintelligence.com/2020/02/building-bypass-with-msbuild.html" ], @@ -54,6 +54,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_suspicious_explorer_winword.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_suspicious_explorer_winword.json index 9403a37e6e529..74aaa9a9c3615 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_suspicious_explorer_winword.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_suspicious_explorer_winword.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Potential DLL SideLoading via Trusted Microsoft Programs", - "query": "event.category:process and event.type:(start or process_started) and process.pe.original_file_name:(WinWord.exe or EXPLORER.EXE or w3wp.exe or DISM.EXE) and not (process.name:(winword.exe or WINWORD.EXE or explorer.exe or w3wp.exe or Dism.exe) or process.executable:(\"C:\\Windows\\explorer.exe\" or C\\:\\\\Program?Files\\\\Microsoft?Office\\\\root\\\\Office*\\\\WINWORD.EXE or C\\:\\\\Program?Files?\\(x86\\)\\\\Microsoft?Office\\\\root\\\\Office*\\\\WINWORD.EXE or \"C:\\Windows\\System32\\Dism.exe\" or \"C:\\Windows\\SysWOW64\\Dism.exe\" or \"C:\\Windows\\System32\\inetsrv\\w3wp.exe\"))", + "query": "process where event.type == \"start\" and\n process.pe.original_file_name in (\"WinWord.exe\", \"EXPLORER.EXE\", \"w3wp.exe\", \"DISM.EXE\") and\n not (process.name : (\"winword.exe\", \"explorer.exe\", \"w3wp.exe\", \"Dism.exe\") or\n process.executable : (\"?:\\\\Windows\\\\explorer.exe\",\n \"?:\\\\Program Files\\\\Microsoft Office\\\\root\\\\Office*\\\\WINWORD.EXE\",\n \"?:\\\\Program Files?(x86)\\\\Microsoft Office\\\\root\\\\Office*\\\\WINWORD.EXE\",\n \"?:\\\\Windows\\\\System32\\\\Dism.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\Dism.exe\",\n \"?:\\\\Windows\\\\System32\\\\inetsrv\\\\w3wp.exe\")\n )\n", "risk_score": 73, "rule_id": "1160dcdb-0a0a-4a79-91d8-9b84616edebd", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_via_trusted_developer_utilities.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_via_trusted_developer_utilities.json deleted file mode 100644 index f6a3cdf222f36..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_execution_via_trusted_developer_utilities.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Identifies possibly suspicious activity using trusted Windows developer activity.", - "false_positives": [ - "These programs may be used by Windows developers but use by non-engineers is unusual." - ], - "from": "now-9m", - "index": [ - "winlogbeat-*", - "logs-endpoint.events.*", - "logs-windows.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Trusted Developer Application Usage", - "query": "event.category:process and event.type:(start or process_started) and process.name:(MSBuild.exe or msxsl.exe)", - "risk_score": 21, - "rule_id": "9d110cb3-5f4b-4c9a-b9f5-53f0a1707ae1", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Defense Evasion" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0005", - "name": "Defense Evasion", - "reference": "https://attack.mitre.org/tactics/TA0005/" - }, - "technique": [ - { - "id": "T1127", - "name": "Trusted Developer Utilities Proxy Execution", - "reference": "https://attack.mitre.org/techniques/T1127/" - } - ] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0002", - "name": "Execution", - "reference": "https://attack.mitre.org/tactics/TA0002/" - }, - "technique": [] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json index 853c6514e0dd4..6512c5ad473b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_configuration_modified.json @@ -14,7 +14,7 @@ "license": "Elastic License v2", "name": "GCP Storage Bucket Configuration Modification", "note": "The GCP Filebeat module must be enabled to use this rule.", - "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:storage.buckets.update and event.outcome:success", + "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:\"storage.buckets.update\" and event.outcome:success", "references": [ "https://cloud.google.com/storage/docs/key-terms#buckets" ], @@ -31,5 +31,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json index 5a3db946e0a1d..e79c14e76cba7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_gcp_storage_bucket_permissions_modified.json @@ -14,7 +14,7 @@ "license": "Elastic License v2", "name": "GCP Storage Bucket Permissions Modification", "note": "The GCP Filebeat module must be enabled to use this rule.", - "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:storage.setIamPermissions and event.outcome:success", + "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:\"storage.setIamPermissions\" and event.outcome:success", "references": [ "https://cloud.google.com/storage/docs/access-control/iam-permissions" ], @@ -48,5 +48,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json index 31dd11ca719f7..a98f57a132ba4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_guardduty_detector_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS GuardDuty Detector Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteDetector and event.dataset:aws.cloudtrail and event.provider:guardduty.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:guardduty.amazonaws.com and event.action:DeleteDetector and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/guardduty/delete-detector.html", "https://docs.aws.amazon.com/guardduty/latest/APIReference/API_DeleteDetector.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_hex_encoding_or_decoding_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_hex_encoding_or_decoding_activity.json deleted file mode 100644 index fde65e1d1f9d8..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_hex_encoding_or_decoding_activity.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Adversaries may encode/decode data in an attempt to evade detection by host- or network-based security controls.", - "false_positives": [ - "Automated tools such as Jenkins may encode or decode files as part of their normal behavior. These events can be filtered by the process executable or username values." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Hex Encoding/Decoding Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:(hexdump or od or xxd)", - "risk_score": 21, - "rule_id": "a9198571-b135-4a76-b055-e3e5a476fd83", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection", - "Defense Evasion" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0005", - "name": "Defense Evasion", - "reference": "https://attack.mitre.org/tactics/TA0005/" - }, - "technique": [ - { - "id": "T1140", - "name": "Deobfuscate/Decode Files or Information", - "reference": "https://attack.mitre.org/techniques/T1140/" - }, - { - "id": "T1027", - "name": "Obfuscated Files or Information", - "reference": "https://attack.mitre.org/techniques/T1027/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_iis_httplogging_disabled.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_iis_httplogging_disabled.json index 30284a3c999bf..16de1c9c21f97 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_iis_httplogging_disabled.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_iis_httplogging_disabled.json @@ -9,11 +9,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "max_signals": 33, "name": "IIS HTTP Logging Disabled", - "query": "event.category:process and event.type:(start or process_started) and (process.name:appcmd.exe or process.pe.original_file_name:appcmd.exe) and process.args:/dontLog\\:\\\"True\\\" and not process.parent.name:iissetup.exe", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"appcmd.exe\" or process.pe.original_file_name == \"appcmd.exe\") and\n process.args : \"/dontLog*:*True\" and\n not process.parent.name : \"iissetup.exe\"\n", "risk_score": 73, "rule_id": "ebf1adea-ccf2-4943-8b96-7ab11ca173a5", "severity": "high", @@ -42,6 +42,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_suspicious_werfault_childproc.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_suspicious_werfault_childproc.json index 670619cc5753a..e0f544ceb832c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_suspicious_werfault_childproc.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_suspicious_werfault_childproc.json @@ -4,7 +4,7 @@ ], "description": "A suspicious WerFault child process was detected, which may indicate an attempt to run unnoticed. Verify process details such as command line, network connections, file writes and parent process details as well.", "false_positives": [ - "Custom Windows Error Reporting Debugger" + "Custom Windows error reporting debugger or applications restarted by WerFault after a crash." ], "from": "now-9m", "index": [ @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious WerFault Child Process", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:WerFault.exe and not process.name:(cofire.exe or psr.exe or VsJITDebugger.exe or TTTracer.exe or rundll32.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"WerFault.exe\" and\n not process.name : (\"cofire.exe\",\n \"psr.exe\",\n \"VsJITDebugger.exe\",\n \"TTTracer.exe\",\n \"rundll32.exe\",\n \"LogiOptionsMgr.exe\") and\n not process.args : (\"/LOADSAVEDWINDOWS\",\n \"/restore\",\n \"RestartByRestartManager*\",\n \"--restarted\",\n \"createdump\",\n \"dontsend\",\n \"/watson\")\n", "references": [ "https://www.hexacorn.com/blog/2019/09/19/silentprocessexit-quick-look-under-the-hood/", "https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES/blob/master/Persistence/persistence_SilentProcessExit_ImageHijack_sysmon_13_1.evtx", @@ -49,6 +49,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_trusted_directory.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_trusted_directory.json index 7fda1a757a9d0..b0d11121c1a15 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_trusted_directory.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_masquerading_trusted_directory.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Program Files Directory Masquerading", - "query": "process where event.type in (\"start\", \"process_started\", \"info\") and\n /* capture both fake program files directory in process executable as well as if passed in process args as a dll*/\n process.args : (\"C:\\\\*Program*Files*\\\\*\", \"C:\\\\*Program*Files*\\\\*\") and\n not process.args : (\"C:\\\\Program Files\\\\*\", \"C:\\\\Program Files (x86)\\\\*\")\n", + "query": "process where event.type == \"start\" and\n process.executable : \"C:\\\\*Program*Files*\\\\*.exe\" and\n not process.executable : (\"C:\\\\Program Files\\\\*.exe\", \"C:\\\\Program Files (x86)\\\\*.exe\", \"C:\\\\Users\\\\*.exe\", \"C:\\\\ProgramData\\\\*.exe\")\n", "risk_score": 47, "rule_id": "32c5cf9c-2ef8-4e87-819e-5ccb7cd18b14", "severity": "medium", @@ -42,5 +42,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modification_of_boot_config.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modification_of_boot_config.json index 29668a1202d6a..7c58d82ec1061 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modification_of_boot_config.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modification_of_boot_config.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Modification of Boot Configuration", - "query": "event.category:process and event.type:(start or process_started) and process.name:bcdedit.exe and process.args:(/set and (bootstatuspolicy and ignoreallfailures or no and recoveryenabled))", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"bcdedit.exe\" or process.pe.original_file_name == \"bcdedit.exe\") and\n (process.args : \"/set\" and process.args : \"bootstatuspolicy\" and process.args : \"ignoreallfailures\") or\n (process.args : \"no\" and process.args : \"recoveryenabled\")\n", "risk_score": 21, "rule_id": "69c251fb-a5d6-4035-b5ec-40438bd829ff", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modify_environment_launchctl.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modify_environment_launchctl.json index a27a207832d3f..d41804247945b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modify_environment_launchctl.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_modify_environment_launchctl.json @@ -11,7 +11,7 @@ "language": "kuery", "license": "Elastic License v2", "name": "Modification of Environment Variable via Launchctl", - "query": "event.category:process and event.type:start and process.name:launchctl and process.args:(setenv and not (JAVA*_HOME or RUNTIME_JAVA_HOME or DBUS_LAUNCHD_SESSION_BUS_SOCKET or ANT_HOME))", + "query": "event.category:process and event.type:start and process.name:launchctl and process.args:(setenv and not (JAVA*_HOME or RUNTIME_JAVA_HOME or DBUS_LAUNCHD_SESSION_BUS_SOCKET or ANT_HOME or LG_WEBOS_TV_SDK_HOME or WEBOS_CLI_TV or EDEN_ENV) ) and not process.parent.executable:(\"/Applications/NoMachine.app/Contents/Frameworks/bin/nxserver.bin\" or \"/usr/local/bin/kr\" or \"/Applications/NoMachine.app/Contents/Frameworks/bin/nxserver.bin\" or \"/Applications/IntelliJ IDEA CE.app/Contents/jbr/Contents/Home/lib/jspawnhelper\")", "references": [ "https://github.com/rapid7/metasploit-framework/blob/master//modules/post/osx/escalate/tccbypass.rb" ], @@ -51,5 +51,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json index 148e175ed677b..727bd4a579566 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_s3_bucket_configuration_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS S3 Bucket Configuration Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(DeleteBucketPolicy or DeleteBucketReplication or DeleteBucketCors or DeleteBucketEncryption or DeleteBucketLifecycle) and event.dataset:aws.cloudtrail and event.provider:s3.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:s3.amazonaws.com and event.action:(DeleteBucketPolicy or DeleteBucketReplication or DeleteBucketCors or DeleteBucketEncryption or DeleteBucketLifecycle) and event.outcome:success", "references": [ "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketPolicy.html", "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketReplication.html", @@ -54,5 +54,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_managedcode_host_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_managedcode_host_process.json index 69451728e8fc0..3f5d69ff7f4ec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_managedcode_host_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_managedcode_host_process.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious Managed Code Hosting Process", - "query": "event.category:file and not event.type:deletion and file.name:(wscript.exe.log or mshta.exe.log or wscript.exe.log or wmic.exe.log or svchost.exe.log or dllhost.exe.log or cmstp.exe.log or regsvr32.exe.log)", + "query": "sequence by process.entity_id with maxspan=5m\n [process where event.type == \"start\" and \n process.name : (\"wscript.exe\", \"cscript.exe\", \"mshta.exe\", \"wmic.exe\", \"regsvr32.exe\", \"svchost.exe\", \"dllhost.exe\", \"cmstp.exe\")]\n [file where event.type != \"deletion\" and\n file.name : (\"wscript.exe.log\",\n \"cscript.exe\",\n \"mshta.exe.log\",\n \"wmic.exe.log\",\n \"svchost.exe.log\",\n \"dllhost.exe.log\",\n \"cmstp.exe.log\",\n \"regsvr32.exe.log\")]\n", "references": [ "https://blog.menasec.net/2019/07/interesting-difr-traces-of-net-clr.html" ], @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_scrobj_load.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_scrobj_load.json index 8460293f0be1f..7af9829cb43f5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_scrobj_load.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_suspicious_scrobj_load.json @@ -5,15 +5,13 @@ "description": "Identifies scrobj.dll loaded into unusual Microsoft processes. This usually means a malicious scriptlet is being executed in the target process.", "from": "now-9m", "index": [ - "logs-endpoint.events.*", - "winlogbeat-*", - "logs-windows.*" + "logs-endpoint.events.*" ], "language": "eql", "license": "Elastic License v2", - "name": "Windows Suspicious Script Object Execution", - "query": "/* add winlogbeat-* when process.code_signature.* fields are populated */\n\nsequence by process.entity_id with maxspan = 2m\n [process where event.type in (\"start\", \"process_started\") and\n /* uncomment once in winlogbeat */\n /* process.code_signature.subject_name : \"Microsoft Corporation\" and process.code_signature.trusted : true and */\n not process.name : (\n \"cscript.exe\",\n \"iexplore.exe\",\n \"MicrosoftEdge.exe\",\n \"msiexec.exe\",\n \"smartscreen.exe\",\n \"taskhostw.exe\",\n \"w3wp.exe\",\n \"wscript.exe\")]\n [library where event.type == \"start\" and dll.name : \"scrobj.dll\"]\n", - "risk_score": 21, + "name": "Suspicious Script Object Execution", + "query": "sequence by process.entity_id with maxspan=2m\n [process where event.type == \"start\" \n and (process.code_signature.subject_name in (\"Microsoft Corporation\", \"Microsoft Windows\") and \n process.code_signature.trusted == true) and\n not process.executable : (\n \"?:\\\\Windows\\\\System32\\\\cscript.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\cscript.exe\",\n \"?:\\\\Program Files (x86)\\\\Internet Explorer\\\\iexplore.exe\",\n \"?:\\\\Program Files\\\\Internet Explorer\\\\iexplore.exe\",\n \"?:\\\\Windows\\\\SystemApps\\\\Microsoft.MicrosoftEdge_*\\\\MicrosoftEdge.exe\",\n \"?:\\\\Windows\\\\system32\\\\msiexec.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\msiexec.exe\",\n \"?:\\\\Windows\\\\System32\\\\smartscreen.exe\",\n \"?:\\\\Windows\\\\system32\\\\taskhostw.exe\",\n \"?:\\\\windows\\\\system32\\\\inetsrv\\\\w3wp.exe\",\n \"?:\\\\windows\\\\SysWOW64\\\\inetsrv\\\\w3wp.exe\",\n \"?:\\\\Windows\\\\system32\\\\wscript.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\wscript.exe\",\n \"?:\\\\Windows\\\\system32\\\\mobsync.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\mobsync.exe\",\n \"?:\\\\Windows\\\\System32\\\\cmd.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\cmd.exe\")]\n [library where event.type == \"start\" and dll.name : \"scrobj.dll\"]\n", + "risk_score": 47, "rule_id": "4ed678a9-3a4f-41fb-9fea-f85a6e0a0dff", "severity": "medium", "tags": [ @@ -31,9 +29,15 @@ "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/" }, - "technique": [] + "technique": [ + { + "id": "T1218", + "name": "Signed Binary Proxy Execution", + "reference": "https://attack.mitre.org/techniques/T1218/" + } + ] } ], "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_system_critical_proc_abnormal_file_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_system_critical_proc_abnormal_file_activity.json index ea60b9a38d27c..6fa6f0ab569a8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_system_critical_proc_abnormal_file_activity.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_system_critical_proc_abnormal_file_activity.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Unusual Executable File Creation by a System Critical Process", - "query": "event.category:file and not event.type:deletion and file.extension:(exe or dll) and process.name:(smss.exe or autochk.exe or csrss.exe or wininit.exe or services.exe or lsass.exe or winlogon.exe or userinit.exe or LogonUI.exe)", + "query": "file where event.type != \"deletion\" and\n file.extension : (\"exe\", \"dll\") and\n process.name : (\"smss.exe\",\n \"autochk.exe\",\n \"csrss.exe\",\n \"wininit.exe\",\n \"services.exe\",\n \"lsass.exe\",\n \"winlogon.exe\",\n \"userinit.exe\",\n \"LogonUI.exe\")\n", "risk_score": 73, "rule_id": "e94262f2-c1e9-4d3f-a907-aeab16712e1a", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_timestomp_touch.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_timestomp_touch.json index 74dbc53ee1c0a..ee45ffce416d2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_timestomp_touch.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_timestomp_touch.json @@ -12,7 +12,7 @@ "license": "Elastic License v2", "max_signals": 33, "name": "Timestomping using Touch Command", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"touch\" and process.args : (\"-r\", \"-t\", \"-a*\",\"-m*\")\n", + "query": "process where event.type == \"start\" and\n process.name : \"touch\" and user.id != \"0\" and\n process.args : (\"-r\", \"-t\", \"-a*\",\"-m*\") and\n not process.args : (\"/usr/lib/go-*/bin/go\", \"/usr/lib/dracut/dracut-functions.sh\", \"/tmp/KSInstallAction.*/m/.patch/*\")\n", "risk_score": 47, "rule_id": "b0046934-486e-462f-9487-0d4cf9e429c6", "severity": "medium", @@ -50,5 +50,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_dir_ads.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_dir_ads.json index 4e354e69ca1e0..196a3de9b9e6f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_dir_ads.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_dir_ads.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Unusual Process Execution Path - Alternate Data Stream", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.args : \"C:\\\\*:*\"\n", + "query": "process where event.type == \"start\" and\n process.args : \"?:\\\\*:*\" and process.args_count == 1\n", "risk_score": 47, "rule_id": "4bd1c1af-79d4-4d37-9efa-6e0240640242", "severity": "medium", @@ -42,5 +42,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_network_connection_via_rundll32.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_network_connection_via_rundll32.json index 7413d91523820..d920c4f853dfd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_network_connection_via_rundll32.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_network_connection_via_rundll32.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Unusual Network Connection via RunDLL32", - "query": "sequence by host.id, process.entity_id with maxspan=1m\n [process where event.type in (\"start\", \"process_started\", \"info\") and process.name : \"rundll32.exe\" and process.args_count == 1]\n [network where process.name : \"rundll32.exe\" and network.protocol != \"dns\" and network.direction == \"outgoing\" and\n not cidrmatch(destination.ip, \"10.0.0.0/8\", \"172.16.0.0/12\", \"192.168.0.0/16\", \"127.0.0.0/8\")]\n", + "query": "sequence by host.id, process.entity_id with maxspan=1m\n [process where event.type in (\"start\", \"process_started\") and process.name : \"rundll32.exe\" and process.args_count == 1]\n [network where process.name : \"rundll32.exe\" and\n not cidrmatch(destination.ip, \"10.0.0.0/8\", \"172.16.0.0/12\", \"192.168.0.0/16\", \"127.0.0.0/8\", \"FE80::/10\", \"::1/128\")]\n", "risk_score": 47, "rule_id": "52aaab7b-b51c-441a-89ce-4387b3aea886", "severity": "medium", @@ -48,5 +48,5 @@ } ], "type": "eql", - "version": 8 + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_system_vp_child_program.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_system_vp_child_program.json index e629aefec2a67..2153b4c8e8c04 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_system_vp_child_program.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_unusual_system_vp_child_program.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Unusual Child Process from a System Virtual Process", - "query": "event.category:process and event.type:(start or process_started) and process.parent.pid:4 and not process.executable:(Registry or MemCompression or \"C:\\Windows\\System32\\smss.exe\")", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.pid == 4 and\n not process.executable : (\"Registry\", \"MemCompression\", \"?:\\\\Windows\\\\System32\\\\smss.exe\")\n", "risk_score": 73, "rule_id": "de9bd7e0-49e9-4e92-a64d-53ade2e66af1", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_via_filter_manager.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_via_filter_manager.json index 82a75d0920b6f..51d1789804548 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_via_filter_manager.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_via_filter_manager.json @@ -9,13 +9,13 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Potential Evasion via Filter Manager", - "query": "event.category:process and event.type:(start or process_started) and process.name:fltMC.exe", - "risk_score": 21, + "query": "process where event.type in (\"start\", \"process_started\") and \n process.name : \"fltMC.exe\" and process.args : \"unload\"\n", + "risk_score": 47, "rule_id": "06dceabf-adca-48af-ac79-ffdf4c3b1e9a", - "severity": "low", + "severity": "medium", "tags": [ "Elastic", "Host", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 6 + "type": "eql", + "version": 7 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_volume_shadow_copy_deletion_via_wmic.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_volume_shadow_copy_deletion_via_wmic.json index 6a1f32cf5fb75..e519b23a32b0d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_volume_shadow_copy_deletion_via_wmic.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_volume_shadow_copy_deletion_via_wmic.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Volume Shadow Copy Deletion via WMIC", - "query": "event.category:process and event.type:(start or process_started) and process.name:WMIC.exe and process.args:(delete and shadowcopy)", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"WMIC.exe\" or process.pe.original_file_name == \"wmic.exe\") and\n process.args : \"delete\" and process.args : \"shadowcopy\"\n", "risk_score": 73, "rule_id": "dc9c1f74-dac3-48e3-b47f-eb79db358f57", "severity": "high", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json index a489ce7fd54cc..ebe1cbf18d6a8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/defense_evasion_waf_acl_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS WAF Access Control List Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteWebACL and event.dataset:aws.cloudtrail and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.action:DeleteWebACL and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf-regional/delete-web-acl.html", "https://docs.aws.amazon.com/waf/latest/APIReference/API_wafRegional_DeleteWebACL.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_net_command_system_account.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_net_command_system_account.json index 218704ed5714c..87b32d14791bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_net_command_system_account.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_net_command_system_account.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Net command via SYSTEM account", - "query": "event.category:process and event.type:(start or process_started) and (process.name:(whoami.exe or net.exe) or process.name:net1.exe and not process.parent.name:net.exe) and user.name:SYSTEM", + "query": "process where event.type in (\"start\", \"process_started\") and \n user.id in (\"S-1-5-18\", \"S-1-5-19\", \"S-1-5-20\") and\n process.name : \"whoami.exe\" or\n (process.name : \"net1.exe\" and not process.parent.name : \"net.exe\")\n", "risk_score": 21, "rule_id": "2856446a-34e6-435b-9fb5-f8f040bfa7ed", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 6 + "type": "eql", + "version": 7 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_external_ip_lookup.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_external_ip_lookup.json new file mode 100644 index 0000000000000..1df7e2138b969 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_external_ip_lookup.json @@ -0,0 +1,53 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies domains commonly used by adversaries for post-exploitation IP lookups. It is common for adversaries to test for Internet access and acquire their external IP address after they have gained access to a system. Among others, this has been observed in campaigns leveraging the information stealer, Trickbot.", + "false_positives": [ + "If the domains listed in this rule are used as part of an authorized workflow, this rule will be triggered by those events. Validate that this is expected activity and tune the rule to fit your environment variables." + ], + "from": "now-9m", + "index": [ + "winlogbeat-*", + "logs-endpoint.events.*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "External IP Lookup fron Non-Browser Process", + "query": "network where network.protocol == \"dns\" and\n process.name != null and user.id not in (\"S-1-5-19\", \"S-1-5-20\") and\n event.action == \"lookup_requested\" and\n /* Add new external IP lookup services here */\n dns.question.name :\n (\n \"*api.ipify.org\",\n \"*freegeoip.app\",\n \"*checkip.amazonaws.com\",\n \"*checkip.dyndns.org\",\n \"*freegeoip.app\",\n \"*icanhazip.com\",\n \"*ifconfig.*\",\n \"*ipecho.net\",\n \"*ipgeoapi.com\",\n \"*ipinfo.io\",\n \"*ip.anysrc.net\",\n \"*myexternalip.com\",\n \"*myipaddress.com\",\n \"*showipaddress.com\",\n \"*whatismyipaddress.com\",\n \"*wtfismyip.com\"\n ) and\n /* Insert noisy false positives here */\n not process.executable :\n (\n \"?:\\\\Program Files\\\\*.exe\",\n \"?:\\\\Program Files (x86)\\\\*.exe\",\n \"?:\\\\Windows\\\\System32\\\\WWAHost.exe\",\n \"?:\\\\Windows\\\\System32\\\\smartscreen.exe\",\n \"?:\\\\Windows\\\\System32\\\\MicrosoftEdgeCP.exe\",\n \"?:\\\\ProgramData\\\\Microsoft\\\\Windows Defender\\\\Platform\\\\*\\\\MsMpEng.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Programs\\\\Fiddler\\\\Fiddler.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Programs\\\\Microsoft VS Code\\\\Code.exe\",\n \"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Microsoft\\\\OneDrive\\\\OneDrive.exe\"\n )\n", + "references": [ + "https://community.jisc.ac.uk/blogs/csirt/article/trickbot-analysis-and-mitigation", + "https://www.cybereason.com/blog/dropping-anchor-from-a-trickbot-infection-to-the-discovery-of-the-anchor-malware" + ], + "risk_score": 21, + "rule_id": "1d72d014-e2ab-4707-b056-9b96abe7b511", + "severity": "low", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Discovery" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0007", + "name": "Discovery", + "reference": "https://attack.mitre.org/tactics/TA0007/" + }, + "technique": [ + { + "id": "T1016", + "name": "System Network Configuration Discovery", + "reference": "https://attack.mitre.org/techniques/T1016/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 4 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_public_ip_reconnaissance.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_public_ip_reconnaissance.json deleted file mode 100644 index a4e2b89a6d8ca..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_post_exploitation_public_ip_reconnaissance.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Identifies domains commonly used by adversaries for post-exploitation IP reconnaissance. It is common for adversaries to test for Internet access and acquire their public IP address after they have gained access to a system. Among others, this has been observed in campaigns leveraging the information stealer, Trickbot.", - "false_positives": [ - "If the domains listed in this rule are used as part of an authorized workflow, this rule will be triggered by those events. Validate that this is expected activity and tune the rule to fit your environment variables." - ], - "index": [ - "packetbeat-*" - ], - "language": "lucene", - "license": "Elastic License v2", - "name": "Public IP Reconnaissance Activity", - "note": "This rule takes HTTP redirects and HTTP referrer's into account, however neither HTTP redirect status codes nor HTTP referrer's are visible with TLS traffic which can lead to multiple events per alert.", - "query": "event.category:network AND event.type:connection AND server.domain:(ipecho.net OR ipinfo.io OR ifconfig.co OR ifconfig.me OR icanhazip.com OR myexternalip.com OR api.ipify.org OR bot.whatismyipaddress.com OR ip.anysrc.net OR wtfismyip.com) AND NOT http.response.status_code:302 AND status:OK AND NOT _exists_:http.request.referrer", - "references": [ - "https://community.jisc.ac.uk/blogs/csirt/article/trickbot-analysis-and-mitigation", - "https://www.cybereason.com/blog/dropping-anchor-from-a-trickbot-infection-to-the-discovery-of-the-anchor-malware" - ], - "risk_score": 21, - "rule_id": "1d72d014-e2ab-4707-b056-9b96abe7b511", - "severity": "low", - "tags": [ - "Elastic", - "Network", - "Threat Detection", - "Discovery" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0007", - "name": "Discovery", - "reference": "https://attack.mitre.org/tactics/TA0007/" - }, - "technique": [ - { - "id": "T1016", - "name": "System Network Configuration Discovery", - "reference": "https://attack.mitre.org/techniques/T1016/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 3 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_process_discovery_via_tasklist_command.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_process_discovery_via_tasklist_command.json deleted file mode 100644 index f0839c5229d42..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_process_discovery_via_tasklist_command.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Adversaries may attempt to get information about running processes on a system.", - "false_positives": [ - "Administrators may use the tasklist command to display a list of currently running processes. By itself, it does not indicate malicious activity. After obtaining a foothold, it's possible adversaries may use discovery commands like tasklist to get information about running processes." - ], - "from": "now-9m", - "index": [ - "winlogbeat-*", - "logs-endpoint.events.*", - "logs-windows.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Process Discovery via Tasklist", - "query": "event.category:process and event.type:(start or process_started) and process.name:tasklist.exe", - "risk_score": 21, - "rule_id": "cc16f774-59f9-462d-8b98-d27ccd4519ec", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Discovery" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0007", - "name": "Discovery", - "reference": "https://attack.mitre.org/tactics/TA0007/" - }, - "technique": [ - { - "id": "T1057", - "name": "Process Discovery", - "reference": "https://attack.mitre.org/techniques/T1057/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 6 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_query_registry_via_reg.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_query_registry_via_reg.json deleted file mode 100644 index 7694f6ae30048..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_query_registry_via_reg.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Enumeration or discovery of the Windows registry using reg.exe. This information can be used to perform follow-on activities.", - "from": "now-9m", - "index": [ - "logs-endpoint.events.*", - "winlogbeat-*", - "logs-windows.*" - ], - "language": "eql", - "license": "Elastic License v2", - "name": "Query Registry via reg.exe", - "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"reg.exe\" or process.pe.original_file_name == \"reg.exe\") and\n process.args == \"query\"\n", - "risk_score": 21, - "rule_id": "68113fdc-3105-4cdd-85bb-e643c416ef0b", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Discovery" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0007", - "name": "Discovery", - "reference": "https://attack.mitre.org/tactics/TA0007/" - }, - "technique": [ - { - "id": "T1012", - "name": "Query Registry", - "reference": "https://attack.mitre.org/techniques/T1012/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "eql", - "version": 3 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_security_software_grep.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_security_software_grep.json index 486c7c8a978a6..4862cf42e92c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_security_software_grep.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_security_software_grep.json @@ -3,15 +3,18 @@ "Elastic" ], "description": "Identifies the use of the grep command to discover known third-party macOS and Linux security tools, such as Antivirus or Host Firewall details.", + "false_positives": [ + "Endpoint Security installers, updaters and post installation verification scripts." + ], "from": "now-9m", "index": [ "logs-endpoint.events.*", "auditbeat-*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Security Software Discovery via Grep", - "query": "event.category : process and event.type : (start or process_started) and process.name : grep and process.args : (\"Little Snitch\" or Avast* or Avira* or ESET* or esets_* or BlockBlock or 360* or LuLu or KnockKnock* or kav or KIS or RTProtectionDaemon or Malware* or VShieldScanner or WebProtection or webinspectord or McAfee* or isecespd* or macmnsvc* or masvc or kesl or avscan or guard or rtvscand or symcfgd or scmdaemon or symantec or elastic-endpoint )", + "query": "process where event.type == \"start\" and\nprocess.name : \"grep\" and user.id != \"0\" and\n not process.parent.executable : \"/Library/Application Support/*\" and\n process.args :\n (\"Little Snitch*\",\n \"Avast*\",\n \"Avira*\",\n \"ESET*\",\n \"BlockBlock*\",\n \"360Sec*\",\n \"LuLu*\",\n \"KnockKnock*\",\n \"kav\",\n \"KIS\",\n \"RTProtectionDaemon*\",\n \"Malware*\",\n \"VShieldScanner*\",\n \"WebProtection*\",\n \"webinspectord*\",\n \"McAfee*\",\n \"isecespd*\",\n \"macmnsvc*\",\n \"masvc*\",\n \"kesl*\",\n \"avscan*\",\n \"guard*\",\n \"rtvscand*\",\n \"symcfgd*\",\n \"scmdaemon*\",\n \"symantec*\",\n \"sophos*\",\n \"osquery*\",\n \"elastic-endpoint*\"\n ) and\n not (process.args : \"Avast\" and process.args : \"Passwords\")\n", "risk_score": 47, "rule_id": "870aecc0-cea4-4110-af3f-e02e9b373655", "severity": "medium", @@ -35,12 +38,19 @@ { "id": "T1518", "name": "Software Discovery", - "reference": "https://attack.mitre.org/techniques/T1518/" + "reference": "https://attack.mitre.org/techniques/T1518/", + "subtechnique": [ + { + "id": "T1518.001", + "name": "Security Software Discovery", + "reference": "https://attack.mitre.org/techniques/T1518/001/" + } + ] } ] } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 1 + "type": "eql", + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_users_domain_built_in_commands.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_users_domain_built_in_commands.json index da5f8941701c3..a9276ca950fa5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_users_domain_built_in_commands.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_users_domain_built_in_commands.json @@ -11,7 +11,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Enumeration of Users or Groups via Built-in Commands", - "query": "process where event.type in (\"start\", \"process_started\") and\n not process.parent.executable : (\"/Applications/NoMAD.app/Contents/MacOS/NoMAD\", \n \"/Applications/ZoomPresence.app/Contents/MacOS/ZoomPresence\") and \n process.name : (\"ldapsearch\", \"dsmemberutil\") or\n (process.name : \"dscl\" and \n process.args : (\"read\", \"-read\", \"list\", \"-list\", \"ls\", \"search\", \"-search\") and \n process.args : (\"/Active Directory/*\", \"/Users*\", \"/Groups*\"))\n", + "query": "process where event.type in (\"start\", \"process_started\") and\n not process.parent.executable : (\"/Applications/NoMAD.app/Contents/MacOS/NoMAD\", \n \"/Applications/ZoomPresence.app/Contents/MacOS/ZoomPresence\",\n \"/Applications/Sourcetree.app/Contents/MacOS/Sourcetree\",\n \"/Library/Application Support/JAMF/Jamf.app/Contents/MacOS/JamfDaemon.app/Contents/MacOS/JamfDaemon\",\n \"/usr/local/jamf/bin/jamf\"\n ) and \n process.name : (\"ldapsearch\", \"dsmemberutil\") or\n (process.name : \"dscl\" and \n process.args : (\"read\", \"-read\", \"list\", \"-list\", \"ls\", \"search\", \"-search\") and \n process.args : (\"/Active Directory/*\", \"/Users*\", \"/Groups*\"))\n", "risk_score": 21, "rule_id": "6e9b351e-a531-4bdc-b73e-7034d6eed7ff", "severity": "low", @@ -46,5 +46,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_command_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_command_activity.json index 95f4ab50a7c46..9999ab2ffb973 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_command_activity.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_command_activity.json @@ -12,10 +12,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Whoami Process Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:whoami.exe", + "query": "process where event.type in (\"start\", \"process_started\") and process.name : \"whoami.exe\"\n", "risk_score": 21, "rule_id": "ef862985-3f13-4262-a686-5f357bbb9bc2", "severity": "low", @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 6 + "type": "eql", + "version": 7 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_commmand.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_commmand.json deleted file mode 100644 index 712b729c3f82f..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/discovery_whoami_commmand.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "The whoami application was executed on a Linux host. This is often used by tools and persistence mechanisms to test for privileged access.", - "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-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "User Discovery via Whoami", - "query": "event.category:process and event.type:(start or process_started) and process.name:whoami", - "risk_score": 21, - "rule_id": "120559c6-5e24-49f4-9e30-8ffe697df6b9", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection", - "Discovery" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0007", - "name": "Discovery", - "reference": "https://attack.mitre.org/tactics/TA0007/" - }, - "technique": [ - { - "id": "T1033", - "name": "System Owner/User Discovery", - "reference": "https://attack.mitre.org/techniques/T1033/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_powershell.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_powershell.json deleted file mode 100644 index 6b44764b54f2b..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_powershell.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Identifies a suspicious parent child process relationship with cmd.exe descending from PowerShell.exe.", - "from": "now-9m", - "index": [ - "winlogbeat-*", - "logs-endpoint.events.*", - "logs-windows.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "PowerShell spawning Cmd", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:powershell.exe and process.name:cmd.exe", - "risk_score": 21, - "rule_id": "0f616aee-8161-4120-857e-742366f5eeb3", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Execution" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0002", - "name": "Execution", - "reference": "https://attack.mitre.org/tactics/TA0002/" - }, - "technique": [ - { - "id": "T1059", - "name": "Command and Scripting Interpreter", - "reference": "https://attack.mitre.org/techniques/T1059/", - "subtechnique": [ - { - "id": "T1059.001", - "name": "PowerShell", - "reference": "https://attack.mitre.org/techniques/T1059/001/" - } - ] - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_svchost.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_svchost.json index bfb1c2a667cf9..f0270a576c88b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_svchost.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_svchost.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Svchost spawning Cmd", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:svchost.exe and process.name:cmd.exe", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"svchost.exe\" and process.name : \"cmd.exe\" and \n not (process.pe.original_file_name == \"Cmd.Exe\" and process.args : \"?:\\\\Program Files\\\\Npcap\\\\CheckStatus.bat??\")\n", "risk_score": 21, "rule_id": "fd7a6052-58fa-4397-93c3-4795249ccfa2", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_unusual_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_unusual_process.json index db473becae526..ac21f5be5eaef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_unusual_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_started_by_unusual_process.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Unusual Parent Process for cmd.exe", - "query": "event.category:process and event.type:(start or process_started) and process.name:cmd.exe and process.parent.name:(lsass.exe or csrss.exe or notepad.exe or regsvr32.exe or dllhost.exe or LogonUI.exe or wermgr.exe or spoolsv.exe or jucheck.exe or jusched.exe or ctfmon.exe or taskhostw.exe or GoogleUpdate.exe or sppsvc.exe or sihost.exe or slui.exe or SIHClient.exe or SearchIndexer.exe or SearchProtocolHost.exe or FlashPlayerUpdateService.exe or WerFault.exe or WUDFHost.exe or unsecapp.exe or wlanext.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"cmd.exe\" and\n process.parent.name : (\"lsass.exe\",\n \"csrss.exe\",\n \"epad.exe\",\n \"regsvr32.exe\",\n \"dllhost.exe\",\n \"LogonUI.exe\",\n \"wermgr.exe\",\n \"spoolsv.exe\",\n \"jucheck.exe\",\n \"jusched.exe\",\n \"ctfmon.exe\",\n \"taskhostw.exe\",\n \"GoogleUpdate.exe\",\n \"sppsvc.exe\",\n \"sihost.exe\",\n \"slui.exe\",\n \"SIHClient.exe\",\n \"SearchIndexer.exe\",\n \"SearchProtocolHost.exe\",\n \"FlashPlayerUpdateService.exe\",\n \"WerFault.exe\",\n \"WUDFHost.exe\",\n \"unsecapp.exe\",\n \"wlanext.exe\" )\n", "risk_score": 47, "rule_id": "3b47900d-e793-49e8-968f-c90dc3526aa1", "severity": "medium", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_via_rundll32.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_via_rundll32.json index 519694cf2e730..1b25b865a4f29 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_via_rundll32.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_command_shell_via_rundll32.json @@ -3,6 +3,9 @@ "Elastic" ], "description": "Identifies command shell activity started via RunDLL32, which is commonly abused by attackers to host malicious code.", + "false_positives": [ + "Microsoft Windows installers leveraging RunDLL32 for installation." + ], "from": "now-9m", "index": [ "winlogbeat-*", @@ -12,7 +15,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Command Shell Activity Started via RunDLL32", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : (\"cmd.exe\", \"powershell.exe\") and\n process.parent.name : \"rundll32.exe\" and \n /* common FPs can be added here */\n not process.parent.args : \"C:\\\\Windows\\\\System32\\\\SHELL32.dll,RunAsNewUser_RunDLL\"\n", + "query": "process where event.type == \"start\" and\n process.name : (\"cmd.exe\", \"powershell.exe\") and\n process.parent.name : \"rundll32.exe\" and process.parent.command_line != null and\n /* common FPs can be added here */\n not process.parent.args : (\"C:\\\\Windows\\\\System32\\\\SHELL32.dll,RunAsNewUser_RunDLL\",\n \"C:\\\\WINDOWS\\\\*.tmp,zzzzInvokeManagedCustomActionOutOfProc\")\n", "risk_score": 21, "rule_id": "9ccf3ce0-0057-440a-91f5-870c6ad39093", "severity": "low", @@ -49,5 +52,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_from_unusual_path_cmdline.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_from_unusual_path_cmdline.json index 738d090f4c2be..f05496fc641a1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_from_unusual_path_cmdline.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_from_unusual_path_cmdline.json @@ -13,7 +13,7 @@ "license": "Elastic License v2", "name": "Execution from Unusual Directory - Command Line", "note": "This is related to the Process Execution from an Unusual Directory rule", - "query": "process where event.type in (\"start\", \"process_started\", \"info\") and\n process.name : (\"wscript.exe\",\"cscript.exe\",\"rundll32.exe\",\"regsvr32.exe\",\"cmstp.exe\",\"RegAsm.exe\",\"installutil.exe\",\"mshta.exe\",\"RegSvcs.exe\", \"powershell.exe\", \"pwsh.exe\", \"cmd.exe\") and\n /* add suspicious execution paths here */\nprocess.args : (\"C:\\\\PerfLogs\\\\*\",\"C:\\\\Users\\\\Public\\\\*\",\"C:\\\\Users\\\\Default\\\\*\",\"C:\\\\Windows\\\\Tasks\\\\*\",\"C:\\\\Intel\\\\*\", \"C:\\\\AMD\\\\Temp\\\\*\", \n \"C:\\\\Windows\\\\AppReadiness\\\\*\", \"C:\\\\Windows\\\\ServiceState\\\\*\",\"C:\\\\Windows\\\\security\\\\*\",\"C:\\\\Windows\\\\IdentityCRL\\\\*\",\"C:\\\\Windows\\\\Branding\\\\*\",\"C:\\\\Windows\\\\csc\\\\*\",\n \"C:\\\\Windows\\\\DigitalLocker\\\\*\",\"C:\\\\Windows\\\\en-US\\\\*\",\"C:\\\\Windows\\\\wlansvc\\\\*\",\"C:\\\\Windows\\\\Prefetch\\\\*\",\"C:\\\\Windows\\\\Fonts\\\\*\",\n \"C:\\\\Windows\\\\diagnostics\\\\*\",\"C:\\\\Windows\\\\TAPI\\\\*\",\"C:\\\\Windows\\\\INF\\\\*\",\"C:\\\\Windows\\\\System32\\\\Speech\\\\*\",\"C:\\\\windows\\\\tracing\\\\*\",\n \"c:\\\\windows\\\\IME\\\\*\",\"c:\\\\Windows\\\\Performance\\\\*\",\"c:\\\\windows\\\\intel\\\\*\",\"c:\\\\windows\\\\ms\\\\*\",\"C:\\\\Windows\\\\dot3svc\\\\*\",\"C:\\\\Windows\\\\ServiceProfiles\\\\*\",\n \"C:\\\\Windows\\\\panther\\\\*\",\"C:\\\\Windows\\\\RemotePackages\\\\*\",\"C:\\\\Windows\\\\OCR\\\\*\",\"C:\\\\Windows\\\\appcompat\\\\*\",\"C:\\\\Windows\\\\apppatch\\\\*\",\"C:\\\\Windows\\\\addins\\\\*\",\n \"C:\\\\Windows\\\\Setup\\\\*\",\"C:\\\\Windows\\\\Help\\\\*\",\"C:\\\\Windows\\\\SKB\\\\*\",\"C:\\\\Windows\\\\Vss\\\\*\",\"C:\\\\Windows\\\\Web\\\\*\",\"C:\\\\Windows\\\\servicing\\\\*\",\"C:\\\\Windows\\\\CbsTemp\\\\*\",\n \"C:\\\\Windows\\\\Logs\\\\*\",\"C:\\\\Windows\\\\WaaS\\\\*\",\"C:\\\\Windows\\\\twain_32\\\\*\",\"C:\\\\Windows\\\\ShellExperiences\\\\*\",\"C:\\\\Windows\\\\ShellComponents\\\\*\",\"C:\\\\Windows\\\\PLA\\\\*\",\n \"C:\\\\Windows\\\\Migration\\\\*\",\"C:\\\\Windows\\\\debug\\\\*\",\"C:\\\\Windows\\\\Cursors\\\\*\",\"C:\\\\Windows\\\\Containers\\\\*\",\"C:\\\\Windows\\\\Boot\\\\*\",\"C:\\\\Windows\\\\bcastdvr\\\\*\",\n \"C:\\\\Windows\\\\assembly\\\\*\",\"C:\\\\Windows\\\\TextInput\\\\*\",\"C:\\\\Windows\\\\security\\\\*\",\"C:\\\\Windows\\\\schemas\\\\*\",\"C:\\\\Windows\\\\SchCache\\\\*\",\"C:\\\\Windows\\\\Resources\\\\*\",\n \"C:\\\\Windows\\\\rescache\\\\*\",\"C:\\\\Windows\\\\Provisioning\\\\*\",\"C:\\\\Windows\\\\PrintDialog\\\\*\",\"C:\\\\Windows\\\\PolicyDefinitions\\\\*\",\"C:\\\\Windows\\\\media\\\\*\",\n \"C:\\\\Windows\\\\Globalization\\\\*\",\"C:\\\\Windows\\\\L2Schemas\\\\*\",\"C:\\\\Windows\\\\LiveKernelReports\\\\*\",\"C:\\\\Windows\\\\ModemLogs\\\\*\",\"C:\\\\Windows\\\\ImmersiveControlPanel\\\\*\",\n \"C:\\\\$Recycle.Bin\\\\*\")\n", + "query": "process where event.type in (\"start\", \"process_started\", \"info\") and\n process.name : (\"wscript.exe\", \n \"cscript.exe\", \n \"rundll32.exe\", \n \"regsvr32.exe\", \n \"cmstp.exe\",\n \"RegAsm.exe\",\n \"installutil.exe\",\n \"mshta.exe\",\n \"RegSvcs.exe\", \n \"powershell.exe\", \n \"pwsh.exe\", \n \"cmd.exe\") and\n \n /* add suspicious execution paths here */\n process.args : (\"C:\\\\PerfLogs\\\\*\",\n \"C:\\\\Users\\\\Public\\\\*\",\n \"C:\\\\Users\\\\Default\\\\*\",\n \"C:\\\\Windows\\\\Tasks\\\\*\",\n \"C:\\\\Intel\\\\*\", \n \"C:\\\\AMD\\\\Temp\\\\*\", \n \"C:\\\\Windows\\\\AppReadiness\\\\*\", \n \"C:\\\\Windows\\\\ServiceState\\\\*\",\n \"C:\\\\Windows\\\\security\\\\*\",\n \"C:\\\\Windows\\\\IdentityCRL\\\\*\",\n \"C:\\\\Windows\\\\Branding\\\\*\",\n \"C:\\\\Windows\\\\csc\\\\*\",\n \"C:\\\\Windows\\\\DigitalLocker\\\\*\",\n \"C:\\\\Windows\\\\en-US\\\\*\",\n \"C:\\\\Windows\\\\wlansvc\\\\*\",\n \"C:\\\\Windows\\\\Prefetch\\\\*\",\n \"C:\\\\Windows\\\\Fonts\\\\*\",\n \"C:\\\\Windows\\\\diagnostics\\\\*\",\n \"C:\\\\Windows\\\\TAPI\\\\*\",\n \"C:\\\\Windows\\\\INF\\\\*\",\n \"C:\\\\Windows\\\\System32\\\\Speech\\\\*\",\n \"C:\\\\windows\\\\tracing\\\\*\",\n \"c:\\\\windows\\\\IME\\\\*\",\n \"c:\\\\Windows\\\\Performance\\\\*\",\n \"c:\\\\windows\\\\intel\\\\*\",\n \"c:\\\\windows\\\\ms\\\\*\",\n \"C:\\\\Windows\\\\dot3svc\\\\*\",\n \"C:\\\\Windows\\\\ServiceProfiles\\\\*\",\n \"C:\\\\Windows\\\\panther\\\\*\",\n \"C:\\\\Windows\\\\RemotePackages\\\\*\",\n \"C:\\\\Windows\\\\OCR\\\\*\",\n \"C:\\\\Windows\\\\appcompat\\\\*\",\n \"C:\\\\Windows\\\\apppatch\\\\*\",\n \"C:\\\\Windows\\\\addins\\\\*\",\n \"C:\\\\Windows\\\\Setup\\\\*\",\n \"C:\\\\Windows\\\\Help\\\\*\",\n \"C:\\\\Windows\\\\SKB\\\\*\",\n \"C:\\\\Windows\\\\Vss\\\\*\",\n \"C:\\\\Windows\\\\Web\\\\*\",\n \"C:\\\\Windows\\\\servicing\\\\*\",\n \"C:\\\\Windows\\\\CbsTemp\\\\*\",\n \"C:\\\\Windows\\\\Logs\\\\*\",\n \"C:\\\\Windows\\\\WaaS\\\\*\",\n \"C:\\\\Windows\\\\twain_32\\\\*\",\n \"C:\\\\Windows\\\\ShellExperiences\\\\*\",\n \"C:\\\\Windows\\\\ShellComponents\\\\*\",\n \"C:\\\\Windows\\\\PLA\\\\*\",\n \"C:\\\\Windows\\\\Migration\\\\*\",\n \"C:\\\\Windows\\\\debug\\\\*\",\n \"C:\\\\Windows\\\\Cursors\\\\*\",\n \"C:\\\\Windows\\\\Containers\\\\*\",\n \"C:\\\\Windows\\\\Boot\\\\*\",\n \"C:\\\\Windows\\\\bcastdvr\\\\*\",\n \"C:\\\\Windows\\\\assembly\\\\*\",\n \"C:\\\\Windows\\\\TextInput\\\\*\",\n \"C:\\\\Windows\\\\security\\\\*\",\n \"C:\\\\Windows\\\\schemas\\\\*\",\n \"C:\\\\Windows\\\\SchCache\\\\*\",\n \"C:\\\\Windows\\\\Resources\\\\*\",\n \"C:\\\\Windows\\\\rescache\\\\*\",\n \"C:\\\\Windows\\\\Provisioning\\\\*\",\n \"C:\\\\Windows\\\\PrintDialog\\\\*\",\n \"C:\\\\Windows\\\\PolicyDefinitions\\\\*\",\n \"C:\\\\Windows\\\\media\\\\*\",\n \"C:\\\\Windows\\\\Globalization\\\\*\",\n \"C:\\\\Windows\\\\L2Schemas\\\\*\",\n \"C:\\\\Windows\\\\LiveKernelReports\\\\*\",\n \"C:\\\\Windows\\\\ModemLogs\\\\*\",\n \"C:\\\\Windows\\\\ImmersiveControlPanel\\\\*\",\n \"C:\\\\$Recycle.Bin\\\\*\") and\n not process.parent.executable : (\"C:\\\\WINDOWS\\\\System32\\\\DriverStore\\\\FileRepository\\\\*\\\\igfxCUIService*.exe\",\n \"C:\\\\Windows\\\\System32\\\\spacedeskService.exe\",\n \"C:\\\\Program Files\\\\Dell\\\\SupportAssistAgent\\\\SRE\\\\SRE.exe\") and\n not (process.name : \"rundll32.exe\" and process.args : (\"uxtheme.dll,#64\", \"PRINTUI.DLL,PrintUIEntry\"))\n", "risk_score": 47, "rule_id": "cff92c41-2225-4763-b4ce-6f71e5bda5e6", "severity": "medium", @@ -26,5 +26,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_register_server_program_connecting_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_register_server_program_connecting_to_the_internet.json index 3bebf4b415506..ca919d06e34a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_register_server_program_connecting_to_the_internet.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_register_server_program_connecting_to_the_internet.json @@ -15,7 +15,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Network Connection via Registration Utility", - "query": "sequence by process.entity_id\n [process where (process.name : \"regsvr32.exe\" or process.name : \"regsvr64.exe\" or\n process.name : \"RegAsm.exe\" or process.name : \"RegSvcs.exe\") and\n event.type == \"start\"]\n [network where (process.name : \"regsvr32.exe\" or process.name : \"regsvr64.exe\" or\n process.name : \"RegAsm.exe\" or process.name : \"RegSvcs.exe\") and\n not cidrmatch(destination.ip, \"10.0.0.0/8\", \"169.254.169.254\", \"172.16.0.0/12\", \"192.168.0.0/16\")]\n", + "query": "sequence by process.entity_id\n [process where event.type == \"start\" and\n process.name : (\"regsvr32.exe\", \"RegAsm.exe\", \"RegSvcs.exe\") and\n not (\n user.id == \"S-1-5-18\" and\n (process.parent.name : \"msiexec.exe\" or process.parent.executable : (\"C:\\\\Program Files (x86)\\\\*.exe\", \"C:\\\\Program Files\\\\*.exe\"))\n )\n ]\n [network where process.name : (\"regsvr32.exe\", \"RegAsm.exe\", \"RegSvcs.exe\") and\n not cidrmatch(destination.ip, \"10.0.0.0/8\", \"169.254.169.254\", \"172.16.0.0/12\", \"192.168.0.0/16\") and network.protocol != \"dns\"]\n", "risk_score": 21, "rule_id": "fb02b8d3-71ee-4af1-bacd-215d23f17efa", "severity": "low", @@ -60,5 +60,5 @@ } ], "type": "eql", - "version": 7 + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_suspicious_pdf_reader.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_suspicious_pdf_reader.json index c637ac93d3d6d..e4e58e89c7d38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_suspicious_pdf_reader.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_suspicious_pdf_reader.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious PDF Reader Child Process", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:(AcroRd32.exe or Acrobat.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)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : (\"AcroRd32.exe\",\n \"Acrobat.exe\",\n \"FoxitPhantomPDF.exe\",\n \"FoxitReader.exe\") and\n process.name : (\"arp.exe\", \"dsquery.exe\", \"dsget.exe\", \"gpresult.exe\", \"hostname.exe\", \"ipconfig.exe\", \"nbtstat.exe\",\n \"net.exe\", \"net1.exe\", \"netsh.exe\", \"netstat.exe\", \"nltest.exe\", \"ping.exe\", \"qprocess.exe\",\n \"quser.exe\", \"qwinsta.exe\", \"reg.exe\", \"sc.exe\", \"systeminfo.exe\", \"tasklist.exe\", \"tracert.exe\",\n \"whoami.exe\", \"bginfo.exe\", \"cdb.exe\", \"cmstp.exe\", \"csi.exe\", \"dnx.exe\", \"fsi.exe\", \"ieexec.exe\",\n \"iexpress.exe\", \"installutil.exe\", \"Microsoft.Workflow.Compiler.exe\", \"msbuild.exe\", \"mshta.exe\",\n \"msxsl.exe\", \"odbcconf.exe\", \"rcsi.exe\", \"regsvr32.exe\", \"xwizard.exe\", \"atbroker.exe\",\n \"forfiles.exe\", \"schtasks.exe\", \"regasm.exe\", \"regsvcs.exe\", \"cmd.exe\", \"cscript.exe\",\n \"powershell.exe\", \"pwsh.exe\", \"wmic.exe\", \"wscript.exe\", \"bitsadmin.exe\", \"certutil.exe\", \"ftp.exe\")\n", "risk_score": 21, "rule_id": "53a26770-9cbd-40c5-8b57-61d01a325e14", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 6 + "type": "eql", + "version": 7 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_compiled_html_file.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_compiled_html_file.json index 53d070e048681..efc3884b417fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_compiled_html_file.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_compiled_html_file.json @@ -12,13 +12,13 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Process Activity via Compiled HTML File", - "query": "event.category:process and event.type:(start or process_started) and process.name:hh.exe", - "risk_score": 21, + "query": "process where event.type in (\"start\", \"process_started\") and \n process.parent.name : \"hh.exe\" and \n process.name : (\"mshta.exe\", \"cmd.exe\", \"powershell.exe\", \"pwsh.exe\", \"cscript.exe\", \"wscript.exe\")\n", + "risk_score": 47, "rule_id": "e3343ab9-4245-4715-b344-e11c56b0a47f", - "severity": "low", + "severity": "medium", "tags": [ "Elastic", "Host", @@ -60,6 +60,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_hidden_shell_conhost.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_hidden_shell_conhost.json index c69f0d5d41755..4b5e38d65e43b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_hidden_shell_conhost.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_hidden_shell_conhost.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Conhost Spawned By Suspicious Parent Process", - "query": "event.category:process and event.type:(start or process_started) and process.name:conhost.exe and process.parent.name:(svchost.exe or lsass.exe or services.exe or smss.exe or winlogon.exe or explorer.exe or dllhost.exe or rundll32.exe or regsvr32.exe or userinit.exe or wininit.exe or spoolsv.exe or wermgr.exe or csrss.exe or ctfmon.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"conhost.exe\" and\n process.parent.name : (\"svchost.exe\", \"lsass.exe\", \"services.exe\", \"smss.exe\", \"winlogon.exe\", \"explorer.exe\",\n \"dllhost.exe\", \"rundll32.exe\", \"regsvr32.exe\", \"userinit.exe\", \"wininit.exe\", \"spoolsv.exe\",\n \"wermgr.exe\", \"csrss.exe\", \"ctfmon.exe\")\n", "references": [ "https://www.fireeye.com/blog/threat-research/2017/08/monitoring-windows-console-activity-part-one.html" ], @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_net_com_assemblies.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_net_com_assemblies.json deleted file mode 100644 index fcda4aa7e3c28..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_net_com_assemblies.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "RegSvcs.exe and RegAsm.exe are Windows command line utilities that are used to register .NET Component Object Model (COM) assemblies. Adversaries can use RegSvcs.exe and RegAsm.exe to proxy execution of code through a trusted Windows utility.", - "from": "now-9m", - "index": [ - "winlogbeat-*", - "logs-endpoint.events.*", - "logs-windows.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Execution via Regsvcs/Regasm", - "query": "event.category:process and event.type:(start or process_started) and process.name:(RegAsm.exe or RegSvcs.exe)", - "risk_score": 21, - "rule_id": "47f09343-8d1f-4bb5-8bb0-00c9d18f5010", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Execution" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0002", - "name": "Execution", - "reference": "https://attack.mitre.org/tactics/TA0002/" - }, - "technique": [] - }, - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0005", - "name": "Defense Evasion", - "reference": "https://attack.mitre.org/tactics/TA0005/" - }, - "technique": [ - { - "id": "T1218", - "name": "Signed Binary Proxy Execution", - "reference": "https://attack.mitre.org/techniques/T1218/", - "subtechnique": [ - { - "id": "T1218.009", - "name": "Regsvcs/Regasm", - "reference": "https://attack.mitre.org/techniques/T1218/009/" - } - ] - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_xp_cmdshell_mssql_stored_procedure.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_xp_cmdshell_mssql_stored_procedure.json index e34469bdde78a..e5be955247bd0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_xp_cmdshell_mssql_stored_procedure.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/execution_via_xp_cmdshell_mssql_stored_procedure.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Execution via MSSQL xp_cmdshell Stored Procedure", - "query": "event.category:process and event.type:(start or process_started) and process.name:cmd.exe and process.parent.name:sqlservr.exe", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : \"cmd.exe\" and process.parent.name : \"sqlservr.exe\"\n", "risk_score": 73, "rule_id": "4ed493fc-d637-4a36-80ff-ac84937e5461", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json index 86ef5c6d8c131..33534d777272d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudtrail_logging_updated.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudTrail Log Updated", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:UpdateTrail and event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:cloudtrail.amazonaws.com and event.action:UpdateTrail and event.outcome:success", "references": [ "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_UpdateTrail.html", "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/update-trail.html" @@ -73,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json index 2af9118b9f0a2..1fa046a285a4d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_group_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudWatch Log Group Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteLogGroup and event.dataset:aws.cloudtrail and event.provider:logs.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:logs.amazonaws.com and event.action:DeleteLogGroup and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-group.html", "https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogGroup.html" @@ -73,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json index 10ac9f12af19c..800e576e22c10 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_cloudwatch_log_stream_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS CloudWatch Log Stream Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteLogStream and event.dataset:aws.cloudtrail and event.provider:logs.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:logs.amazonaws.com and event.action:DeleteLogStream and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-stream.html", "https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogStream.html" @@ -73,5 +73,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json index d766d0dd3a94c..944814f1b47d1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_ec2_disable_ebs_encryption.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS EC2 Encryption Disabled", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DisableEbsEncryptionByDefault and event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.action:DisableEbsEncryptionByDefault and event.outcome:success", "references": [ "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html", "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/disable-ebs-encryption-by-default.html", @@ -59,5 +59,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json index 1ae1dae1a164b..e5875576c5190 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_storage_bucket_deleted.json @@ -14,7 +14,7 @@ "license": "Elastic License v2", "name": "GCP Storage Bucket Deletion", "note": "The GCP Filebeat module must be enabled to use this rule.", - "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:storage.buckets.delete", + "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:\"storage.buckets.delete\"", "references": [ "https://cloud.google.com/storage/docs/key-terms#buckets" ], @@ -48,5 +48,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_virtual_private_cloud_route_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_virtual_private_cloud_route_created.json index a5d94832647cb..4e21cb82c4cd6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_virtual_private_cloud_route_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_gcp_virtual_private_cloud_route_created.json @@ -14,7 +14,7 @@ "license": "Elastic License v2", "name": "GCP Virtual Private Cloud Route Creation", "note": "The GCP Filebeat module must be enabled to use this rule.", - "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:(v*.compute.routes.insert or beta.compute.routes.insert)", + "query": "event.dataset:(googlecloud.audit or gcp.audit) and event.action:(v*.compute.routes.insert or \"beta.compute.routes.insert\")", "references": [ "https://cloud.google.com/vpc/docs/routes", "https://cloud.google.com/vpc/docs/using-routes" @@ -32,5 +32,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_hosts_file_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_hosts_file_modified.json index 8885b30d8be8f..d202cc6e84ee9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_hosts_file_modified.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_hosts_file_modified.json @@ -10,11 +10,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Hosts File Modified", "note": "For Windows systems using Auditbeat, this rule requires adding 'C:/Windows/System32/drivers/etc' as an additional path in the 'file_integrity' module of auditbeat.yml.", - "query": "event.category:file and event.type:(change or creation) and file.path:(\"/private/etc/hosts\" or \"/etc/hosts\" or \"C:\\Windows\\System32\\drivers\\etc\\hosts\")", + "query": "file where event.type in (\"change\", \"creation\") and\n file.path : (\"/private/etc/hosts\", \"/etc/hosts\", \"?:\\\\Windows\\\\System32\\\\drivers\\\\etc\\\\hosts\") \n", "references": [ "https://www.elastic.co/guide/en/beats/auditbeat/current/auditbeat-reference-yml.html" ], @@ -55,6 +55,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json index 3783f9ab1f00f..1f0d48e2ecbc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_deactivate_mfa_device.json @@ -1,6 +1,7 @@ { "author": [ - "Elastic" + "Elastic", + "Austin Songer" ], "description": "Identifies the deactivation of a specified multi-factor authentication (MFA) device and removes it from association with the user name for which it was originally enabled. In AWS Identity and Access Management (IAM), a device must be deactivated before it can be deleted.", "false_positives": [ @@ -16,7 +17,7 @@ "license": "Elastic License v2", "name": "AWS IAM Deactivation of MFA Device", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeactivateMFADevice and event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.action:(DeactivateMFADevice or DeleteVirtualMFADevice) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/deactivate-mfa-device.html", "https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeactivateMFADevice.html" @@ -51,5 +52,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json index 02dcb16607a0f..ebe8df9c785ca 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_iam_group_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS IAM Group Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:DeleteGroup and event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.action:DeleteGroup and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/delete-group.html", "https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteGroup.html" @@ -51,5 +51,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_cluster_deletion.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_cluster_deletion.json index 3a265298e1414..b8c81e467fabb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_cluster_deletion.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_cluster_deletion.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS RDS Cluster Deletion", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(DeleteDBCluster or DeleteGlobalCluster) and event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.action:(DeleteDBCluster or DeleteGlobalCluster) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-db-cluster.html", "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteDBCluster.html", @@ -53,5 +53,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json index b30da3e0a912b..bd56daf3c6c9c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_rds_instance_cluster_stoppage.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS RDS Instance/Cluster Stoppage", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(StopDBCluster or StopDBInstance) and event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.action:(StopDBCluster or StopDBInstance) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/stop-db-cluster.html", "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_StopDBCluster.html", @@ -53,5 +53,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_volume_shadow_copy_deletion_via_vssadmin.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_volume_shadow_copy_deletion_via_vssadmin.json index cfe0632b28728..f0ac38e98441e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_volume_shadow_copy_deletion_via_vssadmin.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/impact_volume_shadow_copy_deletion_via_vssadmin.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Volume Shadow Copy Deletion via VssAdmin", - "query": "event.category:process and event.type:(start or process_started) and process.name:vssadmin.exe and process.args:(delete and shadows)", + "query": "process where event.type in (\"start\", \"process_started\") and\n (process.name : \"vssadmin.exe\" or process.pe.original_file_name == \"VSSADMIN.EXE\") and\n process.args : \"delete\" and process.args : \"shadows\"\n", "risk_score": 73, "rule_id": "b5ea4bfe-a1b2-421f-9d47-22a75a6f2921", "severity": "high", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/index.ts index 081b0dd288c7b..e910b1a10f586 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/index.ts @@ -32,530 +32,519 @@ import rule19 from './apm_405_response_method_not_allowed.json'; import rule20 from './apm_null_user_agent.json'; import rule21 from './apm_sqlmap_user_agent.json'; import rule22 from './command_and_control_dns_directly_to_the_internet.json'; -import rule23 from './command_and_control_ftp_file_transfer_protocol_activity_to_the_internet.json'; -import rule24 from './command_and_control_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; -import rule25 from './command_and_control_nat_traversal_port_activity.json'; -import rule26 from './command_and_control_port_26_activity.json'; -import rule27 from './command_and_control_port_8000_activity_to_the_internet.json'; -import rule28 from './command_and_control_pptp_point_to_point_tunneling_protocol_activity.json'; -import rule29 from './command_and_control_proxy_port_activity_to_the_internet.json'; -import rule30 from './command_and_control_rdp_remote_desktop_protocol_from_the_internet.json'; -import rule31 from './command_and_control_smtp_to_the_internet.json'; -import rule32 from './command_and_control_sql_server_port_activity_to_the_internet.json'; -import rule33 from './command_and_control_ssh_secure_shell_from_the_internet.json'; -import rule34 from './command_and_control_ssh_secure_shell_to_the_internet.json'; -import rule35 from './command_and_control_telnet_port_activity.json'; -import rule36 from './command_and_control_tor_activity_to_the_internet.json'; -import rule37 from './command_and_control_vnc_virtual_network_computing_from_the_internet.json'; -import rule38 from './command_and_control_vnc_virtual_network_computing_to_the_internet.json'; -import rule39 from './credential_access_tcpdump_activity.json'; -import rule40 from './defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json'; -import rule41 from './defense_evasion_clearing_windows_event_logs.json'; -import rule42 from './defense_evasion_delete_volume_usn_journal_with_fsutil.json'; -import rule43 from './defense_evasion_deleting_backup_catalogs_with_wbadmin.json'; -import rule44 from './defense_evasion_disable_windows_firewall_rules_with_netsh.json'; -import rule45 from './defense_evasion_encoding_or_decoding_files_via_certutil.json'; -import rule46 from './defense_evasion_execution_via_trusted_developer_utilities.json'; -import rule47 from './defense_evasion_misc_lolbin_connecting_to_the_internet.json'; -import rule48 from './defense_evasion_msbuild_making_network_connections.json'; -import rule49 from './defense_evasion_unusual_network_connection_via_rundll32.json'; -import rule50 from './defense_evasion_unusual_process_network_connection.json'; -import rule51 from './defense_evasion_via_filter_manager.json'; -import rule52 from './defense_evasion_volume_shadow_copy_deletion_via_wmic.json'; -import rule53 from './discovery_process_discovery_via_tasklist_command.json'; -import rule54 from './discovery_whoami_command_activity.json'; -import rule55 from './discovery_whoami_commmand.json'; -import rule56 from './endgame_adversary_behavior_detected.json'; -import rule57 from './endgame_cred_dumping_detected.json'; -import rule58 from './endgame_cred_dumping_prevented.json'; -import rule59 from './endgame_cred_manipulation_detected.json'; -import rule60 from './endgame_cred_manipulation_prevented.json'; -import rule61 from './endgame_exploit_detected.json'; -import rule62 from './endgame_exploit_prevented.json'; -import rule63 from './endgame_malware_detected.json'; -import rule64 from './endgame_malware_prevented.json'; -import rule65 from './endgame_permission_theft_detected.json'; -import rule66 from './endgame_permission_theft_prevented.json'; -import rule67 from './endgame_process_injection_detected.json'; -import rule68 from './endgame_process_injection_prevented.json'; -import rule69 from './endgame_ransomware_detected.json'; -import rule70 from './endgame_ransomware_prevented.json'; -import rule71 from './execution_command_prompt_connecting_to_the_internet.json'; -import rule72 from './execution_command_shell_started_by_powershell.json'; -import rule73 from './execution_command_shell_started_by_svchost.json'; -import rule74 from './execution_html_help_executable_program_connecting_to_the_internet.json'; -import rule75 from './execution_psexec_lateral_movement_command.json'; -import rule76 from './execution_register_server_program_connecting_to_the_internet.json'; -import rule77 from './execution_via_compiled_html_file.json'; -import rule78 from './impact_volume_shadow_copy_deletion_via_vssadmin.json'; -import rule79 from './initial_access_rdp_remote_desktop_protocol_to_the_internet.json'; -import rule80 from './initial_access_rpc_remote_procedure_call_from_the_internet.json'; -import rule81 from './initial_access_rpc_remote_procedure_call_to_the_internet.json'; -import rule82 from './initial_access_script_executing_powershell.json'; -import rule83 from './initial_access_smb_windows_file_sharing_activity_to_the_internet.json'; -import rule84 from './initial_access_suspicious_ms_office_child_process.json'; -import rule85 from './initial_access_suspicious_ms_outlook_child_process.json'; -import rule86 from './lateral_movement_direct_outbound_smb_connection.json'; -import rule87 from './lateral_movement_local_service_commands.json'; -import rule88 from './linux_hping_activity.json'; -import rule89 from './linux_iodine_activity.json'; -import rule90 from './linux_mknod_activity.json'; -import rule91 from './linux_netcat_network_connection.json'; -import rule92 from './linux_nmap_activity.json'; -import rule93 from './linux_nping_activity.json'; -import rule94 from './linux_process_started_in_temp_directory.json'; -import rule95 from './linux_socat_activity.json'; -import rule96 from './linux_strace_activity.json'; -import rule97 from './persistence_adobe_hijack_persistence.json'; -import rule98 from './persistence_kernel_module_activity.json'; -import rule99 from './persistence_local_scheduled_task_commands.json'; -import rule100 from './persistence_priv_escalation_via_accessibility_features.json'; -import rule101 from './persistence_shell_activity_by_web_server.json'; -import rule102 from './persistence_system_shells_via_services.json'; -import rule103 from './persistence_user_account_creation.json'; -import rule104 from './persistence_via_application_shimming.json'; -import rule105 from './privilege_escalation_unusual_parentchild_relationship.json'; -import rule106 from './defense_evasion_modification_of_boot_config.json'; -import rule107 from './privilege_escalation_uac_bypass_event_viewer.json'; -import rule108 from './defense_evasion_msxsl_network.json'; -import rule109 from './discovery_net_command_system_account.json'; -import rule110 from './command_and_control_certutil_network_connection.json'; -import rule111 from './defense_evasion_cve_2020_0601.json'; -import rule112 from './credential_access_credential_dumping_msbuild.json'; -import rule113 from './defense_evasion_execution_msbuild_started_by_office_app.json'; -import rule114 from './defense_evasion_execution_msbuild_started_by_script.json'; -import rule115 from './defense_evasion_execution_msbuild_started_by_system_process.json'; -import rule116 from './defense_evasion_execution_msbuild_started_renamed.json'; -import rule117 from './defense_evasion_execution_msbuild_started_unusal_process.json'; -import rule118 from './defense_evasion_injection_msbuild.json'; -import rule119 from './execution_via_net_com_assemblies.json'; -import rule120 from './ml_linux_anomalous_network_activity.json'; -import rule121 from './ml_linux_anomalous_network_port_activity.json'; -import rule122 from './ml_linux_anomalous_network_service.json'; -import rule123 from './ml_linux_anomalous_network_url_activity.json'; -import rule124 from './ml_linux_anomalous_process_all_hosts.json'; -import rule125 from './ml_linux_anomalous_user_name.json'; -import rule126 from './ml_packetbeat_dns_tunneling.json'; -import rule127 from './ml_packetbeat_rare_dns_question.json'; -import rule128 from './ml_packetbeat_rare_server_domain.json'; -import rule129 from './ml_packetbeat_rare_urls.json'; -import rule130 from './ml_packetbeat_rare_user_agent.json'; -import rule131 from './ml_rare_process_by_host_linux.json'; -import rule132 from './ml_rare_process_by_host_windows.json'; -import rule133 from './ml_suspicious_login_activity.json'; -import rule134 from './ml_windows_anomalous_network_activity.json'; -import rule135 from './ml_windows_anomalous_path_activity.json'; -import rule136 from './ml_windows_anomalous_process_all_hosts.json'; -import rule137 from './ml_windows_anomalous_process_creation.json'; -import rule138 from './ml_windows_anomalous_script.json'; -import rule139 from './ml_windows_anomalous_service.json'; -import rule140 from './ml_windows_anomalous_user_name.json'; -import rule141 from './ml_windows_rare_user_runas_event.json'; -import rule142 from './ml_windows_rare_user_type10_remote_login.json'; -import rule143 from './execution_suspicious_pdf_reader.json'; -import rule144 from './privilege_escalation_sudoers_file_mod.json'; -import rule145 from './defense_evasion_iis_httplogging_disabled.json'; -import rule146 from './execution_python_tty_shell.json'; -import rule147 from './execution_perl_tty_shell.json'; -import rule148 from './defense_evasion_base16_or_base32_encoding_or_decoding_activity.json'; -import rule149 from './defense_evasion_base64_encoding_or_decoding_activity.json'; -import rule150 from './defense_evasion_hex_encoding_or_decoding_activity.json'; -import rule151 from './defense_evasion_file_mod_writable_dir.json'; -import rule152 from './defense_evasion_disable_selinux_attempt.json'; -import rule153 from './discovery_kernel_module_enumeration.json'; -import rule154 from './lateral_movement_telnet_network_activity_external.json'; -import rule155 from './lateral_movement_telnet_network_activity_internal.json'; -import rule156 from './privilege_escalation_setuid_setgid_bit_set_via_chmod.json'; -import rule157 from './defense_evasion_attempt_to_disable_iptables_or_firewall.json'; -import rule158 from './defense_evasion_kernel_module_removal.json'; -import rule159 from './defense_evasion_attempt_to_disable_syslog_service.json'; -import rule160 from './defense_evasion_file_deletion_via_shred.json'; -import rule161 from './discovery_virtual_machine_fingerprinting.json'; -import rule162 from './defense_evasion_hidden_file_dir_tmp.json'; -import rule163 from './defense_evasion_deletion_of_bash_command_line_history.json'; -import rule164 from './impact_cloudwatch_log_group_deletion.json'; -import rule165 from './impact_cloudwatch_log_stream_deletion.json'; -import rule166 from './impact_rds_instance_cluster_stoppage.json'; -import rule167 from './persistence_attempt_to_deactivate_mfa_for_okta_user_account.json'; -import rule168 from './persistence_rds_cluster_creation.json'; -import rule169 from './credential_access_attempted_bypass_of_okta_mfa.json'; -import rule170 from './defense_evasion_waf_acl_deletion.json'; -import rule171 from './impact_attempt_to_revoke_okta_api_token.json'; -import rule172 from './impact_iam_group_deletion.json'; -import rule173 from './impact_possible_okta_dos_attack.json'; -import rule174 from './impact_rds_cluster_deletion.json'; -import rule175 from './initial_access_suspicious_activity_reported_by_okta_user.json'; -import rule176 from './okta_attempt_to_deactivate_okta_policy.json'; -import rule177 from './okta_attempt_to_deactivate_okta_policy_rule.json'; -import rule178 from './okta_attempt_to_modify_okta_network_zone.json'; -import rule179 from './okta_attempt_to_modify_okta_policy.json'; -import rule180 from './okta_attempt_to_modify_okta_policy_rule.json'; -import rule181 from './okta_threat_detected_by_okta_threatinsight.json'; -import rule182 from './persistence_administrator_privileges_assigned_to_okta_group.json'; -import rule183 from './persistence_attempt_to_create_okta_api_token.json'; -import rule184 from './persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json'; -import rule185 from './defense_evasion_cloudtrail_logging_deleted.json'; -import rule186 from './defense_evasion_ec2_network_acl_deletion.json'; -import rule187 from './impact_iam_deactivate_mfa_device.json'; -import rule188 from './defense_evasion_s3_bucket_configuration_deletion.json'; -import rule189 from './defense_evasion_guardduty_detector_deletion.json'; -import rule190 from './okta_attempt_to_delete_okta_policy.json'; -import rule191 from './credential_access_iam_user_addition_to_group.json'; -import rule192 from './persistence_ec2_network_acl_creation.json'; -import rule193 from './impact_ec2_disable_ebs_encryption.json'; -import rule194 from './persistence_iam_group_creation.json'; -import rule195 from './defense_evasion_waf_rule_or_rule_group_deletion.json'; -import rule196 from './collection_cloudtrail_logging_created.json'; -import rule197 from './defense_evasion_cloudtrail_logging_suspended.json'; -import rule198 from './impact_cloudtrail_logging_updated.json'; -import rule199 from './initial_access_console_login_root.json'; -import rule200 from './defense_evasion_cloudwatch_alarm_deletion.json'; -import rule201 from './defense_evasion_ec2_flow_log_deletion.json'; -import rule202 from './defense_evasion_configuration_recorder_stopped.json'; -import rule203 from './exfiltration_ec2_snapshot_change_activity.json'; -import rule204 from './defense_evasion_config_service_rule_deletion.json'; -import rule205 from './okta_attempt_to_modify_or_delete_application_sign_on_policy.json'; -import rule206 from './command_and_control_download_rar_powershell_from_internet.json'; -import rule207 from './initial_access_password_recovery.json'; -import rule208 from './command_and_control_cobalt_strike_beacon.json'; -import rule209 from './command_and_control_fin7_c2_behavior.json'; -import rule210 from './command_and_control_halfbaked_beacon.json'; -import rule211 from './credential_access_secretsmanager_getsecretvalue.json'; -import rule212 from './initial_access_via_system_manager.json'; -import rule213 from './privilege_escalation_root_login_without_mfa.json'; -import rule214 from './privilege_escalation_updateassumerolepolicy.json'; -import rule215 from './impact_hosts_file_modified.json'; -import rule216 from './elastic_endpoint_security.json'; -import rule217 from './external_alerts.json'; -import rule218 from './initial_access_login_failures.json'; -import rule219 from './initial_access_login_location.json'; -import rule220 from './initial_access_login_sessions.json'; -import rule221 from './initial_access_login_time.json'; -import rule222 from './ml_cloudtrail_error_message_spike.json'; -import rule223 from './ml_cloudtrail_rare_error_code.json'; -import rule224 from './ml_cloudtrail_rare_method_by_city.json'; -import rule225 from './ml_cloudtrail_rare_method_by_country.json'; -import rule226 from './ml_cloudtrail_rare_method_by_user.json'; -import rule227 from './credential_access_aws_iam_assume_role_brute_force.json'; -import rule228 from './credential_access_okta_brute_force_or_password_spraying.json'; -import rule229 from './initial_access_unusual_dns_service_children.json'; -import rule230 from './initial_access_unusual_dns_service_file_writes.json'; -import rule231 from './lateral_movement_dns_server_overflow.json'; -import rule232 from './credential_access_root_console_failure_brute_force.json'; -import rule233 from './initial_access_unsecure_elasticsearch_node.json'; -import rule234 from './credential_access_domain_backup_dpapi_private_keys.json'; -import rule235 from './persistence_gpo_schtask_service_creation.json'; -import rule236 from './credential_access_credentials_keychains.json'; -import rule237 from './credential_access_kerberosdump_kcc.json'; -import rule238 from './defense_evasion_attempt_del_quarantine_attrib.json'; -import rule239 from './execution_suspicious_psexesvc.json'; -import rule240 from './execution_via_xp_cmdshell_mssql_stored_procedure.json'; -import rule241 from './privilege_escalation_printspooler_service_suspicious_file.json'; -import rule242 from './privilege_escalation_printspooler_suspicious_spl_file.json'; -import rule243 from './defense_evasion_azure_diagnostic_settings_deletion.json'; -import rule244 from './execution_command_virtual_machine.json'; -import rule245 from './execution_via_hidden_shell_conhost.json'; -import rule246 from './impact_resource_group_deletion.json'; -import rule247 from './persistence_via_telemetrycontroller_scheduledtask_hijack.json'; -import rule248 from './persistence_via_update_orchestrator_service_hijack.json'; -import rule249 from './collection_update_event_hub_auth_rule.json'; -import rule250 from './credential_access_iis_apppoolsa_pwd_appcmd.json'; -import rule251 from './credential_access_iis_connectionstrings_dumping.json'; -import rule252 from './defense_evasion_event_hub_deletion.json'; -import rule253 from './defense_evasion_firewall_policy_deletion.json'; -import rule254 from './defense_evasion_sdelete_like_filename_rename.json'; -import rule255 from './lateral_movement_remote_ssh_login_enabled.json'; -import rule256 from './persistence_azure_automation_account_created.json'; -import rule257 from './persistence_azure_automation_runbook_created_or_modified.json'; -import rule258 from './persistence_azure_automation_webhook_created.json'; -import rule259 from './privilege_escalation_uac_bypass_diskcleanup_hijack.json'; -import rule260 from './credential_access_attempts_to_brute_force_okta_user_account.json'; -import rule261 from './credential_access_storage_account_key_regenerated.json'; -import rule262 from './defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json'; -import rule263 from './defense_evasion_system_critical_proc_abnormal_file_activity.json'; -import rule264 from './defense_evasion_unusual_system_vp_child_program.json'; -import rule265 from './discovery_blob_container_access_mod.json'; -import rule266 from './persistence_mfa_disabled_for_azure_user.json'; -import rule267 from './persistence_user_added_as_owner_for_azure_application.json'; -import rule268 from './persistence_user_added_as_owner_for_azure_service_principal.json'; -import rule269 from './defense_evasion_dotnet_compiler_parent_process.json'; -import rule270 from './defense_evasion_suspicious_managedcode_host_process.json'; -import rule271 from './execution_command_shell_started_by_unusual_process.json'; -import rule272 from './defense_evasion_masquerading_as_elastic_endpoint_process.json'; -import rule273 from './defense_evasion_masquerading_suspicious_werfault_childproc.json'; -import rule274 from './defense_evasion_masquerading_werfault.json'; -import rule275 from './credential_access_key_vault_modified.json'; -import rule276 from './credential_access_mimikatz_memssp_default_logs.json'; -import rule277 from './defense_evasion_code_injection_conhost.json'; -import rule278 from './defense_evasion_network_watcher_deletion.json'; -import rule279 from './initial_access_external_guest_user_invite.json'; -import rule280 from './defense_evasion_masquerading_renamed_autoit.json'; -import rule281 from './impact_azure_automation_runbook_deleted.json'; -import rule282 from './initial_access_consent_grant_attack_via_azure_registered_application.json'; -import rule283 from './persistence_azure_conditional_access_policy_modified.json'; -import rule284 from './persistence_azure_privileged_identity_management_role_modified.json'; -import rule285 from './command_and_control_teamviewer_remote_file_copy.json'; -import rule286 from './defense_evasion_installutil_beacon.json'; -import rule287 from './defense_evasion_mshta_beacon.json'; -import rule288 from './defense_evasion_network_connection_from_windows_binary.json'; -import rule289 from './defense_evasion_rundll32_no_arguments.json'; -import rule290 from './defense_evasion_suspicious_scrobj_load.json'; -import rule291 from './defense_evasion_suspicious_wmi_script.json'; -import rule292 from './execution_ms_office_written_file.json'; -import rule293 from './execution_pdf_written_file.json'; -import rule294 from './lateral_movement_cmd_service.json'; -import rule295 from './persistence_app_compat_shim.json'; -import rule296 from './command_and_control_remote_file_copy_desktopimgdownldr.json'; -import rule297 from './command_and_control_remote_file_copy_mpcmdrun.json'; -import rule298 from './defense_evasion_execution_suspicious_explorer_winword.json'; -import rule299 from './defense_evasion_suspicious_zoom_child_process.json'; -import rule300 from './ml_linux_anomalous_compiler_activity.json'; -import rule301 from './ml_linux_anomalous_kernel_module_arguments.json'; -import rule302 from './ml_linux_anomalous_sudo_activity.json'; -import rule303 from './ml_linux_system_information_discovery.json'; -import rule304 from './ml_linux_system_network_configuration_discovery.json'; -import rule305 from './ml_linux_system_network_connection_discovery.json'; -import rule306 from './ml_linux_system_process_discovery.json'; -import rule307 from './ml_linux_system_user_discovery.json'; -import rule308 from './discovery_post_exploitation_public_ip_reconnaissance.json'; -import rule309 from './initial_access_zoom_meeting_with_no_passcode.json'; -import rule310 from './defense_evasion_gcp_logging_sink_deletion.json'; -import rule311 from './defense_evasion_gcp_pub_sub_topic_deletion.json'; -import rule312 from './defense_evasion_gcp_firewall_rule_created.json'; -import rule313 from './defense_evasion_gcp_firewall_rule_deleted.json'; -import rule314 from './defense_evasion_gcp_firewall_rule_modified.json'; -import rule315 from './defense_evasion_gcp_logging_bucket_deletion.json'; -import rule316 from './defense_evasion_gcp_storage_bucket_permissions_modified.json'; -import rule317 from './impact_gcp_storage_bucket_deleted.json'; -import rule318 from './initial_access_gcp_iam_custom_role_creation.json'; -import rule319 from './persistence_gcp_iam_service_account_key_deletion.json'; -import rule320 from './persistence_gcp_key_created_for_service_account.json'; -import rule321 from './defense_evasion_gcp_storage_bucket_configuration_modified.json'; -import rule322 from './exfiltration_gcp_logging_sink_modification.json'; -import rule323 from './impact_gcp_iam_role_deletion.json'; -import rule324 from './impact_gcp_service_account_deleted.json'; -import rule325 from './impact_gcp_service_account_disabled.json'; -import rule326 from './impact_gcp_virtual_private_cloud_network_deleted.json'; -import rule327 from './impact_gcp_virtual_private_cloud_route_created.json'; -import rule328 from './impact_gcp_virtual_private_cloud_route_deleted.json'; -import rule329 from './ml_linux_anomalous_metadata_process.json'; -import rule330 from './ml_linux_anomalous_metadata_user.json'; -import rule331 from './ml_windows_anomalous_metadata_process.json'; -import rule332 from './ml_windows_anomalous_metadata_user.json'; -import rule333 from './persistence_gcp_service_account_created.json'; -import rule334 from './collection_gcp_pub_sub_subscription_creation.json'; -import rule335 from './collection_gcp_pub_sub_topic_creation.json'; -import rule336 from './defense_evasion_gcp_pub_sub_subscription_deletion.json'; -import rule337 from './persistence_azure_pim_user_added_global_admin.json'; -import rule338 from './command_and_control_cobalt_strike_default_teamserver_cert.json'; -import rule339 from './defense_evasion_enable_inbound_rdp_with_netsh.json'; -import rule340 from './defense_evasion_execution_lolbas_wuauclt.json'; -import rule341 from './privilege_escalation_unusual_svchost_childproc_childless.json'; -import rule342 from './lateral_movement_rdp_tunnel_plink.json'; -import rule343 from './privilege_escalation_uac_bypass_winfw_mmc_hijack.json'; -import rule344 from './persistence_ms_office_addins_file.json'; -import rule345 from './discovery_adfind_command_activity.json'; -import rule346 from './discovery_security_software_wmic.json'; -import rule347 from './execution_command_shell_via_rundll32.json'; -import rule348 from './execution_suspicious_cmd_wmi.json'; -import rule349 from './lateral_movement_via_startup_folder_rdp_smb.json'; -import rule350 from './privilege_escalation_uac_bypass_com_interface_icmluautil.json'; -import rule351 from './privilege_escalation_uac_bypass_mock_windir.json'; -import rule352 from './defense_evasion_potential_processherpaderping.json'; -import rule353 from './privilege_escalation_uac_bypass_dll_sideloading.json'; -import rule354 from './execution_shared_modules_local_sxs_dll.json'; -import rule355 from './privilege_escalation_uac_bypass_com_clipup.json'; -import rule356 from './initial_access_via_explorer_suspicious_child_parent_args.json'; -import rule357 from './execution_from_unusual_directory.json'; -import rule358 from './execution_from_unusual_path_cmdline.json'; -import rule359 from './credential_access_kerberoasting_unusual_process.json'; -import rule360 from './discovery_peripheral_device.json'; -import rule361 from './lateral_movement_mount_hidden_or_webdav_share_net.json'; -import rule362 from './defense_evasion_deleting_websvr_access_logs.json'; -import rule363 from './defense_evasion_log_files_deleted.json'; -import rule364 from './defense_evasion_timestomp_touch.json'; -import rule365 from './lateral_movement_dcom_hta.json'; -import rule366 from './lateral_movement_execution_via_file_shares_sequence.json'; -import rule367 from './privilege_escalation_uac_bypass_com_ieinstal.json'; -import rule368 from './command_and_control_common_webservices.json'; -import rule369 from './command_and_control_encrypted_channel_freesslcert.json'; -import rule370 from './defense_evasion_process_termination_followed_by_deletion.json'; -import rule371 from './lateral_movement_remote_file_copy_hidden_share.json'; -import rule372 from './attempt_to_deactivate_okta_network_zone.json'; -import rule373 from './attempt_to_delete_okta_network_zone.json'; -import rule374 from './lateral_movement_dcom_mmc20.json'; -import rule375 from './lateral_movement_dcom_shellwindow_shellbrowserwindow.json'; -import rule376 from './okta_attempt_to_deactivate_okta_application.json'; -import rule377 from './okta_attempt_to_delete_okta_application.json'; -import rule378 from './okta_attempt_to_delete_okta_policy_rule.json'; -import rule379 from './okta_attempt_to_modify_okta_application.json'; -import rule380 from './persistence_administrator_role_assigned_to_okta_user.json'; -import rule381 from './lateral_movement_executable_tool_transfer_smb.json'; -import rule382 from './command_and_control_dns_tunneling_nslookup.json'; -import rule383 from './lateral_movement_execution_from_tsclient_mup.json'; -import rule384 from './lateral_movement_rdp_sharprdp_target.json'; -import rule385 from './defense_evasion_clearing_windows_security_logs.json'; -import rule386 from './persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json'; -import rule387 from './execution_suspicious_short_program_name.json'; -import rule388 from './lateral_movement_incoming_wmi.json'; -import rule389 from './persistence_via_hidden_run_key_valuename.json'; -import rule390 from './credential_access_potential_ssh_bruteforce.json'; -import rule391 from './credential_access_promt_for_pwd_via_osascript.json'; -import rule392 from './lateral_movement_remote_services.json'; -import rule393 from './application_added_to_google_workspace_domain.json'; -import rule394 from './domain_added_to_google_workspace_trusted_domains.json'; -import rule395 from './execution_suspicious_image_load_wmi_ms_office.json'; -import rule396 from './execution_suspicious_powershell_imgload.json'; -import rule397 from './google_workspace_admin_role_deletion.json'; -import rule398 from './google_workspace_mfa_enforcement_disabled.json'; -import rule399 from './google_workspace_policy_modified.json'; -import rule400 from './mfa_disabled_for_google_workspace_organization.json'; -import rule401 from './persistence_evasion_registry_ifeo_injection.json'; -import rule402 from './persistence_google_workspace_admin_role_assigned_to_user.json'; -import rule403 from './persistence_google_workspace_custom_admin_role_created.json'; -import rule404 from './persistence_google_workspace_role_modified.json'; -import rule405 from './persistence_suspicious_image_load_scheduled_task_ms_office.json'; -import rule406 from './defense_evasion_masquerading_trusted_directory.json'; -import rule407 from './exfiltration_microsoft_365_exchange_transport_rule_creation.json'; -import rule408 from './initial_access_microsoft_365_exchange_safelinks_disabled.json'; -import rule409 from './microsoft_365_exchange_dkim_signing_config_disabled.json'; -import rule410 from './persistence_appcertdlls_registry.json'; -import rule411 from './persistence_appinitdlls_registry.json'; -import rule412 from './persistence_registry_uncommon.json'; -import rule413 from './persistence_run_key_and_startup_broad.json'; -import rule414 from './persistence_services_registry.json'; -import rule415 from './persistence_startup_folder_file_written_by_suspicious_process.json'; -import rule416 from './persistence_startup_folder_scripts.json'; -import rule417 from './persistence_suspicious_com_hijack_registry.json'; -import rule418 from './persistence_via_lsa_security_support_provider_registry.json'; -import rule419 from './defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json'; -import rule420 from './defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json'; -import rule421 from './defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json'; -import rule422 from './exfiltration_microsoft_365_exchange_transport_rule_mod.json'; -import rule423 from './initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json'; -import rule424 from './initial_access_microsoft_365_exchange_anti_phish_rule_mod.json'; -import rule425 from './lateral_movement_suspicious_rdp_client_imageload.json'; -import rule426 from './persistence_runtime_run_key_startup_susp_procs.json'; -import rule427 from './persistence_suspicious_scheduled_task_runtime.json'; -import rule428 from './defense_evasion_microsoft_365_exchange_dlp_policy_removed.json'; -import rule429 from './lateral_movement_scheduled_task_target.json'; -import rule430 from './persistence_microsoft_365_exchange_management_role_assignment.json'; -import rule431 from './persistence_microsoft_365_teams_guest_access_enabled.json'; -import rule432 from './credential_access_dump_registry_hives.json'; -import rule433 from './defense_evasion_scheduledjobs_at_protocol_enabled.json'; -import rule434 from './persistence_ms_outlook_vba_template.json'; -import rule435 from './persistence_suspicious_service_created_registry.json'; -import rule436 from './privilege_escalation_named_pipe_impersonation.json'; -import rule437 from './credential_access_cmdline_dump_tool.json'; -import rule438 from './credential_access_copy_ntds_sam_volshadowcp_cmdline.json'; -import rule439 from './credential_access_lsass_memdump_file_created.json'; -import rule440 from './lateral_movement_incoming_winrm_shell_execution.json'; -import rule441 from './lateral_movement_powershell_remoting_target.json'; -import rule442 from './defense_evasion_hide_encoded_executable_registry.json'; -import rule443 from './defense_evasion_port_forwarding_added_registry.json'; -import rule444 from './lateral_movement_rdp_enabled_registry.json'; -import rule445 from './privilege_escalation_printspooler_registry_copyfiles.json'; -import rule446 from './privilege_escalation_rogue_windir_environment_var.json'; -import rule447 from './initial_access_scripts_process_started_via_wmi.json'; -import rule448 from './command_and_control_iexplore_via_com.json'; -import rule449 from './command_and_control_remote_file_copy_scripts.json'; -import rule450 from './persistence_local_scheduled_task_scripting.json'; -import rule451 from './persistence_startup_folder_file_written_by_unsigned_process.json'; -import rule452 from './command_and_control_remote_file_copy_powershell.json'; -import rule453 from './credential_access_microsoft_365_brute_force_user_account_attempt.json'; -import rule454 from './microsoft_365_teams_custom_app_interaction_allowed.json'; -import rule455 from './persistence_microsoft_365_teams_external_access_enabled.json'; -import rule456 from './credential_access_microsoft_365_potential_password_spraying_attack.json'; -import rule457 from './defense_evasion_stop_process_service_threshold.json'; -import rule458 from './collection_winrar_encryption.json'; -import rule459 from './defense_evasion_unusual_dir_ads.json'; -import rule460 from './discovery_admin_recon.json'; -import rule461 from './discovery_file_dir_discovery.json'; -import rule462 from './discovery_net_view.json'; -import rule463 from './discovery_query_registry_via_reg.json'; -import rule464 from './discovery_remote_system_discovery_commands_windows.json'; -import rule465 from './persistence_via_windows_management_instrumentation_event_subscription.json'; -import rule466 from './execution_scripting_osascript_exec_followed_by_netcon.json'; -import rule467 from './execution_shell_execution_via_apple_scripting.json'; -import rule468 from './persistence_creation_change_launch_agents_file.json'; -import rule469 from './persistence_creation_modif_launch_deamon_sequence.json'; -import rule470 from './persistence_folder_action_scripts_runtime.json'; -import rule471 from './persistence_login_logout_hooks_defaults.json'; -import rule472 from './privilege_escalation_explicit_creds_via_scripting.json'; -import rule473 from './command_and_control_sunburst_c2_activity_detected.json'; -import rule474 from './defense_evasion_azure_application_credential_modification.json'; -import rule475 from './defense_evasion_azure_service_principal_addition.json'; -import rule476 from './defense_evasion_solarwinds_backdoor_service_disabled_via_registry.json'; -import rule477 from './execution_apt_solarwinds_backdoor_child_cmd_powershell.json'; -import rule478 from './execution_apt_solarwinds_backdoor_unusual_child_processes.json'; -import rule479 from './initial_access_azure_active_directory_powershell_signin.json'; -import rule480 from './collection_email_powershell_exchange_mailbox.json'; -import rule481 from './collection_persistence_powershell_exch_mailbox_activesync_add_device.json'; -import rule482 from './execution_scheduled_task_powershell_source.json'; -import rule483 from './persistence_docker_shortcuts_plist_modification.json'; -import rule484 from './persistence_evasion_hidden_local_account_creation.json'; -import rule485 from './persistence_finder_sync_plugin_pluginkit.json'; -import rule486 from './discovery_security_software_grep.json'; -import rule487 from './credential_access_cookies_chromium_browsers_debugging.json'; -import rule488 from './credential_access_ssh_backdoor_log.json'; -import rule489 from './persistence_credential_access_modify_auth_module_or_config.json'; -import rule490 from './persistence_credential_access_modify_ssh_binaries.json'; -import rule491 from './credential_access_collection_sensitive_files.json'; -import rule492 from './persistence_ssh_authorized_keys_modification.json'; -import rule493 from './defense_evasion_defender_disabled_via_registry.json'; -import rule494 from './defense_evasion_privacy_controls_tcc_database_modification.json'; -import rule495 from './execution_initial_access_suspicious_browser_childproc.json'; -import rule496 from './execution_script_via_automator_workflows.json'; -import rule497 from './persistence_modification_sublime_app_plugin_or_script.json'; -import rule498 from './privilege_escalation_applescript_with_admin_privs.json'; -import rule499 from './credential_access_dumping_keychain_security.json'; -import rule500 from './initial_access_azure_active_directory_high_risk_signin.json'; -import rule501 from './initial_access_suspicious_mac_ms_office_child_process.json'; -import rule502 from './credential_access_mitm_localhost_webproxy.json'; -import rule503 from './persistence_kde_autostart_modification.json'; -import rule504 from './persistence_user_account_added_to_privileged_group_ad.json'; -import rule505 from './defense_evasion_attempt_to_disable_gatekeeper.json'; -import rule506 from './defense_evasion_sandboxed_office_app_suspicious_zip_file.json'; -import rule507 from './persistence_emond_rules_file_creation.json'; -import rule508 from './persistence_emond_rules_process_execution.json'; -import rule509 from './discovery_users_domain_built_in_commands.json'; -import rule510 from './execution_pentest_eggshell_remote_admin_tool.json'; -import rule511 from './defense_evasion_install_root_certificate.json'; -import rule512 from './persistence_credential_access_authorization_plugin_creation.json'; -import rule513 from './persistence_directory_services_plugins_modification.json'; -import rule514 from './defense_evasion_modify_environment_launchctl.json'; -import rule515 from './defense_evasion_safari_config_change.json'; -import rule516 from './defense_evasion_apple_softupdates_modification.json'; -import rule517 from './persistence_cron_jobs_creation_and_runtime.json'; -import rule518 from './credential_access_mod_wdigest_security_provider.json'; -import rule519 from './credential_access_saved_creds_vaultcmd.json'; -import rule520 from './defense_evasion_file_creation_mult_extension.json'; -import rule521 from './execution_enumeration_via_wmiprvse.json'; -import rule522 from './execution_suspicious_jar_child_process.json'; -import rule523 from './persistence_shell_profile_modification.json'; -import rule524 from './persistence_suspicious_calendar_modification.json'; -import rule525 from './persistence_time_provider_mod.json'; -import rule526 from './privilege_escalation_exploit_adobe_acrobat_updater.json'; -import rule527 from './defense_evasion_sip_provider_mod.json'; -import rule528 from './execution_com_object_xwizard.json'; -import rule529 from './privilege_escalation_disable_uac_registry.json'; -import rule530 from './defense_evasion_unusual_ads_file_creation.json'; -import rule531 from './persistence_loginwindow_plist_modification.json'; -import rule532 from './persistence_periodic_tasks_file_mdofiy.json'; -import rule533 from './persistence_via_atom_init_file_modification.json'; -import rule534 from './privilege_escalation_lsa_auth_package.json'; -import rule535 from './privilege_escalation_port_monitor_print_pocessor_abuse.json'; -import rule536 from './credential_access_dumping_hashes_bi_cmds.json'; -import rule537 from './lateral_movement_mounting_smb_share.json'; -import rule538 from './privilege_escalation_echo_nopasswd_sudoers.json'; -import rule539 from './privilege_escalation_ld_preload_shared_object_modif.json'; -import rule540 from './privilege_escalation_root_crontab_filemod.json'; -import rule541 from './defense_evasion_create_mod_root_certificate.json'; -import rule542 from './privilege_escalation_sudo_buffer_overflow.json'; -import rule543 from './execution_installer_spawned_network_event.json'; -import rule544 from './initial_access_suspicious_ms_exchange_files.json'; -import rule545 from './initial_access_suspicious_ms_exchange_process.json'; -import rule546 from './initial_access_suspicious_ms_exchange_worker_child_process.json'; +import rule23 from './command_and_control_nat_traversal_port_activity.json'; +import rule24 from './command_and_control_port_26_activity.json'; +import rule25 from './command_and_control_rdp_remote_desktop_protocol_from_the_internet.json'; +import rule26 from './command_and_control_telnet_port_activity.json'; +import rule27 from './command_and_control_vnc_virtual_network_computing_from_the_internet.json'; +import rule28 from './command_and_control_vnc_virtual_network_computing_to_the_internet.json'; +import rule29 from './defense_evasion_adding_the_hidden_file_attribute_with_via_attribexe.json'; +import rule30 from './defense_evasion_clearing_windows_event_logs.json'; +import rule31 from './defense_evasion_delete_volume_usn_journal_with_fsutil.json'; +import rule32 from './defense_evasion_deleting_backup_catalogs_with_wbadmin.json'; +import rule33 from './defense_evasion_disable_windows_firewall_rules_with_netsh.json'; +import rule34 from './defense_evasion_encoding_or_decoding_files_via_certutil.json'; +import rule35 from './defense_evasion_misc_lolbin_connecting_to_the_internet.json'; +import rule36 from './defense_evasion_msbuild_making_network_connections.json'; +import rule37 from './defense_evasion_unusual_network_connection_via_rundll32.json'; +import rule38 from './defense_evasion_unusual_process_network_connection.json'; +import rule39 from './defense_evasion_via_filter_manager.json'; +import rule40 from './defense_evasion_volume_shadow_copy_deletion_via_wmic.json'; +import rule41 from './discovery_whoami_command_activity.json'; +import rule42 from './endgame_adversary_behavior_detected.json'; +import rule43 from './endgame_cred_dumping_detected.json'; +import rule44 from './endgame_cred_dumping_prevented.json'; +import rule45 from './endgame_cred_manipulation_detected.json'; +import rule46 from './endgame_cred_manipulation_prevented.json'; +import rule47 from './endgame_exploit_detected.json'; +import rule48 from './endgame_exploit_prevented.json'; +import rule49 from './endgame_malware_detected.json'; +import rule50 from './endgame_malware_prevented.json'; +import rule51 from './endgame_permission_theft_detected.json'; +import rule52 from './endgame_permission_theft_prevented.json'; +import rule53 from './endgame_process_injection_detected.json'; +import rule54 from './endgame_process_injection_prevented.json'; +import rule55 from './endgame_ransomware_detected.json'; +import rule56 from './endgame_ransomware_prevented.json'; +import rule57 from './execution_command_prompt_connecting_to_the_internet.json'; +import rule58 from './execution_command_shell_started_by_svchost.json'; +import rule59 from './execution_html_help_executable_program_connecting_to_the_internet.json'; +import rule60 from './execution_psexec_lateral_movement_command.json'; +import rule61 from './execution_register_server_program_connecting_to_the_internet.json'; +import rule62 from './execution_via_compiled_html_file.json'; +import rule63 from './impact_volume_shadow_copy_deletion_via_vssadmin.json'; +import rule64 from './initial_access_rpc_remote_procedure_call_from_the_internet.json'; +import rule65 from './initial_access_rpc_remote_procedure_call_to_the_internet.json'; +import rule66 from './initial_access_script_executing_powershell.json'; +import rule67 from './initial_access_smb_windows_file_sharing_activity_to_the_internet.json'; +import rule68 from './initial_access_suspicious_ms_office_child_process.json'; +import rule69 from './initial_access_suspicious_ms_outlook_child_process.json'; +import rule70 from './lateral_movement_direct_outbound_smb_connection.json'; +import rule71 from './lateral_movement_service_control_spawned_script_int.json'; +import rule72 from './linux_hping_activity.json'; +import rule73 from './linux_iodine_activity.json'; +import rule74 from './linux_netcat_network_connection.json'; +import rule75 from './linux_nping_activity.json'; +import rule76 from './linux_process_started_in_temp_directory.json'; +import rule77 from './linux_strace_activity.json'; +import rule78 from './persistence_adobe_hijack_persistence.json'; +import rule79 from './persistence_local_scheduled_task_creation.json'; +import rule80 from './persistence_priv_escalation_via_accessibility_features.json'; +import rule81 from './persistence_shell_activity_by_web_server.json'; +import rule82 from './persistence_system_shells_via_services.json'; +import rule83 from './persistence_user_account_creation.json'; +import rule84 from './persistence_via_application_shimming.json'; +import rule85 from './privilege_escalation_unusual_parentchild_relationship.json'; +import rule86 from './defense_evasion_modification_of_boot_config.json'; +import rule87 from './privilege_escalation_uac_bypass_event_viewer.json'; +import rule88 from './defense_evasion_msxsl_network.json'; +import rule89 from './discovery_net_command_system_account.json'; +import rule90 from './command_and_control_certutil_network_connection.json'; +import rule91 from './defense_evasion_cve_2020_0601.json'; +import rule92 from './credential_access_credential_dumping_msbuild.json'; +import rule93 from './defense_evasion_execution_msbuild_started_by_office_app.json'; +import rule94 from './defense_evasion_execution_msbuild_started_by_script.json'; +import rule95 from './defense_evasion_execution_msbuild_started_by_system_process.json'; +import rule96 from './defense_evasion_execution_msbuild_started_renamed.json'; +import rule97 from './defense_evasion_execution_msbuild_started_unusal_process.json'; +import rule98 from './defense_evasion_injection_msbuild.json'; +import rule99 from './ml_linux_anomalous_network_activity.json'; +import rule100 from './ml_linux_anomalous_network_port_activity.json'; +import rule101 from './ml_linux_anomalous_network_service.json'; +import rule102 from './ml_linux_anomalous_network_url_activity.json'; +import rule103 from './ml_linux_anomalous_process_all_hosts.json'; +import rule104 from './ml_linux_anomalous_user_name.json'; +import rule105 from './ml_packetbeat_dns_tunneling.json'; +import rule106 from './ml_packetbeat_rare_dns_question.json'; +import rule107 from './ml_packetbeat_rare_server_domain.json'; +import rule108 from './ml_packetbeat_rare_urls.json'; +import rule109 from './ml_packetbeat_rare_user_agent.json'; +import rule110 from './ml_rare_process_by_host_linux.json'; +import rule111 from './ml_rare_process_by_host_windows.json'; +import rule112 from './ml_suspicious_login_activity.json'; +import rule113 from './ml_windows_anomalous_network_activity.json'; +import rule114 from './ml_windows_anomalous_path_activity.json'; +import rule115 from './ml_windows_anomalous_process_all_hosts.json'; +import rule116 from './ml_windows_anomalous_process_creation.json'; +import rule117 from './ml_windows_anomalous_script.json'; +import rule118 from './ml_windows_anomalous_service.json'; +import rule119 from './ml_windows_anomalous_user_name.json'; +import rule120 from './ml_windows_rare_user_runas_event.json'; +import rule121 from './ml_windows_rare_user_type10_remote_login.json'; +import rule122 from './execution_suspicious_pdf_reader.json'; +import rule123 from './privilege_escalation_sudoers_file_mod.json'; +import rule124 from './defense_evasion_iis_httplogging_disabled.json'; +import rule125 from './execution_python_tty_shell.json'; +import rule126 from './execution_perl_tty_shell.json'; +import rule127 from './defense_evasion_base16_or_base32_encoding_or_decoding_activity.json'; +import rule128 from './defense_evasion_file_mod_writable_dir.json'; +import rule129 from './defense_evasion_disable_selinux_attempt.json'; +import rule130 from './discovery_kernel_module_enumeration.json'; +import rule131 from './lateral_movement_telnet_network_activity_external.json'; +import rule132 from './lateral_movement_telnet_network_activity_internal.json'; +import rule133 from './privilege_escalation_setuid_setgid_bit_set_via_chmod.json'; +import rule134 from './defense_evasion_attempt_to_disable_iptables_or_firewall.json'; +import rule135 from './defense_evasion_kernel_module_removal.json'; +import rule136 from './defense_evasion_attempt_to_disable_syslog_service.json'; +import rule137 from './defense_evasion_file_deletion_via_shred.json'; +import rule138 from './discovery_virtual_machine_fingerprinting.json'; +import rule139 from './defense_evasion_hidden_file_dir_tmp.json'; +import rule140 from './defense_evasion_deletion_of_bash_command_line_history.json'; +import rule141 from './impact_cloudwatch_log_group_deletion.json'; +import rule142 from './impact_cloudwatch_log_stream_deletion.json'; +import rule143 from './impact_rds_instance_cluster_stoppage.json'; +import rule144 from './persistence_attempt_to_deactivate_mfa_for_okta_user_account.json'; +import rule145 from './persistence_rds_cluster_creation.json'; +import rule146 from './credential_access_attempted_bypass_of_okta_mfa.json'; +import rule147 from './defense_evasion_waf_acl_deletion.json'; +import rule148 from './impact_attempt_to_revoke_okta_api_token.json'; +import rule149 from './impact_iam_group_deletion.json'; +import rule150 from './impact_possible_okta_dos_attack.json'; +import rule151 from './impact_rds_cluster_deletion.json'; +import rule152 from './initial_access_suspicious_activity_reported_by_okta_user.json'; +import rule153 from './okta_attempt_to_deactivate_okta_policy.json'; +import rule154 from './okta_attempt_to_deactivate_okta_policy_rule.json'; +import rule155 from './okta_attempt_to_modify_okta_network_zone.json'; +import rule156 from './okta_attempt_to_modify_okta_policy.json'; +import rule157 from './okta_attempt_to_modify_okta_policy_rule.json'; +import rule158 from './okta_threat_detected_by_okta_threatinsight.json'; +import rule159 from './persistence_administrator_privileges_assigned_to_okta_group.json'; +import rule160 from './persistence_attempt_to_create_okta_api_token.json'; +import rule161 from './persistence_attempt_to_reset_mfa_factors_for_okta_user_account.json'; +import rule162 from './defense_evasion_cloudtrail_logging_deleted.json'; +import rule163 from './defense_evasion_ec2_network_acl_deletion.json'; +import rule164 from './impact_iam_deactivate_mfa_device.json'; +import rule165 from './defense_evasion_s3_bucket_configuration_deletion.json'; +import rule166 from './defense_evasion_guardduty_detector_deletion.json'; +import rule167 from './okta_attempt_to_delete_okta_policy.json'; +import rule168 from './credential_access_iam_user_addition_to_group.json'; +import rule169 from './persistence_ec2_network_acl_creation.json'; +import rule170 from './impact_ec2_disable_ebs_encryption.json'; +import rule171 from './persistence_iam_group_creation.json'; +import rule172 from './defense_evasion_waf_rule_or_rule_group_deletion.json'; +import rule173 from './collection_cloudtrail_logging_created.json'; +import rule174 from './defense_evasion_cloudtrail_logging_suspended.json'; +import rule175 from './impact_cloudtrail_logging_updated.json'; +import rule176 from './initial_access_console_login_root.json'; +import rule177 from './defense_evasion_cloudwatch_alarm_deletion.json'; +import rule178 from './defense_evasion_ec2_flow_log_deletion.json'; +import rule179 from './defense_evasion_configuration_recorder_stopped.json'; +import rule180 from './exfiltration_ec2_snapshot_change_activity.json'; +import rule181 from './defense_evasion_config_service_rule_deletion.json'; +import rule182 from './okta_attempt_to_modify_or_delete_application_sign_on_policy.json'; +import rule183 from './command_and_control_download_rar_powershell_from_internet.json'; +import rule184 from './initial_access_password_recovery.json'; +import rule185 from './command_and_control_cobalt_strike_beacon.json'; +import rule186 from './command_and_control_fin7_c2_behavior.json'; +import rule187 from './command_and_control_halfbaked_beacon.json'; +import rule188 from './credential_access_secretsmanager_getsecretvalue.json'; +import rule189 from './initial_access_via_system_manager.json'; +import rule190 from './privilege_escalation_root_login_without_mfa.json'; +import rule191 from './privilege_escalation_updateassumerolepolicy.json'; +import rule192 from './impact_hosts_file_modified.json'; +import rule193 from './elastic_endpoint_security.json'; +import rule194 from './external_alerts.json'; +import rule195 from './initial_access_login_failures.json'; +import rule196 from './initial_access_login_location.json'; +import rule197 from './initial_access_login_sessions.json'; +import rule198 from './initial_access_login_time.json'; +import rule199 from './ml_cloudtrail_error_message_spike.json'; +import rule200 from './ml_cloudtrail_rare_error_code.json'; +import rule201 from './ml_cloudtrail_rare_method_by_city.json'; +import rule202 from './ml_cloudtrail_rare_method_by_country.json'; +import rule203 from './ml_cloudtrail_rare_method_by_user.json'; +import rule204 from './credential_access_aws_iam_assume_role_brute_force.json'; +import rule205 from './credential_access_okta_brute_force_or_password_spraying.json'; +import rule206 from './initial_access_unusual_dns_service_children.json'; +import rule207 from './initial_access_unusual_dns_service_file_writes.json'; +import rule208 from './lateral_movement_dns_server_overflow.json'; +import rule209 from './credential_access_root_console_failure_brute_force.json'; +import rule210 from './initial_access_unsecure_elasticsearch_node.json'; +import rule211 from './credential_access_domain_backup_dpapi_private_keys.json'; +import rule212 from './persistence_gpo_schtask_service_creation.json'; +import rule213 from './credential_access_credentials_keychains.json'; +import rule214 from './credential_access_kerberosdump_kcc.json'; +import rule215 from './defense_evasion_attempt_del_quarantine_attrib.json'; +import rule216 from './execution_suspicious_psexesvc.json'; +import rule217 from './execution_via_xp_cmdshell_mssql_stored_procedure.json'; +import rule218 from './privilege_escalation_printspooler_service_suspicious_file.json'; +import rule219 from './privilege_escalation_printspooler_suspicious_spl_file.json'; +import rule220 from './defense_evasion_azure_diagnostic_settings_deletion.json'; +import rule221 from './execution_command_virtual_machine.json'; +import rule222 from './execution_via_hidden_shell_conhost.json'; +import rule223 from './impact_resource_group_deletion.json'; +import rule224 from './persistence_via_telemetrycontroller_scheduledtask_hijack.json'; +import rule225 from './persistence_via_update_orchestrator_service_hijack.json'; +import rule226 from './collection_update_event_hub_auth_rule.json'; +import rule227 from './credential_access_iis_apppoolsa_pwd_appcmd.json'; +import rule228 from './credential_access_iis_connectionstrings_dumping.json'; +import rule229 from './defense_evasion_event_hub_deletion.json'; +import rule230 from './defense_evasion_firewall_policy_deletion.json'; +import rule231 from './defense_evasion_sdelete_like_filename_rename.json'; +import rule232 from './lateral_movement_remote_ssh_login_enabled.json'; +import rule233 from './persistence_azure_automation_account_created.json'; +import rule234 from './persistence_azure_automation_runbook_created_or_modified.json'; +import rule235 from './persistence_azure_automation_webhook_created.json'; +import rule236 from './privilege_escalation_uac_bypass_diskcleanup_hijack.json'; +import rule237 from './credential_access_attempts_to_brute_force_okta_user_account.json'; +import rule238 from './credential_access_storage_account_key_regenerated.json'; +import rule239 from './defense_evasion_suspicious_okta_user_password_reset_or_unlock_attempts.json'; +import rule240 from './defense_evasion_system_critical_proc_abnormal_file_activity.json'; +import rule241 from './defense_evasion_unusual_system_vp_child_program.json'; +import rule242 from './discovery_blob_container_access_mod.json'; +import rule243 from './persistence_mfa_disabled_for_azure_user.json'; +import rule244 from './persistence_user_added_as_owner_for_azure_application.json'; +import rule245 from './persistence_user_added_as_owner_for_azure_service_principal.json'; +import rule246 from './defense_evasion_dotnet_compiler_parent_process.json'; +import rule247 from './defense_evasion_suspicious_managedcode_host_process.json'; +import rule248 from './execution_command_shell_started_by_unusual_process.json'; +import rule249 from './defense_evasion_masquerading_as_elastic_endpoint_process.json'; +import rule250 from './defense_evasion_masquerading_suspicious_werfault_childproc.json'; +import rule251 from './defense_evasion_masquerading_werfault.json'; +import rule252 from './credential_access_key_vault_modified.json'; +import rule253 from './credential_access_mimikatz_memssp_default_logs.json'; +import rule254 from './defense_evasion_code_injection_conhost.json'; +import rule255 from './defense_evasion_network_watcher_deletion.json'; +import rule256 from './initial_access_external_guest_user_invite.json'; +import rule257 from './defense_evasion_masquerading_renamed_autoit.json'; +import rule258 from './impact_azure_automation_runbook_deleted.json'; +import rule259 from './initial_access_consent_grant_attack_via_azure_registered_application.json'; +import rule260 from './persistence_azure_conditional_access_policy_modified.json'; +import rule261 from './persistence_azure_privileged_identity_management_role_modified.json'; +import rule262 from './command_and_control_teamviewer_remote_file_copy.json'; +import rule263 from './defense_evasion_installutil_beacon.json'; +import rule264 from './defense_evasion_mshta_beacon.json'; +import rule265 from './defense_evasion_network_connection_from_windows_binary.json'; +import rule266 from './defense_evasion_rundll32_no_arguments.json'; +import rule267 from './defense_evasion_suspicious_scrobj_load.json'; +import rule268 from './defense_evasion_suspicious_wmi_script.json'; +import rule269 from './execution_ms_office_written_file.json'; +import rule270 from './execution_pdf_written_file.json'; +import rule271 from './lateral_movement_cmd_service.json'; +import rule272 from './persistence_app_compat_shim.json'; +import rule273 from './command_and_control_remote_file_copy_desktopimgdownldr.json'; +import rule274 from './command_and_control_remote_file_copy_mpcmdrun.json'; +import rule275 from './defense_evasion_execution_suspicious_explorer_winword.json'; +import rule276 from './defense_evasion_suspicious_zoom_child_process.json'; +import rule277 from './ml_linux_anomalous_compiler_activity.json'; +import rule278 from './ml_linux_anomalous_kernel_module_arguments.json'; +import rule279 from './ml_linux_anomalous_sudo_activity.json'; +import rule280 from './ml_linux_system_information_discovery.json'; +import rule281 from './ml_linux_system_network_configuration_discovery.json'; +import rule282 from './ml_linux_system_network_connection_discovery.json'; +import rule283 from './ml_linux_system_process_discovery.json'; +import rule284 from './ml_linux_system_user_discovery.json'; +import rule285 from './discovery_post_exploitation_external_ip_lookup.json'; +import rule286 from './initial_access_zoom_meeting_with_no_passcode.json'; +import rule287 from './defense_evasion_gcp_logging_sink_deletion.json'; +import rule288 from './defense_evasion_gcp_pub_sub_topic_deletion.json'; +import rule289 from './defense_evasion_gcp_firewall_rule_created.json'; +import rule290 from './defense_evasion_gcp_firewall_rule_deleted.json'; +import rule291 from './defense_evasion_gcp_firewall_rule_modified.json'; +import rule292 from './defense_evasion_gcp_logging_bucket_deletion.json'; +import rule293 from './defense_evasion_gcp_storage_bucket_permissions_modified.json'; +import rule294 from './impact_gcp_storage_bucket_deleted.json'; +import rule295 from './initial_access_gcp_iam_custom_role_creation.json'; +import rule296 from './persistence_gcp_iam_service_account_key_deletion.json'; +import rule297 from './persistence_gcp_key_created_for_service_account.json'; +import rule298 from './defense_evasion_gcp_storage_bucket_configuration_modified.json'; +import rule299 from './exfiltration_gcp_logging_sink_modification.json'; +import rule300 from './impact_gcp_iam_role_deletion.json'; +import rule301 from './impact_gcp_service_account_deleted.json'; +import rule302 from './impact_gcp_service_account_disabled.json'; +import rule303 from './impact_gcp_virtual_private_cloud_network_deleted.json'; +import rule304 from './impact_gcp_virtual_private_cloud_route_created.json'; +import rule305 from './impact_gcp_virtual_private_cloud_route_deleted.json'; +import rule306 from './ml_linux_anomalous_metadata_process.json'; +import rule307 from './ml_linux_anomalous_metadata_user.json'; +import rule308 from './ml_windows_anomalous_metadata_process.json'; +import rule309 from './ml_windows_anomalous_metadata_user.json'; +import rule310 from './persistence_gcp_service_account_created.json'; +import rule311 from './collection_gcp_pub_sub_subscription_creation.json'; +import rule312 from './collection_gcp_pub_sub_topic_creation.json'; +import rule313 from './defense_evasion_gcp_pub_sub_subscription_deletion.json'; +import rule314 from './persistence_azure_pim_user_added_global_admin.json'; +import rule315 from './command_and_control_cobalt_strike_default_teamserver_cert.json'; +import rule316 from './defense_evasion_enable_inbound_rdp_with_netsh.json'; +import rule317 from './defense_evasion_execution_lolbas_wuauclt.json'; +import rule318 from './privilege_escalation_unusual_svchost_childproc_childless.json'; +import rule319 from './lateral_movement_rdp_tunnel_plink.json'; +import rule320 from './privilege_escalation_uac_bypass_winfw_mmc_hijack.json'; +import rule321 from './persistence_ms_office_addins_file.json'; +import rule322 from './discovery_adfind_command_activity.json'; +import rule323 from './discovery_security_software_wmic.json'; +import rule324 from './execution_command_shell_via_rundll32.json'; +import rule325 from './execution_suspicious_cmd_wmi.json'; +import rule326 from './lateral_movement_via_startup_folder_rdp_smb.json'; +import rule327 from './privilege_escalation_uac_bypass_com_interface_icmluautil.json'; +import rule328 from './privilege_escalation_uac_bypass_mock_windir.json'; +import rule329 from './defense_evasion_potential_processherpaderping.json'; +import rule330 from './privilege_escalation_uac_bypass_dll_sideloading.json'; +import rule331 from './execution_shared_modules_local_sxs_dll.json'; +import rule332 from './privilege_escalation_uac_bypass_com_clipup.json'; +import rule333 from './initial_access_via_explorer_suspicious_child_parent_args.json'; +import rule334 from './execution_from_unusual_directory.json'; +import rule335 from './execution_from_unusual_path_cmdline.json'; +import rule336 from './credential_access_kerberoasting_unusual_process.json'; +import rule337 from './discovery_peripheral_device.json'; +import rule338 from './lateral_movement_mount_hidden_or_webdav_share_net.json'; +import rule339 from './defense_evasion_deleting_websvr_access_logs.json'; +import rule340 from './defense_evasion_log_files_deleted.json'; +import rule341 from './defense_evasion_timestomp_touch.json'; +import rule342 from './lateral_movement_dcom_hta.json'; +import rule343 from './lateral_movement_execution_via_file_shares_sequence.json'; +import rule344 from './privilege_escalation_uac_bypass_com_ieinstal.json'; +import rule345 from './command_and_control_common_webservices.json'; +import rule346 from './command_and_control_encrypted_channel_freesslcert.json'; +import rule347 from './defense_evasion_process_termination_followed_by_deletion.json'; +import rule348 from './lateral_movement_remote_file_copy_hidden_share.json'; +import rule349 from './attempt_to_deactivate_okta_network_zone.json'; +import rule350 from './attempt_to_delete_okta_network_zone.json'; +import rule351 from './lateral_movement_dcom_mmc20.json'; +import rule352 from './lateral_movement_dcom_shellwindow_shellbrowserwindow.json'; +import rule353 from './okta_attempt_to_deactivate_okta_application.json'; +import rule354 from './okta_attempt_to_delete_okta_application.json'; +import rule355 from './okta_attempt_to_delete_okta_policy_rule.json'; +import rule356 from './okta_attempt_to_modify_okta_application.json'; +import rule357 from './persistence_administrator_role_assigned_to_okta_user.json'; +import rule358 from './lateral_movement_executable_tool_transfer_smb.json'; +import rule359 from './command_and_control_dns_tunneling_nslookup.json'; +import rule360 from './lateral_movement_execution_from_tsclient_mup.json'; +import rule361 from './lateral_movement_rdp_sharprdp_target.json'; +import rule362 from './defense_evasion_clearing_windows_security_logs.json'; +import rule363 from './persistence_google_workspace_api_access_granted_via_domain_wide_delegation_of_authority.json'; +import rule364 from './execution_suspicious_short_program_name.json'; +import rule365 from './lateral_movement_incoming_wmi.json'; +import rule366 from './persistence_via_hidden_run_key_valuename.json'; +import rule367 from './credential_access_potential_ssh_bruteforce.json'; +import rule368 from './credential_access_promt_for_pwd_via_osascript.json'; +import rule369 from './lateral_movement_remote_services.json'; +import rule370 from './application_added_to_google_workspace_domain.json'; +import rule371 from './domain_added_to_google_workspace_trusted_domains.json'; +import rule372 from './execution_suspicious_image_load_wmi_ms_office.json'; +import rule373 from './execution_suspicious_powershell_imgload.json'; +import rule374 from './google_workspace_admin_role_deletion.json'; +import rule375 from './google_workspace_mfa_enforcement_disabled.json'; +import rule376 from './google_workspace_policy_modified.json'; +import rule377 from './mfa_disabled_for_google_workspace_organization.json'; +import rule378 from './persistence_evasion_registry_ifeo_injection.json'; +import rule379 from './persistence_google_workspace_admin_role_assigned_to_user.json'; +import rule380 from './persistence_google_workspace_custom_admin_role_created.json'; +import rule381 from './persistence_google_workspace_role_modified.json'; +import rule382 from './persistence_suspicious_image_load_scheduled_task_ms_office.json'; +import rule383 from './defense_evasion_masquerading_trusted_directory.json'; +import rule384 from './exfiltration_microsoft_365_exchange_transport_rule_creation.json'; +import rule385 from './initial_access_microsoft_365_exchange_safelinks_disabled.json'; +import rule386 from './microsoft_365_exchange_dkim_signing_config_disabled.json'; +import rule387 from './persistence_appcertdlls_registry.json'; +import rule388 from './persistence_appinitdlls_registry.json'; +import rule389 from './persistence_registry_uncommon.json'; +import rule390 from './persistence_run_key_and_startup_broad.json'; +import rule391 from './persistence_services_registry.json'; +import rule392 from './persistence_startup_folder_file_written_by_suspicious_process.json'; +import rule393 from './persistence_startup_folder_scripts.json'; +import rule394 from './persistence_suspicious_com_hijack_registry.json'; +import rule395 from './persistence_via_lsa_security_support_provider_registry.json'; +import rule396 from './defense_evasion_microsoft_365_exchange_malware_filter_policy_deletion.json'; +import rule397 from './defense_evasion_microsoft_365_exchange_malware_filter_rule_mod.json'; +import rule398 from './defense_evasion_microsoft_365_exchange_safe_attach_rule_disabled.json'; +import rule399 from './exfiltration_microsoft_365_exchange_transport_rule_mod.json'; +import rule400 from './initial_access_microsoft_365_exchange_anti_phish_policy_deletion.json'; +import rule401 from './initial_access_microsoft_365_exchange_anti_phish_rule_mod.json'; +import rule402 from './lateral_movement_suspicious_rdp_client_imageload.json'; +import rule403 from './persistence_runtime_run_key_startup_susp_procs.json'; +import rule404 from './persistence_suspicious_scheduled_task_runtime.json'; +import rule405 from './defense_evasion_microsoft_365_exchange_dlp_policy_removed.json'; +import rule406 from './lateral_movement_scheduled_task_target.json'; +import rule407 from './persistence_microsoft_365_exchange_management_role_assignment.json'; +import rule408 from './persistence_microsoft_365_teams_guest_access_enabled.json'; +import rule409 from './credential_access_dump_registry_hives.json'; +import rule410 from './defense_evasion_scheduledjobs_at_protocol_enabled.json'; +import rule411 from './persistence_ms_outlook_vba_template.json'; +import rule412 from './persistence_suspicious_service_created_registry.json'; +import rule413 from './privilege_escalation_named_pipe_impersonation.json'; +import rule414 from './credential_access_cmdline_dump_tool.json'; +import rule415 from './credential_access_copy_ntds_sam_volshadowcp_cmdline.json'; +import rule416 from './credential_access_lsass_memdump_file_created.json'; +import rule417 from './lateral_movement_incoming_winrm_shell_execution.json'; +import rule418 from './lateral_movement_powershell_remoting_target.json'; +import rule419 from './defense_evasion_hide_encoded_executable_registry.json'; +import rule420 from './defense_evasion_port_forwarding_added_registry.json'; +import rule421 from './lateral_movement_rdp_enabled_registry.json'; +import rule422 from './privilege_escalation_printspooler_registry_copyfiles.json'; +import rule423 from './privilege_escalation_rogue_windir_environment_var.json'; +import rule424 from './initial_access_scripts_process_started_via_wmi.json'; +import rule425 from './command_and_control_iexplore_via_com.json'; +import rule426 from './command_and_control_remote_file_copy_scripts.json'; +import rule427 from './persistence_local_scheduled_task_scripting.json'; +import rule428 from './persistence_startup_folder_file_written_by_unsigned_process.json'; +import rule429 from './command_and_control_remote_file_copy_powershell.json'; +import rule430 from './credential_access_microsoft_365_brute_force_user_account_attempt.json'; +import rule431 from './microsoft_365_teams_custom_app_interaction_allowed.json'; +import rule432 from './persistence_microsoft_365_teams_external_access_enabled.json'; +import rule433 from './credential_access_microsoft_365_potential_password_spraying_attack.json'; +import rule434 from './defense_evasion_stop_process_service_threshold.json'; +import rule435 from './collection_winrar_encryption.json'; +import rule436 from './defense_evasion_unusual_dir_ads.json'; +import rule437 from './discovery_admin_recon.json'; +import rule438 from './discovery_file_dir_discovery.json'; +import rule439 from './discovery_net_view.json'; +import rule440 from './discovery_remote_system_discovery_commands_windows.json'; +import rule441 from './persistence_via_windows_management_instrumentation_event_subscription.json'; +import rule442 from './execution_scripting_osascript_exec_followed_by_netcon.json'; +import rule443 from './execution_shell_execution_via_apple_scripting.json'; +import rule444 from './persistence_creation_change_launch_agents_file.json'; +import rule445 from './persistence_creation_modif_launch_deamon_sequence.json'; +import rule446 from './persistence_folder_action_scripts_runtime.json'; +import rule447 from './persistence_login_logout_hooks_defaults.json'; +import rule448 from './privilege_escalation_explicit_creds_via_scripting.json'; +import rule449 from './command_and_control_sunburst_c2_activity_detected.json'; +import rule450 from './defense_evasion_azure_application_credential_modification.json'; +import rule451 from './defense_evasion_azure_service_principal_addition.json'; +import rule452 from './defense_evasion_solarwinds_backdoor_service_disabled_via_registry.json'; +import rule453 from './execution_apt_solarwinds_backdoor_child_cmd_powershell.json'; +import rule454 from './execution_apt_solarwinds_backdoor_unusual_child_processes.json'; +import rule455 from './initial_access_azure_active_directory_powershell_signin.json'; +import rule456 from './collection_email_powershell_exchange_mailbox.json'; +import rule457 from './collection_persistence_powershell_exch_mailbox_activesync_add_device.json'; +import rule458 from './execution_scheduled_task_powershell_source.json'; +import rule459 from './persistence_docker_shortcuts_plist_modification.json'; +import rule460 from './persistence_evasion_hidden_local_account_creation.json'; +import rule461 from './persistence_finder_sync_plugin_pluginkit.json'; +import rule462 from './discovery_security_software_grep.json'; +import rule463 from './credential_access_cookies_chromium_browsers_debugging.json'; +import rule464 from './credential_access_ssh_backdoor_log.json'; +import rule465 from './persistence_credential_access_modify_auth_module_or_config.json'; +import rule466 from './persistence_credential_access_modify_ssh_binaries.json'; +import rule467 from './credential_access_collection_sensitive_files.json'; +import rule468 from './persistence_ssh_authorized_keys_modification.json'; +import rule469 from './defense_evasion_defender_disabled_via_registry.json'; +import rule470 from './defense_evasion_privacy_controls_tcc_database_modification.json'; +import rule471 from './execution_initial_access_suspicious_browser_childproc.json'; +import rule472 from './execution_script_via_automator_workflows.json'; +import rule473 from './persistence_modification_sublime_app_plugin_or_script.json'; +import rule474 from './privilege_escalation_applescript_with_admin_privs.json'; +import rule475 from './credential_access_dumping_keychain_security.json'; +import rule476 from './initial_access_azure_active_directory_high_risk_signin.json'; +import rule477 from './initial_access_suspicious_mac_ms_office_child_process.json'; +import rule478 from './credential_access_mitm_localhost_webproxy.json'; +import rule479 from './persistence_kde_autostart_modification.json'; +import rule480 from './persistence_user_account_added_to_privileged_group_ad.json'; +import rule481 from './defense_evasion_attempt_to_disable_gatekeeper.json'; +import rule482 from './defense_evasion_sandboxed_office_app_suspicious_zip_file.json'; +import rule483 from './persistence_emond_rules_file_creation.json'; +import rule484 from './persistence_emond_rules_process_execution.json'; +import rule485 from './discovery_users_domain_built_in_commands.json'; +import rule486 from './execution_pentest_eggshell_remote_admin_tool.json'; +import rule487 from './defense_evasion_install_root_certificate.json'; +import rule488 from './persistence_credential_access_authorization_plugin_creation.json'; +import rule489 from './persistence_directory_services_plugins_modification.json'; +import rule490 from './defense_evasion_modify_environment_launchctl.json'; +import rule491 from './defense_evasion_safari_config_change.json'; +import rule492 from './defense_evasion_apple_softupdates_modification.json'; +import rule493 from './credential_access_mod_wdigest_security_provider.json'; +import rule494 from './credential_access_saved_creds_vaultcmd.json'; +import rule495 from './defense_evasion_file_creation_mult_extension.json'; +import rule496 from './execution_enumeration_via_wmiprvse.json'; +import rule497 from './execution_suspicious_jar_child_process.json'; +import rule498 from './persistence_shell_profile_modification.json'; +import rule499 from './persistence_suspicious_calendar_modification.json'; +import rule500 from './persistence_time_provider_mod.json'; +import rule501 from './privilege_escalation_exploit_adobe_acrobat_updater.json'; +import rule502 from './defense_evasion_sip_provider_mod.json'; +import rule503 from './execution_com_object_xwizard.json'; +import rule504 from './privilege_escalation_disable_uac_registry.json'; +import rule505 from './defense_evasion_unusual_ads_file_creation.json'; +import rule506 from './persistence_loginwindow_plist_modification.json'; +import rule507 from './persistence_periodic_tasks_file_mdofiy.json'; +import rule508 from './persistence_via_atom_init_file_modification.json'; +import rule509 from './privilege_escalation_lsa_auth_package.json'; +import rule510 from './privilege_escalation_port_monitor_print_pocessor_abuse.json'; +import rule511 from './credential_access_dumping_hashes_bi_cmds.json'; +import rule512 from './lateral_movement_mounting_smb_share.json'; +import rule513 from './privilege_escalation_echo_nopasswd_sudoers.json'; +import rule514 from './privilege_escalation_ld_preload_shared_object_modif.json'; +import rule515 from './privilege_escalation_root_crontab_filemod.json'; +import rule516 from './defense_evasion_create_mod_root_certificate.json'; +import rule517 from './privilege_escalation_sudo_buffer_overflow.json'; +import rule518 from './execution_installer_spawned_network_event.json'; +import rule519 from './initial_access_suspicious_ms_exchange_files.json'; +import rule520 from './initial_access_suspicious_ms_exchange_process.json'; +import rule521 from './initial_access_suspicious_ms_exchange_worker_child_process.json'; +import rule522 from './persistence_evasion_registry_startup_shell_folder_modified.json'; +import rule523 from './persistence_local_scheduled_job_creation.json'; +import rule524 from './persistence_via_wmi_stdregprov_run_services.json'; +import rule525 from './credential_access_persistence_network_logon_provider_modification.json'; +import rule526 from './lateral_movement_defense_evasion_lanman_nullsessionpipe_modification.json'; +import rule527 from './collection_microsoft_365_new_inbox_rule.json'; +import rule528 from './ml_high_count_network_denies.json'; +import rule529 from './ml_high_count_network_events.json'; +import rule530 from './ml_rare_destination_country.json'; +import rule531 from './ml_spike_in_traffic_to_a_country.json'; +import rule532 from './command_and_control_tunneling_via_earthworm.json'; +import rule533 from './lateral_movement_evasion_rdp_shadowing.json'; +import rule534 from './threat_intel_module_match.json'; +import rule535 from './persistence_via_bits_job_notify_command.json'; export const rawRules = [ rule1, @@ -1093,15 +1082,4 @@ export const rawRules = [ rule533, rule534, rule535, - rule536, - rule537, - rule538, - rule539, - rule540, - rule541, - rule542, - rule543, - rule544, - rule545, - rule546, ]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json index 48796255ac6bb..c0884cf809e77 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_console_login_root.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS Management Console Root Login", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:ConsoleLogin and event.dataset:aws.cloudtrail and event.provider:signin.amazonaws.com and aws.cloudtrail.user_identity.type:Root and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:signin.amazonaws.com and event.action:ConsoleLogin and aws.cloudtrail.user_identity.type:Root and event.outcome:success", "references": [ "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html" ], @@ -65,5 +65,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json index 667d2eff5fb05..902abb51a6f99 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_password_recovery.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS IAM Password Recovery Requested", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:PasswordRecoveryRequested and event.provider:signin.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:signin.amazonaws.com and event.action:PasswordRecoveryRequested and event.outcome:success", "references": [ "https://www.cadosecurity.com/2020/06/11/an-ongoing-aws-phishing-campaign/" ], @@ -50,5 +50,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_rdp_remote_desktop_protocol_to_the_internet.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_rdp_remote_desktop_protocol_to_the_internet.json deleted file mode 100644 index d58db96e4b1ba..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_rdp_remote_desktop_protocol_to_the_internet.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "This rule detects network events that may indicate the use of RDP traffic to the Internet. RDP is commonly used by system administrators to remotely control a system for maintenance or to use shared resources. It should almost never be directly exposed to the Internet, as it is frequently targeted and exploited by threat actors as an initial access or back-door vector.", - "false_positives": [ - "RDP connections may be made directly to Internet destinations in order to access Windows cloud server instances but such connections are usually made only by engineers. In such cases, only RDP gateways, bastions or jump servers may be expected Internet destinations and can be exempted from this rule. RDP may be required by some work-flows such as remote access and support for specialized software products and servers. Such work-flows are usually known and not unexpected. Usage that is unfamiliar to server or network owners can be unexpected and suspicious." - ], - "from": "now-9m", - "index": [ - "filebeat-*", - "packetbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "RDP (Remote Desktop Protocol) to the Internet", - "query": "event.category:(network or network_traffic) and network.transport:tcp and (destination.port:3389 or event.dataset:zeek.rdp) and source.ip:( 10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16 ) and not destination.ip:( 10.0.0.0/8 or 127.0.0.0/8 or 169.254.0.0/16 or 172.16.0.0/12 or 192.168.0.0/16 or 224.0.0.0/4 or \"::1\" or \"FE80::/10\" or \"FF00::/8\" )", - "risk_score": 21, - "rule_id": "e56993d2-759c-4120-984c-9ec9bb940fd5", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Network", - "Threat Detection", - "Initial Access" - ], - "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": "TA0010", - "name": "Exfiltration", - "reference": "https://attack.mitre.org/tactics/TA0010/" - }, - "technique": [ - { - "id": "T1048", - "name": "Exfiltration Over Alternative Protocol", - "reference": "https://attack.mitre.org/techniques/T1048/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_script_executing_powershell.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_script_executing_powershell.json index 561ac14994efb..4950066d307ff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_script_executing_powershell.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_script_executing_powershell.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Windows Script Executing PowerShell", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:(cscript.exe or wscript.exe) and process.name:powershell.exe", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : (\"cscript.exe\", \"wscript.exe\") and process.name : \"powershell.exe\"\n", "risk_score": 21, "rule_id": "f545ff26-3c94-4fd0-bd33-3c7f95a3a0fc", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_mac_ms_office_child_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_mac_ms_office_child_process.json index 8796a6b4f291f..73b0718e0f27d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_mac_ms_office_child_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_mac_ms_office_child_process.json @@ -10,7 +10,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Suspicious macOS MS Office Child Process", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name:(\"Microsoft Word\", \"Microsoft PowerPoint\", \"Microsoft Excel\") and\n process.name:\n (\n \"bash\", \n \"dash\", \n \"sh\", \n \"tcsh\", \n \"csh\", \n \"zsh\", \n \"ksh\", \n \"fish\", \n \"python*\", \n \"perl*\", \n \"php*\", \n \"osascript\",\n \"pwsh\", \n \"curl\", \n \"wget\", \n \"cp\", \n \"mv\", \n \"base64\", \n \"launchctl\"\n )\n", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name:(\"Microsoft Word\", \"Microsoft PowerPoint\", \"Microsoft Excel\") and\n process.name:\n (\n \"bash\", \n \"dash\", \n \"sh\", \n \"tcsh\", \n \"csh\", \n \"zsh\", \n \"ksh\", \n \"fish\", \n \"python*\", \n \"perl*\", \n \"php*\", \n \"osascript\",\n \"pwsh\", \n \"curl\", \n \"wget\", \n \"cp\", \n \"mv\", \n \"base64\", \n \"launchctl\"\n ) and\n /* noisy false positives related to product version discovery and office errors reporting */\n not process.args:\n (\n \"ProductVersion\",\n \"hw.model\",\n \"ioreg\",\n \"ProductName\",\n \"ProductUserVisibleVersion\",\n \"ProductBuildVersion\",\n \"/Library/Application Support/Microsoft/MERP*/Microsoft Error Reporting.app/Contents/MacOS/Microsoft Error Reporting\"\n )\n", "references": [ "https://blog.malwarebytes.com/cybercrime/2017/02/microsoft-office-macro-malware-targets-macs/" ], @@ -50,5 +50,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_exchange_files.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_exchange_files.json index a999e05a66d5b..bd3e6129fa3ab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_exchange_files.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_exchange_files.json @@ -5,7 +5,8 @@ ], "description": "Identifies suspicious files being written by the Microsoft Exchange Server Unified Messaging (UM) service. This activity has been observed exploiting CVE-2021-26858.", "false_positives": [ - "Files generated during installation will generate a lot of noise, so the rule should only be enabled after the fact." + "Files generated during installation will generate a lot of noise, so the rule should only be enabled after the fact.", + "This rule was tuned using the following baseline: https://raw.githubusercontent.com/microsoft/CSS-Exchange/main/Security/Baselines/baseline_15.2.792.5.csv from Microsoft. Depending on version, consult https://github.com/microsoft/CSS-Exchange/tree/main/Security/Baselines to help determine normalcy." ], "from": "now-9m", "index": [ @@ -16,7 +17,8 @@ "language": "eql", "license": "Elastic License v2", "name": "Microsoft Exchange Server UM Writing Suspicious Files", - "query": "file where event.type == \"creation\" and\n process.parent.name : (\"UMWorkerProcess.exe\", \"umservice.exe\") and\n file.extension : (\"php\", \"jsp\", \"js\", \"aspx\", \"asmx\", \"asax\", \"cfm\", \"shtml\") and\n (\n file.path : (\"C:\\\\inetpub\\\\wwwroot\\\\aspnet_client\\\\*\",\n \"C:\\\\*\\\\FrontEnd\\\\HttpProxy\\\\owa\\\\auth\\\\*\") or\n (file.path : \"C:\\\\*\\\\FrontEnd\\\\HttpProxy\\\\ecp\\\\auth\\\\*\" and not file.name : \"TimeoutLogoff.aspx\")\n )\n", + "note": "## Triage and analysis\nPositive hits can be checked against the established Microsoft [baselines](https://github.com/microsoft/CSS-Exchange/tree/main/Security/Baselines).\n\nMicrosoft highly recommends that the best course of action is patching, but this may not protect already compromised systems\nfrom existing intrusions. Other tools for detecting and mitigating can be found within their Exchange support\n[repository](https://github.com/microsoft/CSS-Exchange/tree/main/Security)\n", + "query": "file where event.type == \"creation\" and\n process.name : (\"UMWorkerProcess.exe\", \"umservice.exe\") and\n file.extension : (\"php\", \"jsp\", \"js\", \"aspx\", \"asmx\", \"asax\", \"cfm\", \"shtml\") and\n (\n file.path : \"?:\\\\inetpub\\\\wwwroot\\\\aspnet_client\\\\*\" or\n\n (file.path : \"?:\\\\*\\\\Microsoft\\\\Exchange Server*\\\\FrontEnd\\\\HttpProxy\\\\owa\\\\auth\\\\*\" and\n not (file.path : \"?:\\\\*\\\\Microsoft\\\\Exchange Server*\\\\FrontEnd\\\\HttpProxy\\\\owa\\\\auth\\\\version\\\\*\" or\n file.name : (\"errorFE.aspx\", \"expiredpassword.aspx\", \"frowny.aspx\", \"GetIdToken.htm\", \"logoff.aspx\",\n \"logon.aspx\", \"OutlookCN.aspx\", \"RedirSuiteServiceProxy.aspx\", \"signout.aspx\"))) or\n\n (file.path : \"?:\\\\*\\\\Microsoft\\\\Exchange Server*\\\\FrontEnd\\\\HttpProxy\\\\ecp\\\\auth\\\\*\" and\n not file.name : \"TimeoutLogoff.aspx\")\n )\n", "references": [ "https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers", "https://www.volexity.com/blog/2021/03/02/active-exploitation-of-microsoft-exchange-zero-day-vulnerabilities" @@ -50,5 +52,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_outlook_child_process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_outlook_child_process.json index 1e5aa4e3950ab..baba3386b048e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_outlook_child_process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_suspicious_ms_outlook_child_process.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious MS Outlook Child Process", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:outlook.exe and process.name:(Microsoft.Workflow.Compiler.exe or arp.exe or atbroker.exe or bginfo.exe or bitsadmin.exe or cdb.exe or certutil.exe or cmd.exe or cmstp.exe or cscript.exe or csi.exe or dnx.exe or dsget.exe or dsquery.exe or forfiles.exe or fsi.exe or ftp.exe or gpresult.exe or hostname.exe or ieexec.exe or iexpress.exe or installutil.exe or ipconfig.exe or mshta.exe or msxsl.exe or nbtstat.exe or net.exe or net1.exe or netsh.exe or netstat.exe or nltest.exe or odbcconf.exe or ping.exe or powershell.exe or pwsh.exe or qprocess.exe or quser.exe or qwinsta.exe or rcsi.exe or reg.exe or regasm.exe or regsvcs.exe or regsvr32.exe or sc.exe or schtasks.exe or systeminfo.exe or tasklist.exe or tracert.exe or whoami.exe or wmic.exe or wscript.exe or xwizard.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"outlook.exe\" and\n process.name : (\"Microsoft.Workflow.Compiler.exe\", \"arp.exe\", \"atbroker.exe\", \"bginfo.exe\", \"bitsadmin.exe\",\n \"cdb.exe\", \"certutil.exe\", \"cmd.exe\", \"cmstp.exe\", \"cscript.exe\", \"csi.exe\", \"dnx.exe\", \"dsget.exe\",\n \"dsquery.exe\", \"forfiles.exe\", \"fsi.exe\", \"ftp.exe\", \"gpresult.exe\", \"hostname.exe\", \"ieexec.exe\",\n \"iexpress.exe\", \"installutil.exe\", \"ipconfig.exe\", \"mshta.exe\", \"msxsl.exe\", \"nbtstat.exe\", \"net.exe\",\n \"net1.exe\", \"netsh.exe\", \"netstat.exe\", \"nltest.exe\", \"odbcconf.exe\", \"ping.exe\", \"powershell.exe\",\n \"pwsh.exe\", \"qprocess.exe\", \"quser.exe\", \"qwinsta.exe\", \"rcsi.exe\", \"reg.exe\", \"regasm.exe\",\n \"regsvcs.exe\", \"regsvr32.exe\", \"sc.exe\", \"schtasks.exe\", \"systeminfo.exe\", \"tasklist.exe\",\n \"tracert.exe\", \"whoami.exe\", \"wmic.exe\", \"wscript.exe\", \"xwizard.exe\")\n", "risk_score": 21, "rule_id": "32f4675e-6c49-4ace-80f9-97c9259dca2e", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_children.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_children.json index bfc9002ee4f54..736090dd66003 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_children.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_children.json @@ -12,11 +12,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Unusual Child Process of dns.exe", "note": "### Investigating Unusual Child Process\nDetection alerts from this rule indicate potential suspicious child processes spawned after exploitation from CVE-2020-1350 (SigRed) has occurred. Here are some possible avenues of investigation:\n- Any suspicious or abnormal child process spawned from dns.exe should be reviewed and investigated with care. It's impossible to predict what an adversary may deploy as the follow-on process after the exploit, but built-in discovery/enumeration utilities should be top of mind (whoami.exe, netstat.exe, systeminfo.exe, tasklist.exe).\n- Built-in Windows programs that contain capabilities used to download and execute additional payloads should also be considered. This is not an exhaustive list, but ideal candidates to start out would be: mshta.exe, powershell.exe, regsvr32.exe, rundll32.exe, wscript.exe, wmic.exe.\n- If the DoS exploit is successful and DNS Server service crashes, be mindful of potential child processes related to werfault.exe occurring.\n- Any subsequent activity following the child process spawned related to execution/network activity should be thoroughly reviewed from the endpoint.", - "query": "event.category:process and event.type:start and process.parent.name:dns.exe and not process.name:conhost.exe", + "query": "process where event.type == \"start\" and process.parent.name : \"dns.exe\" and\n not process.name : \"conhost.exe\"\n", "references": [ "https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/", "https://msrc-blog.microsoft.com/2020/07/14/july-2020-security-update-cve-2020-1350-vulnerability-in-windows-domain-name-system-dns-server/", @@ -50,6 +50,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_file_writes.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_file_writes.json index a1fdf6cc2749b..495f4885f2145 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_file_writes.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_unusual_dns_service_file_writes.json @@ -9,11 +9,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Unusual File Modification by dns.exe", "note": "### Investigating Unusual File Write\nDetection alerts from this rule indicate potential unusual/abnormal file writes from the DNS Server service process (`dns.exe`) after exploitation from CVE-2020-1350 (SigRed) has occurred. Here are some possible avenues of investigation:\n- Post-exploitation, adversaries may write additional files or payloads to the system as additional discovery/exploitation/persistence mechanisms.\n- Any suspicious or abnormal files written from `dns.exe` should be reviewed and investigated with care.", - "query": "event.category:file and process.name:dns.exe and event.type:(creation or deletion or change) and not file.name:dns.log", + "query": "file where process.name : \"dns.exe\" and event.type in (\"creation\", \"deletion\", \"change\") and\n not file.name : \"dns.log\"\n", "references": [ "https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/", "https://msrc-blog.microsoft.com/2020/07/14/july-2020-security-update-cve-2020-1350-vulnerability-in-windows-domain-name-system-dns-server/" @@ -46,6 +46,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_explorer_suspicious_child_parent_args.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_explorer_suspicious_child_parent_args.json index 3ee2c01444ba2..23e8d3b43faf5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_explorer_suspicious_child_parent_args.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/initial_access_via_explorer_suspicious_child_parent_args.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Suspicious Explorer Child Process", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : (\"cscript.exe\", \"wscript.exe\", \"powershell.exe\", \"rundll32.exe\", \"cmd.exe\", \"mshta.exe\", \"regsvr32.exe\") and\n /* Explorer started via DCOM */\n process.parent.name : \"explorer.exe\" and process.parent.args : \"-Embedding\"\n", + "query": "process where event.type in (\"start\", \"process_started\") and\n (\n process.name : (\"cscript.exe\", \"wscript.exe\", \"powershell.exe\", \"rundll32.exe\", \"cmd.exe\", \"mshta.exe\", \"regsvr32.exe\") or\n process.pe.original_file_name in (\"cscript.exe\", \"wscript.exe\", \"PowerShell.EXE\", \"RUNDLL32.EXE\", \"Cmd.Exe\", \"MSHTA.EXE\", \"REGSVR32.EXE\")\n ) and\n /* Explorer started via DCOM */\n process.parent.name : \"explorer.exe\" and process.parent.args : \"-Embedding\" and\n not process.parent.args:\n (\n /* Noisy CLSID_SeparateSingleProcessExplorerHost Explorer COM Class IDs */\n \"/factory,{5BD95610-9434-43C2-886C-57852CC8A120}\",\n \"/factory,{ceff45ee-c862-41de-aee2-a022c81eda92}\"\n )\n", "risk_score": 47, "rule_id": "9a5b4e31-6cde-4295-9ff7-6be1b8567e1b", "severity": "medium", @@ -54,5 +54,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_defense_evasion_lanman_nullsessionpipe_modification.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_defense_evasion_lanman_nullsessionpipe_modification.json new file mode 100644 index 0000000000000..1ccabf42237ef --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_defense_evasion_lanman_nullsessionpipe_modification.json @@ -0,0 +1,57 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies NullSessionPipe registry modifications that specify which pipes can be accessed anonymously. This could be indicative of adversary lateral movement preparation by making the added pipe available to everyone.", + "from": "now-9m", + "index": [ + "logs-endpoint.events.*", + "winlogbeat-*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "NullSessionPipe Registry Modification", + "query": "registry where\nregistry.path : \"HKLM\\\\SYSTEM\\\\*ControlSet*\\\\services\\\\LanmanServer\\\\Parameters\\\\NullSessionPipes\" and\nregistry.data.strings != null\n", + "references": [ + "https://www.welivesecurity.com/2019/05/29/turla-powershell-usage/", + "https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-restrict-anonymous-access-to-named-pipes-and-shares" + ], + "risk_score": 47, + "rule_id": "ddab1f5f-7089-44f5-9fda-de5b11322e77", + "severity": "medium", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Lateral Movement" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0008", + "name": "Lateral Movement", + "reference": "https://attack.mitre.org/tactics/TA0008/" + }, + "technique": [ + { + "id": "T1021", + "name": "Remote Services", + "reference": "https://attack.mitre.org/techniques/T1021/", + "subtechnique": [ + { + "id": "T1021.002", + "name": "SMB/Windows Admin Shares", + "reference": "https://attack.mitre.org/techniques/T1021/002/" + } + ] + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_evasion_rdp_shadowing.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_evasion_rdp_shadowing.json new file mode 100644 index 0000000000000..8656bd4a0712e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_evasion_rdp_shadowing.json @@ -0,0 +1,50 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies the modification of the Remote Desktop Protocol (RDP) Shadow registry or the execution of processes indicative of an active RDP shadowing session. An adversary may abuse the RDP Shadowing feature to spy on or control other users active RDP sessions.", + "from": "now-9m", + "index": [ + "logs-endpoint.events.*", + "winlogbeat-*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Potential Remote Desktop Shadowing Activity", + "query": "/* Identifies the modification of RDP Shadow registry or\n the execution of processes indicative of active shadow RDP session */\n\nany where \n (event.category == \"registry\" and\n registry.path : \"HKLM\\\\Software\\\\Policies\\\\Microsoft\\\\Windows NT\\\\Terminal Services\\\\Shadow\"\n ) or\n (event.category == \"process\" and \n (process.name : (\"RdpSaUacHelper.exe\", \"RdpSaProxy.exe\") and process.parent.name : \"svchost.exe\") or\n (process.pe.original_file_name : \"mstsc.exe\" and process.args : \"/shadow:*\")\n )\n", + "references": [ + "https://bitsadm.in/blog/spying-on-users-using-rdp-shadowing", + "https://swarm.ptsecurity.com/remote-desktop-services-shadowing/" + ], + "risk_score": 73, + "rule_id": "c57f8579-e2a5-4804-847f-f2732edc5156", + "severity": "high", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Lateral Movement" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0008", + "name": "Lateral Movement", + "reference": "https://attack.mitre.org/tactics/TA0008/" + }, + "technique": [ + { + "id": "T1021", + "name": "Remote Services", + "reference": "https://attack.mitre.org/techniques/T1021/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_local_service_commands.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_local_service_commands.json deleted file mode 100644 index 5f92e2cb3dfc0..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_local_service_commands.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "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.", - "from": "now-9m", - "index": [ - "winlogbeat-*", - "logs-endpoint.events.*", - "logs-windows.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Local Service Commands", - "query": "event.category:process and event.type:(start or process_started) and process.name:sc.exe and process.args:(config or create or failure or start)", - "risk_score": 21, - "rule_id": "e8571d5f-bea1-46c2-9f56-998de2d3ed95", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Windows", - "Threat Detection", - "Lateral Movement" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0008", - "name": "Lateral Movement", - "reference": "https://attack.mitre.org/tactics/TA0008/" - }, - "technique": [ - { - "id": "T1021", - "name": "Remote Services", - "reference": "https://attack.mitre.org/techniques/T1021/" - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_service_control_spawned_script_int.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_service_control_spawned_script_int.json new file mode 100644 index 0000000000000..61234f392158f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/lateral_movement_service_control_spawned_script_int.json @@ -0,0 +1,46 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies Service Control (sc.exe) spawning from script interpreter processes to create, modify, or start services. This could be indicative of adversary lateral movement but will be noisy if commonly done by admins.", + "from": "now-9m", + "index": [ + "winlogbeat-*", + "logs-endpoint.events.*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Service Control Spawned via Script Interpreter", + "query": "process where event.type == \"start\" and\n (process.name : \"sc.exe\" or process.pe.original_file_name == \"sc.exe\") and\n process.parent.name : (\"cmd.exe\", \"wscript.exe\", \"rundll32.exe\", \"regsvr32.exe\",\n \"wmic.exe\", \"mshta.exe\",\"powershell.exe\", \"pwsh.exe\") and\n process.args:(\"config\", \"create\", \"start\", \"delete\", \"stop\", \"pause\") and\n /* exclude SYSTEM SID - look for service creations by non-SYSTEM user */\n not user.id : \"S-1-5-18\"\n", + "risk_score": 21, + "rule_id": "e8571d5f-bea1-46c2-9f56-998de2d3ed95", + "severity": "low", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Lateral Movement" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0008", + "name": "Lateral Movement", + "reference": "https://attack.mitre.org/tactics/TA0008/" + }, + "technique": [ + { + "id": "T1021", + "name": "Remote Services", + "reference": "https://attack.mitre.org/techniques/T1021/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 9 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json deleted file mode 100644 index 664a7ccd3b9d1..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "The Linux mknod program is sometimes used in the command payload of a remote command injection (RCI) and other exploits. It is used 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." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Mknod Process Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:mknod", - "references": [ - "https://web.archive.org/web/20191218024607/https://pen-testing.sans.org/blog/2013/05/06/netcat-without-e-no-problem/" - ], - "risk_score": 21, - "rule_id": "61c31c14-507f-4627-8c31-072556b89a9c", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection" - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json deleted file mode 100644 index 023b580adb483..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Nmap was executed on a Linux host. Nmap is a FOSS tool for network scanning and security testing. It can map and discover networks, and identify listening 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." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Nmap Process Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:nmap", - "references": [ - "https://en.wikipedia.org/wiki/Nmap" - ], - "risk_score": 21, - "rule_id": "c87fca17-b3a9-4e83-b545-f30746c53920", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection" - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json deleted file mode 100644 index fdf50bb9bf452..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "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." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Socat Process Activity", - "query": "event.category:process and event.type:(start or process_started) and process.name:socat and not process.args:-V", - "references": [ - "https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/#method-2-using-socat" - ], - "risk_score": 47, - "rule_id": "cd4d5754-07e1-41d4-b9a5-ef4ea6a0a126", - "severity": "medium", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection" - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 7 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_error_code.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_error_code.json index 87822b480cbbb..4cd3583fa681f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_error_code.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_error_code.json @@ -7,7 +7,7 @@ "false_positives": [ "Rare and unusual errors may indicate an impending service failure state. Rare and unusual user error activity can also be due to manual troubleshooting or reconfiguration attempts by insufficiently privileged users, bugs in cloud automation scripts or workflows, or changes to IAM privileges." ], - "from": "now-60m", + "from": "now-2h", "interval": "15m", "license": "Elastic License v2", "machine_learning_job_id": "rare_error_code", @@ -26,5 +26,5 @@ "ML" ], "type": "machine_learning", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_city.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_city.json index c259d5a6b5599..c516ae87e9be8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_city.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_city.json @@ -7,7 +7,7 @@ "false_positives": [ "New or unusual command and user geolocation activity can be due to manual troubleshooting or reconfiguration; changes in cloud automation scripts or workflows; adoption of new services; expansion into new regions; increased adoption of work from home policies; or users who travel frequently." ], - "from": "now-60m", + "from": "now-2h", "interval": "15m", "license": "Elastic License v2", "machine_learning_job_id": "rare_method_for_a_city", @@ -26,5 +26,5 @@ "ML" ], "type": "machine_learning", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_country.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_country.json index bb469c725aed6..8263b06cb6b2e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_country.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_country.json @@ -7,7 +7,7 @@ "false_positives": [ "New or unusual command and user geolocation activity can be due to manual troubleshooting or reconfiguration; changes in cloud automation scripts or workflows; adoption of new services; expansion into new regions; increased adoption of work from home policies; or users who travel frequently." ], - "from": "now-60m", + "from": "now-2h", "interval": "15m", "license": "Elastic License v2", "machine_learning_job_id": "rare_method_for_a_country", @@ -26,5 +26,5 @@ "ML" ], "type": "machine_learning", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_user.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_user.json index c8e995d592e39..3228031911147 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_user.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_cloudtrail_rare_method_by_user.json @@ -7,7 +7,7 @@ "false_positives": [ "New or unusual user command activity can be due to manual troubleshooting or reconfiguration; changes in cloud automation scripts or workflows; adoption of new services; or changes in the way services are used." ], - "from": "now-60m", + "from": "now-2h", "interval": "15m", "license": "Elastic License v2", "machine_learning_job_id": "rare_method_for_a_username", @@ -26,5 +26,5 @@ "ML" ], "type": "machine_learning", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_denies.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_denies.json new file mode 100644 index 0000000000000..7856d13b8d66f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_denies.json @@ -0,0 +1,29 @@ +{ + "anomaly_threshold": 75, + "author": [ + "Elastic" + ], + "description": "A machine learning job detected an unusually large spike in network traffic that was denied by network access control lists (ACLs) or firewall rules. Such a burst of denied traffic is usually caused by either 1) a mis-configured application or firewall or 2) suspicious or malicious activity. Unsuccessful attempts at network transit, in order to connect to command-and-control (C2), or engage in data exfiltration, may produce a burst of failed connections. This could also be due to unusually large amounts of reconnaissance or enumeration traffic. Denial-of-service attacks or traffic floods may also produce such a surge in traffic.", + "false_positives": [ + "A misconfgured network application or firewall may trigger this alert. Security scans or test cycles may trigger this alert." + ], + "from": "now-30m", + "interval": "15m", + "license": "Elastic License", + "machine_learning_job_id": "high-count-network-denies", + "name": "Spike in Firewall Denies", + "references": [ + "https://www.elastic.co/guide/en/security/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "eaa77d63-9679-4ce3-be25-3ba8b795e5fa", + "severity": "low", + "tags": [ + "Elastic", + "Network", + "Threat Detection", + "ML" + ], + "type": "machine_learning", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_events.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_events.json new file mode 100644 index 0000000000000..14aec268cc13b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_high_count_network_events.json @@ -0,0 +1,29 @@ +{ + "anomaly_threshold": 75, + "author": [ + "Elastic" + ], + "description": "A machine learning job detected an unusually large spike in network traffic. Such a burst of traffic, if not caused by a surge in business activity, can be due to suspicious or malicious activity. Large-scale data exfiltration may produce a burst of network traffic; this could also be due to unusually large amounts of reconnaissance or enumeration traffic. Denial-of-service attacks or traffic floods may also produce such a surge in traffic.", + "false_positives": [ + "Business workflows that occur very occasionally, and involve an unsual surge in network trafic, can trigger this alert. A new business workflow or a surge in business activity may trigger this alert. A misconfigured network application or firewall may trigger this alert." + ], + "from": "now-30m", + "interval": "15m", + "license": "Elastic License", + "machine_learning_job_id": "high-count-network-events", + "name": "Spike in Network Traffic", + "references": [ + "https://www.elastic.co/guide/en/security/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "b240bfb8-26b7-4e5e-924e-218144a3fa71", + "severity": "low", + "tags": [ + "Elastic", + "Network", + "Threat Detection", + "ML" + ], + "type": "machine_learning", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_rare_destination_country.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_rare_destination_country.json new file mode 100644 index 0000000000000..571c7e0d0d32c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_rare_destination_country.json @@ -0,0 +1,29 @@ +{ + "anomaly_threshold": 75, + "author": [ + "Elastic" + ], + "description": "A machine learning job detected a rare destination country name in the network logs. This can be due to initial access, persistence, command-and-control, or exfiltration activity. For example, when a user clicks on a link in a phishing email or opens a malicious document, a request may be sent to download and run a payload from a server in a country which does not normally appear in network traffic or business work-flows. Malware instances and persistence mechanisms may communicate with command-and-control (C2) infrastructure in their country of origin, which may be an unusual destination country for the source network.", + "false_positives": [ + "Business workflows that occur very occasionally, and involve a business relationship with an organization in a country that does not routinely appear in network events, can trigger this alert. A new business workflow with an organization in a country with which no workflows previously existed may trigger this alert - although the model will learn that the new destination country is no longer anomalous as the activity becomes ongoing. Business travelers who roam to many countries for brief periods may trigger this alert." + ], + "from": "now-30m", + "interval": "15m", + "license": "Elastic License", + "machine_learning_job_id": "rare-destination-country", + "name": "Network Traffic to Rare Destination Country", + "references": [ + "https://www.elastic.co/guide/en/security/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "35f86980-1fb1-4dff-b311-3be941549c8d", + "severity": "low", + "tags": [ + "Elastic", + "Network", + "Threat Detection", + "ML" + ], + "type": "machine_learning", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_spike_in_traffic_to_a_country.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_spike_in_traffic_to_a_country.json new file mode 100644 index 0000000000000..e1e571bbd1c99 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/ml_spike_in_traffic_to_a_country.json @@ -0,0 +1,29 @@ +{ + "anomaly_threshold": 75, + "author": [ + "Elastic" + ], + "description": "A machine learning job detected an unusually large spike in network activity to one destination country in the network logs. This could be due to unusually large amounts of reconnaissance or enumeration traffic. Data exfiltration activity may also produce such a surge in traffic to a destination country which does not normally appear in network traffic or business work-flows. Malware instances and persistence mechanisms may communicate with command-and-control (C2) infrastructure in their country of origin, which may be an unusual destination country for the source network.", + "false_positives": [ + "Business workflows that occur very occasionally, and involve an unusual surge in network traffic to one destination country, can trigger this alert. A new business workflow or a surge in business activity in a particular country may trigger this alert. Business travelers who roam to many countries for brief periods may trigger this alert if they engage in volumetric network activity." + ], + "from": "now-30m", + "interval": "15m", + "license": "Elastic License", + "machine_learning_job_id": "high-count-by-destination-country", + "name": "Spike in Network Traffic To a Country", + "references": [ + "https://www.elastic.co/guide/en/security/current/prebuilt-ml-jobs.html" + ], + "risk_score": 21, + "rule_id": "c7db5533-ca2a-41f6-a8b0-ee98abe0f573", + "severity": "low", + "tags": [ + "Elastic", + "Network", + "Threat Detection", + "ML" + ], + "type": "machine_learning", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_adobe_hijack_persistence.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_adobe_hijack_persistence.json index c42c48560c32f..66580e65df4a9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_adobe_hijack_persistence.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_adobe_hijack_persistence.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Adobe Hijack Persistence", - "query": "event.category:file and event.type:creation and 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 not process.name:msiexec.exe", + "query": "file where event.type == \"creation\" and\n file.path : (\"?:\\\\Program Files (x86)\\\\Adobe\\\\Acrobat Reader DC\\\\Reader\\\\AcroCEF\\\\RdrCEF.exe\",\n \"?:\\\\Program Files\\\\Adobe\\\\Acrobat Reader DC\\\\Reader\\\\AcroCEF\\\\RdrCEF.exe\") and\n not process.name : \"msiexec.exe\"\n", "risk_score": 21, "rule_id": "2bf78aa2-9c56-48de-b139-f169bf99cf86", "severity": "low", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json index d8faa12add9fa..f13f108cbd7a5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_azure_automation_webhook_created.json @@ -31,7 +31,6 @@ "Configuration Audit" ], "timestamp_override": "event.ingested", - "to": "now-25m", "type": "query", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_credential_access_modify_auth_module_or_config.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_credential_access_modify_auth_module_or_config.json index 7895594743de8..e1d8c05438b81 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_credential_access_modify_auth_module_or_config.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_credential_access_modify_auth_module_or_config.json @@ -14,7 +14,7 @@ "language": "kuery", "license": "Elastic License v2", "name": "Modification of Standard Authentication Module or Configuration", - "query": "event.category:file and event.type:change and (file.name:pam_*.so or file.path:(/etc/pam.d/* or /private/etc/pam.d/*)) and process.executable: (* and not ( /bin/yum or \"/usr/sbin/pam-auth-update\" or /usr/libexec/packagekitd or /usr/bin/dpkg or /usr/bin/vim or /usr/libexec/xpcproxy or /usr/bin/bsdtar or /usr/local/bin/brew ) )", + "query": "event.category:file and event.type:change and (file.name:pam_*.so or file.path:(/etc/pam.d/* or /private/etc/pam.d/*)) and process.executable: (* and not ( /bin/yum or \"/usr/sbin/pam-auth-update\" or /usr/libexec/packagekitd or /usr/bin/dpkg or /usr/bin/vim or /usr/libexec/xpcproxy or /usr/bin/bsdtar or /usr/local/bin/brew or /usr/bin/rsync or /usr/bin/yum or /var/lib/docker/*/bin/yum or /var/lib/docker/*/bin/dpkg or ./merged/var/lib/docker/*/bin/dpkg or \"/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/XPCServices/package_script_service.xpc/Contents/MacOS/package_script_service\" ) ) and not file.path: ( /tmp/snap.rootfs_*/pam_*.so or /tmp/newroot/lib/*/pam_*.so or /private/var/folders/*/T/com.apple.fileprovider.ArchiveService/TemporaryItems/*/lib/security/pam_*.so or /tmp/newroot/usr/lib64/security/pam_*.so )", "references": [ "https://github.com/zephrax/linux-pam-backdoor", "https://github.com/eurialo/pambd", @@ -67,5 +67,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 1 + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_cron_jobs_creation_and_runtime.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_cron_jobs_creation_and_runtime.json deleted file mode 100644 index 982f3b505c8f5..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_cron_jobs_creation_and_runtime.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Identifies the creation or execution of a cron job. Adversaries may abuse cron jobs to perform task scheduling for initial or recurring execution of malicious code.", - "false_positives": [ - "Legitimate software or scripts using cron jobs for recurring tasks." - ], - "from": "now-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Potential Persistence via Cron Job", - "query": "event.category:process and event.type:(start or process_started or info) and not user.name:root and ((process.name:crontab and not process.args:(\"-l\" or \"-r\" or \"-e\" or \"-help\" or \"-h\")) or (process.parent.name:cron and not process.name:\"running job\" and not process.executable:(/Applications/Docker.app/Contents/Resources/bin/docker or /usr/bin/killall or /usr/sbin/sendmail or /usr/bin/env or /usr/bin/timeshift or /bin/rm)))", - "references": [ - "https://archive.f-secure.com/weblog/archives/00002576.html", - "https://ss64.com/osx/crontab.html" - ], - "risk_score": 21, - "rule_id": "b1c14366-f4f8-49a0-bcbb-51d2de8b0bb8", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "macOS", - "Threat Detection", - "Persistence" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0003", - "name": "Persistence", - "reference": "https://attack.mitre.org/tactics/TA0003/" - }, - "technique": [ - { - "id": "T1053", - "name": "Scheduled Task/Job", - "reference": "https://attack.mitre.org/techniques/T1053/", - "subtechnique": [ - { - "id": "T1053.003", - "name": "Cron", - "reference": "https://attack.mitre.org/techniques/T1053/003/" - } - ] - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 1 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json index 2cd50efc59fb1..f1ece4525079a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_ec2_network_acl_creation.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS EC2 Network Access Control List Creation", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(CreateNetworkAcl or CreateNetworkAclEntry) and event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:ec2.amazonaws.com and event.action:(CreateNetworkAcl or CreateNetworkAclEntry) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/create-network-acl.html", "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateNetworkAcl.html", @@ -53,5 +53,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_evasion_registry_startup_shell_folder_modified.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_evasion_registry_startup_shell_folder_modified.json new file mode 100644 index 0000000000000..ec985807b3137 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_evasion_registry_startup_shell_folder_modified.json @@ -0,0 +1,54 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies suspicious startup shell folder modifications to change the default Startup directory in order to bypass detections monitoring file creation in the Windows Startup folder.", + "from": "now-9m", + "index": [ + "winlogbeat-*", + "logs-endpoint.events.*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Suspicious Startup Shell Folder Modification", + "note": "Verify file creation events in the new Windows Startup folder location.", + "query": "registry where\n registry.path : (\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\User Shell Folders\\\\Common Startup\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell Folders\\\\Common Startup\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\User Shell Folders\\\\Startup\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell Folders\\\\Startup\"\n ) and\n registry.data.strings != null and\n /* Normal Startup Folder Paths */\n not registry.data.strings : (\n \"C:\\\\ProgramData\\\\Microsoft\\\\Windows\\\\Start Menu\\\\Programs\\\\Startup\",\n \"%ProgramData%\\\\Microsoft\\\\Windows\\\\Start Menu\\\\Programs\\\\Startup\",\n \"%USERPROFILE%\\\\AppData\\\\Roaming\\\\Microsoft\\\\Windows\\\\Start Menu\\\\Programs\\\\Startup\",\n \"C:\\\\Users\\\\*\\\\AppData\\\\Roaming\\\\Microsoft\\\\Windows\\\\Start Menu\\\\Programs\\\\Startup\"\n )\n", + "risk_score": 73, + "rule_id": "c8b150f0-0164-475b-a75e-74b47800a9ff", + "severity": "high", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Persistence" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0003", + "name": "Persistence", + "reference": "https://attack.mitre.org/tactics/TA0003/" + }, + "technique": [ + { + "id": "T1547", + "name": "Boot or Logon Autostart Execution", + "reference": "https://attack.mitre.org/techniques/T1547/", + "subtechnique": [ + { + "id": "T1547.001", + "name": "Registry Run Keys / Startup Folder", + "reference": "https://attack.mitre.org/techniques/T1547/001/" + } + ] + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gpo_schtask_service_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gpo_schtask_service_creation.json index 042e42fdbecd6..86b1cd3e71eaf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gpo_schtask_service_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_gpo_schtask_service_creation.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Creation or Modification of a new GPO Scheduled Task or Service", - "query": "event.category:file and not event.type:deletion and file.path:(C\\:\\\\Windows\\\\SYSVOL\\\\domain\\\\Policies\\\\*\\\\MACHINE\\\\Preferences\\\\ScheduledTasks\\\\ScheduledTasks.xml or C\\:\\\\Windows\\\\SYSVOL\\\\domain\\\\Policies\\\\*\\\\MACHINE\\\\Preferences\\\\Preferences\\\\Services\\\\Services.xml) and not process.name:dfsrs.exe", + "query": "file where event.type != \"deletion\" and\n file.path : (\"?:\\\\Windows\\\\SYSVOL\\\\domain\\\\Policies\\\\*\\\\MACHINE\\\\Preferences\\\\ScheduledTasks\\\\ScheduledTasks.xml\",\n \"?:\\\\Windows\\\\SYSVOL\\\\domain\\\\Policies\\\\*\\\\MACHINE\\\\Preferences\\\\Preferences\\\\Services\\\\Services.xml\") and\n not process.name : \"dfsrs.exe\"\n", "risk_score": 21, "rule_id": "c0429aa8-9974-42da-bfb6-53a0a515a145", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json index ceff8cc41dc91..155be18fe8add 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_iam_group_creation.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS IAM Group Creation", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:CreateGroup and event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:iam.amazonaws.com and event.action:CreateGroup and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-group.html", "https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateGroup.html" @@ -58,5 +58,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_kernel_module_activity.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_kernel_module_activity.json deleted file mode 100644 index 5a453faa1fafc..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_kernel_module_activity.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "author": [ - "Elastic" - ], - "description": "Identifies loadable kernel module errors, which are often indicative of potential persistence attempts.", - "false_positives": [ - "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-9m", - "index": [ - "auditbeat-*", - "logs-endpoint.events.*" - ], - "language": "kuery", - "license": "Elastic License v2", - "name": "Persistence via Kernel Module Modification", - "query": "event.category:process and event.type:(start or process_started) and process.name:(insmod or kmod or modprobe or rmod)", - "references": [ - "https://www.hackers-arise.com/single-post/2017/11/03/Linux-for-Hackers-Part-10-Loadable-Kernel-Modules-LKM" - ], - "risk_score": 21, - "rule_id": "81cc58f5-8062-49a2-ba84-5cc4b4d31c40", - "severity": "low", - "tags": [ - "Elastic", - "Host", - "Linux", - "Threat Detection", - "Persistence" - ], - "threat": [ - { - "framework": "MITRE ATT&CK", - "tactic": { - "id": "TA0003", - "name": "Persistence", - "reference": "https://attack.mitre.org/tactics/TA0003/" - }, - "technique": [ - { - "id": "T1547", - "name": "Boot or Logon Autostart Execution", - "reference": "https://attack.mitre.org/techniques/T1547/", - "subtechnique": [ - { - "id": "T1547.006", - "name": "Kernel Modules and Extensions", - "reference": "https://attack.mitre.org/techniques/T1547/006/" - } - ] - } - ] - } - ], - "timestamp_override": "event.ingested", - "type": "query", - "version": 8 -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_commands.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_job_creation.json similarity index 53% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_commands.json rename to x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_job_creation.json index 769aba1ad647c..6e656209fd055 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_commands.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_job_creation.json @@ -2,9 +2,9 @@ "author": [ "Elastic" ], - "description": "A scheduled task can be used by an adversary to establish persistence, move laterally, and/or escalate privileges.", + "description": "A job can be used to schedule programs or scripts to be executed at a specified date and time. Adversaries may abuse task scheduling functionality to facilitate initial or recurring execution of malicious code.", "false_positives": [ - "Legitimate scheduled tasks may be created during installation of new software." + "Legitimate scheduled jobs may be created during installation of new software." ], "from": "now-9m", "index": [ @@ -12,13 +12,13 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", - "name": "Local Scheduled Task Commands", - "query": "event.category:process and event.type:(start or process_started) and process.name:schtasks.exe and process.args:(-change or -create or -run or -s or /S or /change or /create or /run)", - "risk_score": 21, - "rule_id": "afcce5ad-65de-4ed2-8516-5e093d3ac99a", - "severity": "low", + "name": "Persistence via Scheduled Job Creation", + "query": "file where event.type != \"deletion\" and\n file.path : \"?:\\\\Windows\\\\Tasks\\\\*\" and file.extension : \"job\"\n", + "risk_score": 47, + "rule_id": "1327384f-00f3-44d5-9a8c-2373ba071e92", + "severity": "medium", "tags": [ "Elastic", "Host", @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 1 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_creation.json new file mode 100644 index 0000000000000..8b6fa370f4ab9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_local_scheduled_task_creation.json @@ -0,0 +1,56 @@ +{ + "author": [ + "Elastic" + ], + "description": "A scheduled task can be used by an adversary to establish persistence, move laterally, and/or escalate privileges.", + "false_positives": [ + "Legitimate scheduled tasks may be created during installation of new software." + ], + "from": "now-9m", + "index": [ + "winlogbeat-*", + "logs-endpoint.events.*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Local Scheduled Task Creation", + "query": "sequence with maxspan=1m\n [process where event.type != \"end\" and\n ((process.name : (\"cmd.exe\", \"wscript.exe\", \"rundll32.exe\", \"regsvr32.exe\", \"wmic.exe\", \"mshta.exe\",\n \"powershell.exe\", \"pwsh.exe\", \"WmiPrvSe.exe\", \"wsmprovhost.exe\", \"winrshost.exe\") or\n process.pe.original_file_name : (\"cmd.exe\", \"wscript.exe\", \"rundll32.exe\", \"regsvr32.exe\", \"wmic.exe\", \"mshta.exe\",\n \"powershell.exe\", \"pwsh.exe\", \"WmiPrvSe.exe\", \"wsmprovhost.exe\",\n \"winrshost.exe\")) or\n process.code_signature.trusted == false)] by process.entity_id\n [process where event.type == \"start\" and\n (process.name : \"schtasks.exe\" or process.pe.original_file_name == \"schtasks.exe\") and\n process.args : (\"/create\", \"-create\") and process.args : (\"/RU\", \"/SC\", \"/TN\", \"/TR\", \"/F\", \"/XML\") and\n /* exclude SYSTEM SIDs - look for task creations by non-SYSTEM user */\n not user.id : (\"S-1-5-18\", \"S-1-5-19\", \"S-1-5-20\")] by process.parent.entity_id\n", + "risk_score": 21, + "rule_id": "afcce5ad-65de-4ed2-8516-5e093d3ac99a", + "severity": "low", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Persistence" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0003", + "name": "Persistence", + "reference": "https://attack.mitre.org/tactics/TA0003/" + }, + "technique": [ + { + "id": "T1053", + "name": "Scheduled Task/Job", + "reference": "https://attack.mitre.org/techniques/T1053/", + "subtechnique": [ + { + "id": "T1053.005", + "name": "Scheduled Task", + "reference": "https://attack.mitre.org/techniques/T1053/005/" + } + ] + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 8 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_login_logout_hooks_defaults.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_login_logout_hooks_defaults.json index 662ea6678a195..ade4f76e3c0dc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_login_logout_hooks_defaults.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_login_logout_hooks_defaults.json @@ -11,7 +11,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Persistence via Login or Logout Hook", - "query": "process where event.type in (\"start\", \"process_started\") and\n process.name == \"defaults\" and process.args == \"write\" and process.args in (\"LoginHook\", \"LogoutHook\")\n", + "query": "process where event.type == \"start\" and\n process.name == \"defaults\" and process.args == \"write\" and process.args in (\"LoginHook\", \"LogoutHook\") and\n not process.args :\n (\n \"Support/JAMF/ManagementFrameworkScripts/logouthook.sh\",\n \"Support/JAMF/ManagementFrameworkScripts/loginhook.sh\",\n \"/Library/Application Support/JAMF/ManagementFrameworkScripts/logouthook.sh\",\n \"/Library/Application Support/JAMF/ManagementFrameworkScripts/loginhook.sh\"\n )\n", "references": [ "https://www.virusbulletin.com/uploads/pdf/conference_slides/2014/Wardle-VB2014.pdf", "https://www.manpagez.com/man/1/defaults/" @@ -45,5 +45,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json index a7ea7424721a5..d0d3200d00058 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_rds_cluster_creation.json @@ -16,7 +16,7 @@ "license": "Elastic License v2", "name": "AWS RDS Cluster Creation", "note": "The AWS Filebeat module must be enabled to use this rule.", - "query": "event.action:(CreateDBCluster or CreateGlobalCluster) and event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.outcome:success", + "query": "event.dataset:aws.cloudtrail and event.provider:rds.amazonaws.com and event.action:(CreateDBCluster or CreateGlobalCluster) and event.outcome:success", "references": [ "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/create-db-cluster.html", "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBCluster.html", @@ -62,5 +62,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 5 + "version": 6 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_run_key_and_startup_broad.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_run_key_and_startup_broad.json index 690afcf5a8025..46bbeb00f4d05 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_run_key_and_startup_broad.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_run_key_and_startup_broad.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Startup or Run Key Registry Modification", - "query": "/* uncomment length once stable */\nregistry where /* length(registry.data.strings) > 0 and */\n registry.path : (\n /* Machine Hive */\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\User Shell Folders\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell Folders\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\\\\*\", \n /* Users Hive */\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\User Shell Folders\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell Folders\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\\\\*\"\n ) and\n /* add here common legit changes without making too restrictive as this is one of the most abused AESPs */\n not registry.data.strings : \"ctfmon.exe /n\" and\n not (registry.value : \"Application Restart #*\" and process.name : \"csrss.exe\") and\n user.domain != \"NT AUTHORITY\" and\n not registry.data.strings : (\"C:\\\\Program Files\\\\*.exe\", \"C:\\\\Program Files (x86)\\\\*.exe\") and\n not process.executable : (\"C:\\\\Windows\\\\System32\\\\msiexec.exe\", \"C:\\\\Windows\\\\SysWOW64\\\\msiexec.exe\")\n", + "query": "registry where registry.data.strings != null and\n registry.path : (\n /* Machine Hive */\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\", \n \"HKLM\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\\\\*\", \n /* Users Hive */\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\", \n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\\\\*\"\n ) and\n /* add common legitimate changes without being too restrictive as this is one of the most abused AESPs */\n not registry.data.strings : \"ctfmon.exe /n\" and\n not (registry.value : \"Application Restart #*\" and process.name : \"csrss.exe\") and\n user.id not in (\"S-1-5-18\", \"S-1-5-19\", \"S-1-5-20\") and\n not registry.data.strings : (\"?:\\\\Program Files\\\\*.exe\", \"?:\\\\Program Files (x86)\\\\*.exe\") and\n not process.executable : (\"?:\\\\Windows\\\\System32\\\\msiexec.exe\", \"?:\\\\Windows\\\\SysWOW64\\\\msiexec.exe\") and\n not (process.name : \"OneDriveSetup.exe\" and\n registry.value : (\"Delete Cached Standalone Update Binary\", \"Delete Cached Update Binary\", \"amd64\", \"Uninstall *\") and\n registry.data.strings : \"?:\\\\Windows\\\\system32\\\\cmd.exe /q /c * \\\"?:\\\\Users\\\\*\\\\AppData\\\\Local\\\\Microsoft\\\\OneDrive\\\\*\\\"\")\n", "risk_score": 21, "rule_id": "97fc44d3-8dae-4019-ae83-298c3015600f", "severity": "low", @@ -49,5 +49,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_services_registry.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_services_registry.json index 8ad7ef7a22c9b..64b9aba81551d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_services_registry.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_services_registry.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "Unusual Persistence via Services Registry", - "query": "registry where registry.path : (\"HKLM\\\\SYSTEM\\\\ControlSet*\\\\Services\\\\*\\\\ServiceDLL\", \"HKLM\\\\SYSTEM\\\\ControlSet*\\\\Services\\\\*\\\\ImagePath\") and\n not registry.data.strings : (\"C:\\\\windows\\\\system32\\\\Drivers\\\\*.sys\", \n \"\\\\SystemRoot\\\\System32\\\\drivers\\\\*.sys\", \n \"system32\\\\DRIVERS\\\\USBSTOR\") and\n not (process.name : \"procexp??.exe\" and registry.data.strings : \"C:\\\\*\\\\procexp*.sys\") and\n not process.executable : (\"C:\\\\Program Files*\\\\*.exe\", \n \"C:\\\\Windows\\\\System32\\\\svchost.exe\", \n \"C:\\\\Windows\\\\winsxs\\\\*\\\\TiWorker.exe\", \n \"C:\\\\Windows\\\\System32\\\\drvinst.exe\", \n \"C:\\\\Windows\\\\System32\\\\services.exe\", \n \"C:\\\\Windows\\\\System32\\\\msiexec.exe\", \n \"C:\\\\Windows\\\\System32\\\\regsvr32.exe\")\n", + "query": "registry where registry.path : (\"HKLM\\\\SYSTEM\\\\ControlSet*\\\\Services\\\\*\\\\ServiceDLL\", \"HKLM\\\\SYSTEM\\\\ControlSet*\\\\Services\\\\*\\\\ImagePath\") and\n not registry.data.strings : (\"?:\\\\windows\\\\system32\\\\Drivers\\\\*.sys\",\n \"\\\\SystemRoot\\\\System32\\\\drivers\\\\*.sys\",\n \"\\\\??\\\\?:\\\\Windows\\\\system32\\\\Drivers\\\\*.SYS\",\n \"system32\\\\DRIVERS\\\\USBSTOR\") and\n not (process.name : \"procexp??.exe\" and registry.data.strings : \"?:\\\\*\\\\procexp*.sys\") and\n not process.executable : (\"?:\\\\Program Files\\\\*.exe\",\n \"?:\\\\Program Files (x86)\\\\*.exe\",\n \"?:\\\\Windows\\\\System32\\\\svchost.exe\",\n \"?:\\\\Windows\\\\winsxs\\\\*\\\\TiWorker.exe\",\n \"?:\\\\Windows\\\\System32\\\\drvinst.exe\",\n \"?:\\\\Windows\\\\System32\\\\services.exe\",\n \"?:\\\\Windows\\\\System32\\\\msiexec.exe\",\n \"?:\\\\Windows\\\\System32\\\\regsvr32.exe\")\n", "risk_score": 21, "rule_id": "403ef0d3-8259-40c9-a5b6-d48354712e49", "severity": "low", @@ -49,5 +49,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 3 + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_system_shells_via_services.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_system_shells_via_services.json index 8c26a67c65a64..709396a5eaf2f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_system_shells_via_services.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_system_shells_via_services.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "System Shells via Services", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:services.exe and process.name:(cmd.exe or powershell.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"services.exe\" and\n process.name : (\"cmd.exe\", \"powershell.exe\") and\n \n /* Third party FP's */\n not process.args : \"NVDisplay.ContainerLocalSystem\"\n", "risk_score": 47, "rule_id": "0022d47d-39c7-4f69-a232-4fe9dc7a3acd", "severity": "medium", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 8 + "type": "eql", + "version": 9 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_added_to_privileged_group_ad.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_added_to_privileged_group_ad.json index 875745e0d161a..c63d96b106a01 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_added_to_privileged_group_ad.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_added_to_privileged_group_ad.json @@ -7,19 +7,18 @@ "from": "now-9m", "index": [ "winlogbeat-*", - "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "User Added to Privileged Group in Active Directory", - "query": "event.category:iam and event.action:\"added-member-to-group\" and group.name:(Administrators or \"Local Administrators\" or \"Domain Admins\" or \"Enterprise Admins\" or \"Backup Admins\" or \"Schema Admins\" or \"DnsAdmins\")", + "query": "iam where event.action == \"added-member-to-group\" and\n group.name : (\"Admin*\",\n \"Local Administrators\",\n \"Domain Admins\",\n \"Enterprise Admins\",\n \"Backup Admins\",\n \"Schema Admins\",\n \"DnsAdmins\",\n \"Exchange Organization Administrators\")\n", "references": [ "https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-b--privileged-accounts-and-groups-in-active-directory" ], - "risk_score": 21, + "risk_score": 47, "rule_id": "5cd8e1f7-0050-4afc-b2df-904e40b2f5ae", - "severity": "low", + "severity": "medium", "tags": [ "Elastic", "Host", @@ -52,6 +51,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 1 + "type": "eql", + "version": 2 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_creation.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_creation.json index 21a297c3fd8ef..0e2b01a1967d2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_creation.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_user_account_creation.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "User Account Creation", - "query": "event.category:process and event.type:(start or process_started) and process.name:(net.exe or net1.exe) and not process.parent.name:net.exe and process.args:(user and (/ad or /add))", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.name : (\"net.exe\", \"net1.exe\") and\n not process.parent.name : \"net.exe\" and\n (process.args : \"user\" and process.args : (\"/ad\", \"/add\"))\n", "risk_score": 21, "rule_id": "1aa9181a-492b-4c01-8b16-fa0735786b2b", "severity": "low", @@ -41,6 +41,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_application_shimming.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_application_shimming.json index 2143dd2743240..13c8829869c8d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_application_shimming.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_application_shimming.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Potential Application Shimming via Sdbinst", - "query": "event.category:process and event.type:(start or process_started) and process.name:sdbinst.exe", + "query": "process where event.type in (\"start\", \"process_started\") and process.name : \"sdbinst.exe\"\n", "risk_score": 21, "rule_id": "fd4a992d-6130-4802-9ff8-829b89ae801f", "severity": "low", @@ -70,6 +70,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_bits_job_notify_command.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_bits_job_notify_command.json new file mode 100644 index 0000000000000..6cd322d20d4e8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_bits_job_notify_command.json @@ -0,0 +1,52 @@ +{ + "author": [ + "Elastic" + ], + "description": "An adversary can use the Background Intelligent Transfer Service (BITS) SetNotifyCmdLine method to execute a program that runs after a job finishes transferring data or after a job enters a specified state in order to persist on a system.", + "from": "now-9m", + "index": [ + "logs-endpoint.events.*", + "winlogbeat-*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Persistence via BITS Job Notify Cmdline", + "query": "process where event.type == \"start\" and\n process.parent.name : \"svchost.exe\" and process.parent.args : \"BITS\" and\n not process.executable :\n (\"?:\\\\Windows\\\\System32\\\\WerFaultSecure.exe\",\n \"?:\\\\Windows\\\\System32\\\\WerFault.exe\",\n \"?:\\\\Windows\\\\System32\\\\wermgr.exe\",\n \"?:\\\\WINDOWS\\\\system32\\\\directxdatabaseupdater.exe\")\n", + "references": [ + "https://pentestlab.blog/2019/10/30/persistence-bits-jobs/", + "https://docs.microsoft.com/en-us/windows/win32/api/bits1_5/nf-bits1_5-ibackgroundcopyjob2-setnotifycmdline", + "https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/bitsadmin-setnotifycmdline", + "https://www.elastic.co/blog/hunting-for-persistence-using-elastic-security-part-2" + ], + "risk_score": 47, + "rule_id": "c3b915e0-22f3-4bf7-991d-b643513c722f", + "severity": "medium", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Persistence" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0003", + "name": "Persistence", + "reference": "https://attack.mitre.org/tactics/TA0003/" + }, + "technique": [ + { + "id": "T1197", + "name": "BITS Jobs", + "reference": "https://attack.mitre.org/techniques/T1197/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_telemetrycontroller_scheduledtask_hijack.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_telemetrycontroller_scheduledtask_hijack.json index 51b7f34fdc7b9..dca20728b40fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_telemetrycontroller_scheduledtask_hijack.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_telemetrycontroller_scheduledtask_hijack.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Persistence via TelemetryController Scheduled Task Hijack", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:(CompatTelRunner.exe or compattelrunner.exe) and process.args:-cv* and not process.name:(conhost.exe or DeviceCensus.exe or devicecensus.exe or CompatTelRunner.exe or compattelrunner.exe or DismHost.exe or dismhost.exe or rundll32.exe or powershell.exe)", + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"CompatTelRunner.exe\" and process.args : \"-cv*\" and\n not process.name : (\"conhost.exe\",\n \"DeviceCensus.exe\",\n \"CompatTelRunner.exe\",\n \"DismHost.exe\",\n \"rundll32.exe\",\n \"powershell.exe\")\n", "references": [ "https://www.trustedsec.com/blog/abusing-windows-telemetry-for-persistence/?utm_content=131234033&utm_medium=social&utm_source=twitter&hss_channel=tw-403811306" ], @@ -44,6 +44,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 4 + "type": "eql", + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_wmi_stdregprov_run_services.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_wmi_stdregprov_run_services.json new file mode 100644 index 0000000000000..3fdb1e1ebc96d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/persistence_via_wmi_stdregprov_run_services.json @@ -0,0 +1,83 @@ +{ + "author": [ + "Elastic" + ], + "description": "Identifies use of the Windows Management Instrumentation StdRegProv (registry provider) to modify commonly abused registry locations for persistence.", + "from": "now-9m", + "index": [ + "logs-endpoint.events.*", + "winlogbeat-*", + "logs-windows.*" + ], + "language": "eql", + "license": "Elastic License v2", + "name": "Persistence via WMI Standard Registry Provider", + "query": "registry where \n registry.data.strings != null and process.name : \"WmiPrvSe.exe\" and\n registry.path : (\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\",\n \"HKLM\\\\Software\\\\WOW6432Node\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\\\\*\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run\\\\*\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\",\n \"HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnce\\\\*\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\RunOnceEx\\\\*\",\n \"HKLM\\\\SYSTEM\\\\*ControlSet*\\\\Services\\\\*\\\\ServiceDLL\",\n \"HKLM\\\\SYSTEM\\\\*ControlSet*\\\\Services\\\\*\\\\ImagePath\",\n \"HKEY_USERS\\\\*\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\\\\*\", \n \"HKEY_USERS\\\\*\\\\Environment\\\\UserInitMprLogonScript\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Windows\\\\Load\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Winlogon\\\\Shell\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\System\\\\Shell\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\System\\\\Scripts\\\\Logoff\\\\Script\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\System\\\\Scripts\\\\Logon\\\\Script\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\System\\\\Scripts\\\\Shutdown\\\\Script\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\System\\\\Scripts\\\\Startup\\\\Script\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Ctf\\\\LangBarAddin\\\\*\\\\FilePath\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Internet Explorer\\\\Extensions\\\\*\\\\Exec\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Internet Explorer\\\\Extensions\\\\*\\\\Script\", \n \"HKEY_USERS\\\\*\\\\SOFTWARE\\\\Microsoft\\\\Command Processor\\\\Autorun\"\n )\n", + "references": [ + "https://docs.microsoft.com/en-us/previous-versions/windows/desktop/regprov/stdregprov" + ], + "risk_score": 73, + "rule_id": "70d12c9c-0dbd-4a1a-bc44-1467502c9cf6", + "severity": "high", + "tags": [ + "Elastic", + "Host", + "Windows", + "Threat Detection", + "Persistence" + ], + "threat": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0003", + "name": "Persistence", + "reference": "https://attack.mitre.org/tactics/TA0003/" + }, + "technique": [ + { + "id": "T1547", + "name": "Boot or Logon Autostart Execution", + "reference": "https://attack.mitre.org/techniques/T1547/", + "subtechnique": [ + { + "id": "T1547.001", + "name": "Registry Run Keys / Startup Folder", + "reference": "https://attack.mitre.org/techniques/T1547/001/" + } + ] + }, + { + "id": "T1543", + "name": "Create or Modify System Process", + "reference": "https://attack.mitre.org/techniques/T1543/", + "subtechnique": [ + { + "id": "T1543.003", + "name": "Windows Service", + "reference": "https://attack.mitre.org/techniques/T1543/003/" + } + ] + } + ] + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0002", + "name": "Execution", + "reference": "https://attack.mitre.org/tactics/TA0002/" + }, + "technique": [ + { + "id": "T1047", + "name": "Windows Management Instrumentation", + "reference": "https://attack.mitre.org/techniques/T1047/" + } + ] + } + ], + "timestamp_override": "event.ingested", + "type": "eql", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_service_suspicious_file.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_service_suspicious_file.json index ac6e8c470ef2f..7197c7f979a38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_service_suspicious_file.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_service_suspicious_file.json @@ -9,10 +9,10 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious PrintSpooler Service Executable File Creation", - "query": "event.category:file and not event.type:deletion and process.name:spoolsv.exe and file.extension:(exe or dll) and not file.path:(C\\:\\\\Windows\\\\System32\\\\spool\\\\* or C\\:\\\\Windows\\\\Temp\\\\* or C\\:\\\\Users\\\\*)", + "query": "file where event.type != \"deletion\" and process.name : \"spoolsv.exe\" and\n file.extension : (\"exe\", \"dll\") and\n not file.path : (\"?:\\\\Windows\\\\System32\\\\spool\\\\*\", \"?:\\\\Windows\\\\Temp\\\\*\", \"?:\\\\Users\\\\*\")\n", "references": [ "https://voidsec.com/cve-2020-1337-printdemon-is-dead-long-live-printdemon/", "https://www.thezdi.com/blog/2020/7/8/cve-2020-1300-remote-code-execution-through-microsoft-windows-cab-files" @@ -45,6 +45,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_suspicious_spl_file.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_suspicious_spl_file.json index 964206f86d9cf..52a835b3d6fd9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_suspicious_spl_file.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_printspooler_suspicious_spl_file.json @@ -9,11 +9,11 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Suspicious PrintSpooler SPL File Created", "note": "Refer to CVEs, CVE-2020-1048 and CVE-2020-1337 for further information on the vulnerability and exploit. Verify that the relevant system is patched.", - "query": "event.category:file and not event.type:deletion and file.extension:(spl or SPL) and file.path:C\\:\\\\Windows\\\\System32\\\\spool\\\\PRINTERS\\\\* and not process.name:(spoolsv.exe or printfilterpipelinesvc.exe or PrintIsolationHost.exe or splwow64.exe or msiexec.exe or poqexec.exe)", + "query": "file where event.type != \"deletion\" and\n file.extension : \"spl\" and\n file.path : \"?:\\\\Windows\\\\System32\\\\spool\\\\PRINTERS\\\\*\" and\n not process.name : (\"spoolsv.exe\",\n \"printfilterpipelinesvc.exe\",\n \"PrintIsolationHost.exe\",\n \"splwow64.exe\",\n \"msiexec.exe\",\n \"poqexec.exe\")\n", "references": [ "https://safebreach.com/Post/How-we-bypassed-CVE-2020-1048-Patch-and-got-CVE-2020-1337" ], @@ -45,6 +45,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 3 + "type": "eql", + "version": 4 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_setuid_setgid_bit_set_via_chmod.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_setuid_setgid_bit_set_via_chmod.json index 844fa83e20fff..67633985221e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_setuid_setgid_bit_set_via_chmod.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_setuid_setgid_bit_set_via_chmod.json @@ -12,7 +12,7 @@ "license": "Elastic License v2", "max_signals": 33, "name": "Setuid / Setgid Bit Set via chmod", - "query": "event.category:process AND event.type:(start OR process_started) AND process.name:chmod AND process.args:(\"+s\" OR \"u+s\" OR /4[0-9]{3}/ OR g+s OR /2[0-9]{3}/)", + "query": "event.category:process AND event.type:(start OR process_started) AND process.name:chmod AND process.args:(\"+s\" OR \"u+s\" OR /4[0-9]{3}/ OR g+s OR /2[0-9]{3}/) AND NOT process.args: ( /.*\\/Applications\\/VirtualBox.app\\/.+/ OR /\\/usr\\/local\\/lib\\/python.+/ OR /\\/var\\/folders\\/.+\\/FP.*nstallHelper/ OR /\\/Library\\/Filesystems\\/.+/ OR /\\/usr\\/lib\\/virtualbox\\/.+/ OR /\\/Library\\/Application.*/ OR \"/run/postgresql\" OR \"/var/crash\" OR \"/var/run/postgresql\" OR /\\/usr\\/bin\\/.+/ OR /\\/usr\\/local\\/share\\/.+/ OR /\\/Applications\\/.+/ OR /\\/usr\\/libexec\\/.+/ OR \"/var/metrics\" OR /\\/var\\/lib\\/dpkg\\/.+/ OR /\\/run\\/log\\/journal\\/.*/ OR \\/Users\\/*\\/.minikube\\/bin\\/docker-machine-driver-hyperkit ) AND NOT process.parent.executable: ( /\\/var\\/lib\\/docker\\/.+/ OR \"/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/XPCServices/package_script_service.xpc/Contents/MacOS/package_script_service\" OR \"/var/lib/dpkg/info/whoopsie.postinst\" )", "risk_score": 21, "rule_id": "8a1b0278-0f9a-487d-96bd-d4833298e87a", "severity": "low", @@ -59,5 +59,5 @@ ], "timestamp_override": "event.ingested", "type": "query", - "version": 7 + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_diskcleanup_hijack.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_diskcleanup_hijack.json index c127302c2e086..9bdd9375b89b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_diskcleanup_hijack.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_diskcleanup_hijack.json @@ -12,7 +12,7 @@ "language": "eql", "license": "Elastic License v2", "name": "UAC Bypass via DiskCleanup Scheduled Task Hijack", - "query": "process where event.type in (\"start\", \"process_started\") and\nprocess.args:\"/autoclean\" and process.args:\"/d\" and\nnot process.executable : (\"C:\\\\Windows\\\\System32\\\\cleanmgr.exe\", \"C:\\\\Windows\\\\SysWOW64\\\\cleanmgr.exe\")\n", + "query": "process where event.type == \"start\" and\n process.args : \"/autoclean\" and process.args : \"/d\" and\n not process.executable : (\"C:\\\\Windows\\\\System32\\\\cleanmgr.exe\",\n \"C:\\\\Windows\\\\SysWOW64\\\\cleanmgr.exe\",\n \"C:\\\\Windows\\\\System32\\\\taskhostw.exe\")\n", "risk_score": 47, "rule_id": "1dcc51f6-ba26-49e7-9ef4-2655abb2361e", "severity": "medium", @@ -49,5 +49,5 @@ ], "timestamp_override": "event.ingested", "type": "eql", - "version": 4 + "version": 5 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_event_viewer.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_event_viewer.json index fe6f6ff854ab4..08ebede619793 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_event_viewer.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/privilege_escalation_uac_bypass_event_viewer.json @@ -9,13 +9,13 @@ "logs-endpoint.events.*", "logs-windows.*" ], - "language": "kuery", + "language": "eql", "license": "Elastic License v2", "name": "Bypass UAC via Event Viewer", - "query": "event.category:process and event.type:(start or process_started) and process.parent.name:eventvwr.exe and not process.executable:(\"C:\\Windows\\SysWOW64\\mmc.exe\" or \"C:\\Windows\\System32\\mmc.exe\")", - "risk_score": 21, + "query": "process where event.type in (\"start\", \"process_started\") and\n process.parent.name : \"eventvwr.exe\" and\n not process.executable : \n (\"?:\\\\Windows\\\\SysWOW64\\\\mmc.exe\", \n \"?:\\\\Windows\\\\System32\\\\mmc.exe\",\n \"?:\\\\Windows\\\\SysWOW64\\\\WerFault.exe\",\n \"?:\\\\Windows\\\\System32\\\\WerFault.exe\")\n", + "risk_score": 73, "rule_id": "31b4c719-f2b4-41f6-a9bd-fce93c2eaf62", - "severity": "low", + "severity": "high", "tags": [ "Elastic", "Host", @@ -48,6 +48,6 @@ } ], "timestamp_override": "event.ingested", - "type": "query", - "version": 7 + "type": "eql", + "version": 8 } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/threat_intel_module_match.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/threat_intel_module_match.json new file mode 100644 index 0000000000000..ab99f9c69c847 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/threat_intel_module_match.json @@ -0,0 +1,198 @@ +{ + "author": [ + "Elastic" + ], + "description": "This rule is triggered when indicators from the Threat Intel Filebeat module has a match against local file or network observations.", + "from": "now-10m", + "index": [ + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "interval": "9m", + "language": "kuery", + "license": "Elastic License v2", + "name": "Threat Intel Filebeat Module Indicator Match", + "note": "## Triage and Analysis\nIf an indicator matches a local observation, the following enriched fields will be generated to identify the indicator, field, and type matched.\n\n- `threatintel.indicator.matched.atomic` - this identifies the atomic indicator that matched the local observation\n- `threatintel.indicator.matched.field` - this identifies the indicator field that matched the local observation\n- `threatintel.indicator.matched.type` - this identifies the indicator type that matched the local observation\n", + "query": "file.hash.*:* or file.pe.imphash:* or source.ip:* or destination.ip:* or url.full:* or registry.path:*", + "references": [ + "https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-threatintel.html" + ], + "risk_score": 99, + "rule_id": "dc672cb7-d5df-4d1f-a6d7-0841b1caafb9", + "severity": "critical", + "tags": [ + "Elastic", + "Windows", + "Elastic Endgame", + "Network", + "Continuous Monitoring", + "SecOps", + "Monitoring" + ], + "threat_filters": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "disabled": false, + "key": "event.module", + "negate": false, + "params": { + "query": "threatintel" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "event.module": "threatintel" + } + } + }, + { + "$state": { + "store": "appState" + }, + "meta": { + "disabled": false, + "key": "event.category", + "negate": false, + "params": { + "query": "threat" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "event.category": "threat" + } + } + }, + { + "$state": { + "store": "appState" + }, + "meta": { + "disabled": false, + "key": "event.kind", + "negate": false, + "params": { + "query": "enrichment" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "event.kind": "enrichment" + } + } + }, + { + "$state": { + "store": "appState" + }, + "meta": { + "disabled": false, + "key": "event.type", + "negate": false, + "params": { + "query": "indicator" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "event.type": "indicator" + } + } + } + ], + "threat_index": [ + "filebeat-*" + ], + "threat_indicator_path": "", + "threat_language": "kuery", + "threat_mapping": [ + { + "entries": [ + { + "field": "file.hash.md5", + "type": "mapping", + "value": "threatintel.indicator.file.hash.md5" + } + ] + }, + { + "entries": [ + { + "field": "file.hash.sha1", + "type": "mapping", + "value": "threatintel.indicator.file.hash.sha1" + } + ] + }, + { + "entries": [ + { + "field": "file.hash.sha256", + "type": "mapping", + "value": "threatintel.indicator.file.hash.sha256" + } + ] + }, + { + "entries": [ + { + "field": "file.pe.imphash", + "type": "mapping", + "value": "threatintel.indicator.file.pe.imphash" + } + ] + }, + { + "entries": [ + { + "field": "source.ip", + "type": "mapping", + "value": "threatintel.indicator.ip" + } + ] + }, + { + "entries": [ + { + "field": "destination.ip", + "type": "mapping", + "value": "threatintel.indicator.ip" + } + ] + }, + { + "entries": [ + { + "field": "url.full", + "type": "mapping", + "value": "threatintel.indicator.url.full" + } + ] + }, + { + "entries": [ + { + "field": "registry.path", + "type": "mapping", + "value": "threatintel.indicator.registry.path" + } + ] + } + ], + "threat_query": "event.module:threatintel and (threatintel.indicator.file.hash.*:* or threatintel.indicator.file.pe.imphash:* or threatintel.indicator.ip:* or threatintel.indicator.registry.path:* or threatintel.indicator.url.full:*)", + "timeline_id": "495ad7a7-316e-4544-8a0f-9c098daee76e", + "timeline_title": "Generic Threat Match Timeline", + "type": "threat_match", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index b0c8cd6c4dd69..38cae8d1cf50f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -33,7 +33,6 @@ export const updateRules = async ({ } const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); - const throttle = ruleUpdate.throttle ?? null; const enabled = ruleUpdate.enabled ?? true; const newInternalRule: InternalRuleUpdate = { name: ruleUpdate.name, @@ -73,7 +72,10 @@ export const updateRules = async ({ ...typeSpecificParams, }, schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions: throttle === 'rule' ? (ruleUpdate.actions ?? []).map(transformRuleToAlertAction) : [], + actions: + ruleUpdate.throttle === 'rule' + ? (ruleUpdate.actions ?? []).map(transformRuleToAlertAction) + : [], throttle: null, notifyWhen: null, }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts index 18fa84c9cf3ae..f457a1a11422c 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts @@ -40,6 +40,9 @@ export const buildFrameworkRequest = async ( export const escapeHatch = schema.object({}, { unknowns: 'allow' }); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + */ export const formatErrors = (errors: rt.Errors): string[] => { const err = errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts index f6d78f2f1259f..51892a1a05d55 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.test.ts @@ -126,7 +126,7 @@ describe('Index Fields', () => { }, { description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', + 'Type of the agent. The agent type always stays the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -252,7 +252,7 @@ describe('Index Fields', () => { { category: 'agent', description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', + 'Type of the agent. The agent type always stays the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -426,7 +426,7 @@ describe('Index Fields', () => { { category: 'agent', description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', + 'Type of the agent. The agent type always stays the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.test.ts index da19df32ac87a..5f732d2bacdac 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.test.ts @@ -8,329 +8,177 @@ import { eventHit } from '../../../../../../common/utils/mock_event_details'; import { EventHit } from '../../../../../../common/search_strategy'; import { TIMELINE_EVENTS_FIELDS } from './constants'; -import { buildObjectForFieldPath, formatTimelineData } from './helpers'; +import { buildFieldsRequest, buildObjectForFieldPath, formatTimelineData } from './helpers'; -describe('#formatTimelineData', () => { - it('happy path', async () => { - const res = await formatTimelineData( - [ - '@timestamp', - 'host.name', - 'destination.ip', - 'source.ip', - 'source.geo.location', - 'threat.indicator.matched.field', - ], - TIMELINE_EVENTS_FIELDS, - eventHit - ); - expect(res).toEqual({ - cursor: { - tiebreaker: 'beats-ci-immutable-ubuntu-1804-1605624279743236239', - value: '1605624488922', - }, - node: { - _id: 'tkCt1nUBaEgqnrVSZ8R_', - _index: 'auditbeat-7.8.0-2020.11.05-000003', - data: [ - { - field: '@timestamp', - value: ['2020-11-17T14:48:08.922Z'], - }, - { - field: 'host.name', - value: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'], - }, - { - field: 'threat.indicator.matched.field', - value: ['matched_field', 'other_matched_field', 'matched_field_2'], - }, - { - field: 'source.geo.location', - value: [`{"lon":118.7778,"lat":32.0617}`], - }, +describe('search strategy timeline helpers', () => { + describe('#formatTimelineData', () => { + it('happy path', async () => { + const res = await formatTimelineData( + [ + '@timestamp', + 'host.name', + 'destination.ip', + 'source.ip', + 'source.geo.location', + 'threat.indicator.matched.field', ], - ecs: { - '@timestamp': ['2020-11-17T14:48:08.922Z'], + TIMELINE_EVENTS_FIELDS, + eventHit + ); + expect(res).toEqual({ + cursor: { + tiebreaker: 'beats-ci-immutable-ubuntu-1804-1605624279743236239', + value: '1605624488922', + }, + node: { _id: 'tkCt1nUBaEgqnrVSZ8R_', _index: 'auditbeat-7.8.0-2020.11.05-000003', - agent: { - type: ['auditbeat'], - }, - event: { - action: ['process_started'], - category: ['process'], - dataset: ['process'], - kind: ['event'], - module: ['system'], - type: ['start'], - }, - host: { - id: ['e59991e835905c65ed3e455b33e13bd6'], - ip: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'], - name: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'], - os: { - family: ['debian'], + data: [ + { + field: '@timestamp', + value: ['2020-11-17T14:48:08.922Z'], }, - }, - message: ['Process go (PID: 4313) by user jenkins STARTED'], - process: { - args: ['go', 'vet', './...'], - entity_id: ['Z59cIkAAIw8ZoK0H'], - executable: [ - '/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go', - ], - hash: { - sha1: ['1eac22336a41e0660fb302add9d97daa2bcc7040'], + { + field: 'host.name', + value: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'], }, - name: ['go'], - pid: ['4313'], - ppid: ['3977'], - working_directory: [ - '/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat', - ], - }, - timestamp: '2020-11-17T14:48:08.922Z', - user: { - name: ['jenkins'], - }, - threat: { - indicator: [ - { - event: { - dataset: [], - reference: [], - }, - matched: { - atomic: ['matched_atomic'], - field: ['matched_field', 'other_matched_field'], - type: [], - }, - provider: ['yourself'], + { + field: 'threat.indicator.matched.field', + value: ['matched_field', 'other_matched_field', 'matched_field_2'], + }, + { + field: 'source.geo.location', + value: [`{"lon":118.7778,"lat":32.0617}`], + }, + ], + ecs: { + '@timestamp': ['2020-11-17T14:48:08.922Z'], + _id: 'tkCt1nUBaEgqnrVSZ8R_', + _index: 'auditbeat-7.8.0-2020.11.05-000003', + agent: { + type: ['auditbeat'], + }, + event: { + action: ['process_started'], + category: ['process'], + dataset: ['process'], + kind: ['event'], + module: ['system'], + type: ['start'], + }, + host: { + id: ['e59991e835905c65ed3e455b33e13bd6'], + ip: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'], + name: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'], + os: { + family: ['debian'], }, - { - event: { - dataset: [], - reference: [], + }, + message: ['Process go (PID: 4313) by user jenkins STARTED'], + process: { + args: ['go', 'vet', './...'], + entity_id: ['Z59cIkAAIw8ZoK0H'], + executable: [ + '/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go', + ], + hash: { + sha1: ['1eac22336a41e0660fb302add9d97daa2bcc7040'], + }, + name: ['go'], + pid: ['4313'], + ppid: ['3977'], + working_directory: [ + '/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat', + ], + }, + timestamp: '2020-11-17T14:48:08.922Z', + user: { + name: ['jenkins'], + }, + threat: { + indicator: [ + { + event: { + dataset: [], + reference: [], + }, + matched: { + atomic: ['matched_atomic'], + field: ['matched_field', 'other_matched_field'], + type: [], + }, + provider: ['yourself'], }, - matched: { - atomic: ['matched_atomic_2'], - field: ['matched_field_2'], - type: [], + { + event: { + dataset: [], + reference: [], + }, + matched: { + atomic: ['matched_atomic_2'], + field: ['matched_field_2'], + type: [], + }, + provider: ['other_you'], }, - provider: ['other_you'], - }, - ], + ], + }, }, }, - }, + }); }); - }); - it('rule signal results', async () => { - const response: EventHit = { - _index: '.siem-signals-patrykkopycinski-default-000007', - _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', - _score: 0, - _source: { - signal: { - threshold_result: { - count: 10000, - value: '2a990c11-f61b-4c8e-b210-da2574e9f9db', - }, - parent: { - depth: 0, - index: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - id: '0268af90-d8da-576a-9747-2a191519416a', - type: 'event', - }, - depth: 1, - _meta: { - version: 14, - }, - rule: { - note: null, - throttle: null, - references: [], - severity_mapping: [], - description: 'asdasd', - created_at: '2021-01-09T11:25:45.046Z', - language: 'kuery', - threshold: { - field: '', - value: 200, - }, - building_block_type: null, - output_index: '.siem-signals-patrykkopycinski-default', - type: 'threshold', - rule_name_override: null, - enabled: true, - exceptions_list: [], - updated_at: '2021-01-09T13:36:39.204Z', - timestamp_override: null, - from: 'now-360s', - id: '696c24e0-526d-11eb-836c-e1620268b945', - timeline_id: null, - max_signals: 100, - severity: 'low', - risk_score: 21, - risk_score_mapping: [], - author: [], - query: '_id :*', - index: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - filters: [ - { - $state: { - store: 'appState', - }, - meta: { - negate: false, - alias: null, - disabled: false, - type: 'exists', - value: 'exists', - key: '_index', - }, - exists: { - field: '_index', - }, - }, - { - $state: { - store: 'appState', - }, - meta: { - negate: false, - alias: 'id_exists', - disabled: false, - type: 'exists', - value: 'exists', - key: '_id', - }, - exists: { - field: '_id', - }, - }, - ], - created_by: 'patryk_test_user', - version: 1, - saved_id: null, - tags: [], - rule_id: '2a990c11-f61b-4c8e-b210-da2574e9f9db', - license: '', - immutable: false, - timeline_title: null, - meta: { - from: '1m', - kibana_siem_app_url: 'http://localhost:5601/app/security', + it('rule signal results', async () => { + const response: EventHit = { + _index: '.siem-signals-patrykkopycinski-default-000007', + _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', + _score: 0, + _source: { + signal: { + threshold_result: { + count: 10000, + value: '2a990c11-f61b-4c8e-b210-da2574e9f9db', }, - name: 'Threshold test', - updated_by: 'patryk_test_user', - interval: '5m', - false_positives: [], - to: 'now', - threat: [], - actions: [], - }, - original_time: '2021-01-09T13:39:32.595Z', - ancestors: [ - { + parent: { depth: 0, index: 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', id: '0268af90-d8da-576a-9747-2a191519416a', type: 'event', }, - ], - parents: [ - { - depth: 0, - index: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - id: '0268af90-d8da-576a-9747-2a191519416a', - type: 'event', + depth: 1, + _meta: { + version: 14, }, - ], - status: 'open', - }, - }, - fields: { - 'signal.rule.output_index': ['.siem-signals-patrykkopycinski-default'], - 'signal.rule.from': ['now-360s'], - 'signal.rule.language': ['kuery'], - '@timestamp': ['2021-01-09T13:41:40.517Z'], - 'signal.rule.query': ['_id :*'], - 'signal.rule.type': ['threshold'], - 'signal.rule.id': ['696c24e0-526d-11eb-836c-e1620268b945'], - 'signal.rule.risk_score': [21], - 'signal.status': ['open'], - 'event.kind': ['signal'], - 'signal.original_time': ['2021-01-09T13:39:32.595Z'], - 'signal.rule.severity': ['low'], - 'signal.rule.version': ['1'], - 'signal.rule.index': [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - 'signal.rule.name': ['Threshold test'], - 'signal.rule.to': ['now'], - }, - _type: '', - sort: ['1610199700517'], - aggregations: {}, - }; - - expect( - await formatTimelineData( - ['@timestamp', 'host.name', 'destination.ip', 'source.ip'], - TIMELINE_EVENTS_FIELDS, - response - ) - ).toEqual({ - cursor: { - tiebreaker: null, - value: '', - }, - node: { - _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', - _index: '.siem-signals-patrykkopycinski-default-000007', - data: [ - { - field: '@timestamp', - value: ['2021-01-09T13:41:40.517Z'], - }, - ], - ecs: { - '@timestamp': ['2021-01-09T13:41:40.517Z'], - timestamp: '2021-01-09T13:41:40.517Z', - _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', - _index: '.siem-signals-patrykkopycinski-default-000007', - event: { - kind: ['signal'], - }, - signal: { - original_time: ['2021-01-09T13:39:32.595Z'], - status: ['open'], - threshold_result: ['{"count":10000,"value":"2a990c11-f61b-4c8e-b210-da2574e9f9db"}'], rule: { - building_block_type: [], + note: null, + throttle: null, + references: [], + severity_mapping: [], + description: 'asdasd', + created_at: '2021-01-09T11:25:45.046Z', + language: 'kuery', + threshold: { + field: '', + value: 200, + }, + building_block_type: null, + output_index: '.siem-signals-patrykkopycinski-default', + type: 'threshold', + rule_name_override: null, + enabled: true, exceptions_list: [], - from: ['now-360s'], - id: ['696c24e0-526d-11eb-836c-e1620268b945'], + updated_at: '2021-01-09T13:36:39.204Z', + timestamp_override: null, + from: 'now-360s', + id: '696c24e0-526d-11eb-836c-e1620268b945', + timeline_id: null, + max_signals: 100, + severity: 'low', + risk_score: 21, + risk_score_mapping: [], + author: [], + query: '_id :*', index: [ 'apm-*-transaction*', 'auditbeat-*', @@ -340,27 +188,8 @@ describe('#formatTimelineData', () => { 'packetbeat-*', 'winlogbeat-*', ], - language: ['kuery'], - name: ['Threshold test'], - output_index: ['.siem-signals-patrykkopycinski-default'], - risk_score: ['21'], - query: ['_id :*'], - severity: ['low'], - to: ['now'], - type: ['threshold'], - version: ['1'], - timeline_id: [], - timeline_title: [], - saved_id: [], - note: [], - threshold: [ - JSON.stringify({ - field: '', - value: 200, - }), - ], filters: [ - JSON.stringify({ + { $state: { store: 'appState', }, @@ -375,8 +204,8 @@ describe('#formatTimelineData', () => { exists: { field: '_index', }, - }), - JSON.stringify({ + }, + { $state: { store: 'appState', }, @@ -391,16 +220,189 @@ describe('#formatTimelineData', () => { exists: { field: '_id', }, - }), + }, ], + created_by: 'patryk_test_user', + version: 1, + saved_id: null, + tags: [], + rule_id: '2a990c11-f61b-4c8e-b210-da2574e9f9db', + license: '', + immutable: false, + timeline_title: null, + meta: { + from: '1m', + kibana_siem_app_url: 'http://localhost:5601/app/security', + }, + name: 'Threshold test', + updated_by: 'patryk_test_user', + interval: '5m', + false_positives: [], + to: 'now', + threat: [], + actions: [], + }, + original_time: '2021-01-09T13:39:32.595Z', + ancestors: [ + { + depth: 0, + index: + 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', + id: '0268af90-d8da-576a-9747-2a191519416a', + type: 'event', + }, + ], + parents: [ + { + depth: 0, + index: + 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', + id: '0268af90-d8da-576a-9747-2a191519416a', + type: 'event', + }, + ], + status: 'open', + }, + }, + fields: { + 'signal.rule.output_index': ['.siem-signals-patrykkopycinski-default'], + 'signal.rule.from': ['now-360s'], + 'signal.rule.language': ['kuery'], + '@timestamp': ['2021-01-09T13:41:40.517Z'], + 'signal.rule.query': ['_id :*'], + 'signal.rule.type': ['threshold'], + 'signal.rule.id': ['696c24e0-526d-11eb-836c-e1620268b945'], + 'signal.rule.risk_score': [21], + 'signal.status': ['open'], + 'event.kind': ['signal'], + 'signal.original_time': ['2021-01-09T13:39:32.595Z'], + 'signal.rule.severity': ['low'], + 'signal.rule.version': ['1'], + 'signal.rule.index': [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + 'signal.rule.name': ['Threshold test'], + 'signal.rule.to': ['now'], + }, + _type: '', + sort: ['1610199700517'], + aggregations: {}, + }; + + expect( + await formatTimelineData( + ['@timestamp', 'host.name', 'destination.ip', 'source.ip'], + TIMELINE_EVENTS_FIELDS, + response + ) + ).toEqual({ + cursor: { + tiebreaker: null, + value: '', + }, + node: { + _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', + _index: '.siem-signals-patrykkopycinski-default-000007', + data: [ + { + field: '@timestamp', + value: ['2021-01-09T13:41:40.517Z'], + }, + ], + ecs: { + '@timestamp': ['2021-01-09T13:41:40.517Z'], + timestamp: '2021-01-09T13:41:40.517Z', + _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', + _index: '.siem-signals-patrykkopycinski-default-000007', + event: { + kind: ['signal'], + }, + signal: { + original_time: ['2021-01-09T13:39:32.595Z'], + status: ['open'], + threshold_result: ['{"count":10000,"value":"2a990c11-f61b-4c8e-b210-da2574e9f9db"}'], + rule: { + building_block_type: [], + exceptions_list: [], + from: ['now-360s'], + id: ['696c24e0-526d-11eb-836c-e1620268b945'], + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + language: ['kuery'], + name: ['Threshold test'], + output_index: ['.siem-signals-patrykkopycinski-default'], + risk_score: ['21'], + query: ['_id :*'], + severity: ['low'], + to: ['now'], + type: ['threshold'], + version: ['1'], + timeline_id: [], + timeline_title: [], + saved_id: [], + note: [], + threshold: [ + JSON.stringify({ + field: '', + value: 200, + }), + ], + filters: [ + JSON.stringify({ + $state: { + store: 'appState', + }, + meta: { + negate: false, + alias: null, + disabled: false, + type: 'exists', + value: 'exists', + key: '_index', + }, + exists: { + field: '_index', + }, + }), + JSON.stringify({ + $state: { + store: 'appState', + }, + meta: { + negate: false, + alias: 'id_exists', + disabled: false, + type: 'exists', + value: 'exists', + key: '_id', + }, + exists: { + field: '_id', + }, + }), + ], + }, }, }, }, - }, + }); }); }); - describe('buildObjectForFieldPath', () => { + describe('#buildObjectForFieldPath', () => { it('builds an object from a single non-nested field', () => { expect(buildObjectForFieldPath('@timestamp', eventHit)).toEqual({ '@timestamp': ['2020-11-17T14:48:08.922Z'], @@ -568,4 +570,946 @@ describe('#formatTimelineData', () => { }); }); }); + + describe('#buildFieldsRequest', () => { + it('happy path', async () => { + const res = await buildFieldsRequest([ + '@timestamp', + 'host.name', + 'destination.ip', + 'source.ip', + 'source.geo.location', + 'threat.indicator.matched.field', + ]); + expect(res).toEqual([ + { + field: '@timestamp', + include_unmapped: true, + }, + { + field: 'host.name', + include_unmapped: true, + }, + { + field: 'destination.ip', + include_unmapped: true, + }, + { + field: 'source.ip', + include_unmapped: true, + }, + { + field: 'source.geo.location', + include_unmapped: true, + }, + { + field: 'threat.indicator.matched.field', + include_unmapped: true, + }, + { + field: 'signal.status', + include_unmapped: true, + }, + { + field: 'signal.group.id', + include_unmapped: true, + }, + { + field: 'signal.original_time', + include_unmapped: true, + }, + { + field: 'signal.rule.filters', + include_unmapped: true, + }, + { + field: 'signal.rule.from', + include_unmapped: true, + }, + { + field: 'signal.rule.language', + include_unmapped: true, + }, + { + field: 'signal.rule.query', + include_unmapped: true, + }, + { + field: 'signal.rule.name', + include_unmapped: true, + }, + { + field: 'signal.rule.to', + include_unmapped: true, + }, + { + field: 'signal.rule.id', + include_unmapped: true, + }, + { + field: 'signal.rule.index', + include_unmapped: true, + }, + { + field: 'signal.rule.type', + include_unmapped: true, + }, + { + field: 'signal.original_event.kind', + include_unmapped: true, + }, + { + field: 'signal.original_event.module', + include_unmapped: true, + }, + { + field: 'signal.rule.version', + include_unmapped: true, + }, + { + field: 'signal.rule.severity', + include_unmapped: true, + }, + { + field: 'signal.rule.risk_score', + include_unmapped: true, + }, + { + field: 'signal.threshold_result', + include_unmapped: true, + }, + { + field: 'event.code', + include_unmapped: true, + }, + { + field: 'event.module', + include_unmapped: true, + }, + { + field: 'event.action', + include_unmapped: true, + }, + { + field: 'event.category', + include_unmapped: true, + }, + { + field: 'user.name', + include_unmapped: true, + }, + { + field: 'message', + include_unmapped: true, + }, + { + field: 'system.auth.ssh.signature', + include_unmapped: true, + }, + { + field: 'system.auth.ssh.method', + include_unmapped: true, + }, + { + field: 'system.audit.package.arch', + include_unmapped: true, + }, + { + field: 'system.audit.package.entity_id', + include_unmapped: true, + }, + { + field: 'system.audit.package.name', + include_unmapped: true, + }, + { + field: 'system.audit.package.size', + include_unmapped: true, + }, + { + field: 'system.audit.package.summary', + include_unmapped: true, + }, + { + field: 'system.audit.package.version', + include_unmapped: true, + }, + { + field: 'event.created', + include_unmapped: true, + }, + { + field: 'event.dataset', + include_unmapped: true, + }, + { + field: 'event.duration', + include_unmapped: true, + }, + { + field: 'event.end', + include_unmapped: true, + }, + { + field: 'event.hash', + include_unmapped: true, + }, + { + field: 'event.id', + include_unmapped: true, + }, + { + field: 'event.kind', + include_unmapped: true, + }, + { + field: 'event.original', + include_unmapped: true, + }, + { + field: 'event.outcome', + include_unmapped: true, + }, + { + field: 'event.risk_score', + include_unmapped: true, + }, + { + field: 'event.risk_score_norm', + include_unmapped: true, + }, + { + field: 'event.severity', + include_unmapped: true, + }, + { + field: 'event.start', + include_unmapped: true, + }, + { + field: 'event.timezone', + include_unmapped: true, + }, + { + field: 'event.type', + include_unmapped: true, + }, + { + field: 'agent.type', + include_unmapped: true, + }, + { + field: 'auditd.result', + include_unmapped: true, + }, + { + field: 'auditd.session', + include_unmapped: true, + }, + { + field: 'auditd.data.acct', + include_unmapped: true, + }, + { + field: 'auditd.data.terminal', + include_unmapped: true, + }, + { + field: 'auditd.data.op', + include_unmapped: true, + }, + { + field: 'auditd.summary.actor.primary', + include_unmapped: true, + }, + { + field: 'auditd.summary.actor.secondary', + include_unmapped: true, + }, + { + field: 'auditd.summary.object.primary', + include_unmapped: true, + }, + { + field: 'auditd.summary.object.secondary', + include_unmapped: true, + }, + { + field: 'auditd.summary.object.type', + include_unmapped: true, + }, + { + field: 'auditd.summary.how', + include_unmapped: true, + }, + { + field: 'auditd.summary.message_type', + include_unmapped: true, + }, + { + field: 'auditd.summary.sequence', + include_unmapped: true, + }, + { + field: 'file.Ext.original.path', + include_unmapped: true, + }, + { + field: 'file.name', + include_unmapped: true, + }, + { + field: 'file.target_path', + include_unmapped: true, + }, + { + field: 'file.extension', + include_unmapped: true, + }, + { + field: 'file.type', + include_unmapped: true, + }, + { + field: 'file.device', + include_unmapped: true, + }, + { + field: 'file.inode', + include_unmapped: true, + }, + { + field: 'file.uid', + include_unmapped: true, + }, + { + field: 'file.owner', + include_unmapped: true, + }, + { + field: 'file.gid', + include_unmapped: true, + }, + { + field: 'file.group', + include_unmapped: true, + }, + { + field: 'file.mode', + include_unmapped: true, + }, + { + field: 'file.size', + include_unmapped: true, + }, + { + field: 'file.mtime', + include_unmapped: true, + }, + { + field: 'file.ctime', + include_unmapped: true, + }, + { + field: 'file.path', + include_unmapped: true, + }, + { + field: 'file.Ext.code_signature', + include_unmapped: true, + }, + { + field: 'file.Ext.code_signature.subject_name', + include_unmapped: true, + }, + { + field: 'file.Ext.code_signature.trusted', + include_unmapped: true, + }, + { + field: 'file.hash.sha256', + include_unmapped: true, + }, + { + field: 'host.os.family', + include_unmapped: true, + }, + { + field: 'host.id', + include_unmapped: true, + }, + { + field: 'host.ip', + include_unmapped: true, + }, + { + field: 'registry.key', + include_unmapped: true, + }, + { + field: 'registry.path', + include_unmapped: true, + }, + { + field: 'rule.reference', + include_unmapped: true, + }, + { + field: 'source.bytes', + include_unmapped: true, + }, + { + field: 'source.packets', + include_unmapped: true, + }, + { + field: 'source.port', + include_unmapped: true, + }, + { + field: 'source.geo.continent_name', + include_unmapped: true, + }, + { + field: 'source.geo.country_name', + include_unmapped: true, + }, + { + field: 'source.geo.country_iso_code', + include_unmapped: true, + }, + { + field: 'source.geo.city_name', + include_unmapped: true, + }, + { + field: 'source.geo.region_iso_code', + include_unmapped: true, + }, + { + field: 'source.geo.region_name', + include_unmapped: true, + }, + { + field: 'destination.bytes', + include_unmapped: true, + }, + { + field: 'destination.packets', + include_unmapped: true, + }, + { + field: 'destination.port', + include_unmapped: true, + }, + { + field: 'destination.geo.continent_name', + include_unmapped: true, + }, + { + field: 'destination.geo.country_name', + include_unmapped: true, + }, + { + field: 'destination.geo.country_iso_code', + include_unmapped: true, + }, + { + field: 'destination.geo.city_name', + include_unmapped: true, + }, + { + field: 'destination.geo.region_iso_code', + include_unmapped: true, + }, + { + field: 'destination.geo.region_name', + include_unmapped: true, + }, + { + field: 'dns.question.name', + include_unmapped: true, + }, + { + field: 'dns.question.type', + include_unmapped: true, + }, + { + field: 'dns.resolved_ip', + include_unmapped: true, + }, + { + field: 'dns.response_code', + include_unmapped: true, + }, + { + field: 'endgame.exit_code', + include_unmapped: true, + }, + { + field: 'endgame.file_name', + include_unmapped: true, + }, + { + field: 'endgame.file_path', + include_unmapped: true, + }, + { + field: 'endgame.logon_type', + include_unmapped: true, + }, + { + field: 'endgame.parent_process_name', + include_unmapped: true, + }, + { + field: 'endgame.pid', + include_unmapped: true, + }, + { + field: 'endgame.process_name', + include_unmapped: true, + }, + { + field: 'endgame.subject_domain_name', + include_unmapped: true, + }, + { + field: 'endgame.subject_logon_id', + include_unmapped: true, + }, + { + field: 'endgame.subject_user_name', + include_unmapped: true, + }, + { + field: 'endgame.target_domain_name', + include_unmapped: true, + }, + { + field: 'endgame.target_logon_id', + include_unmapped: true, + }, + { + field: 'endgame.target_user_name', + include_unmapped: true, + }, + { + field: 'signal.rule.saved_id', + include_unmapped: true, + }, + { + field: 'signal.rule.timeline_id', + include_unmapped: true, + }, + { + field: 'signal.rule.timeline_title', + include_unmapped: true, + }, + { + field: 'signal.rule.output_index', + include_unmapped: true, + }, + { + field: 'signal.rule.note', + include_unmapped: true, + }, + { + field: 'signal.rule.threshold', + include_unmapped: true, + }, + { + field: 'signal.rule.exceptions_list', + include_unmapped: true, + }, + { + field: 'signal.rule.building_block_type', + include_unmapped: true, + }, + { + field: 'suricata.eve.proto', + include_unmapped: true, + }, + { + field: 'suricata.eve.flow_id', + include_unmapped: true, + }, + { + field: 'suricata.eve.alert.signature', + include_unmapped: true, + }, + { + field: 'suricata.eve.alert.signature_id', + include_unmapped: true, + }, + { + field: 'network.bytes', + include_unmapped: true, + }, + { + field: 'network.community_id', + include_unmapped: true, + }, + { + field: 'network.direction', + include_unmapped: true, + }, + { + field: 'network.packets', + include_unmapped: true, + }, + { + field: 'network.protocol', + include_unmapped: true, + }, + { + field: 'network.transport', + include_unmapped: true, + }, + { + field: 'http.version', + include_unmapped: true, + }, + { + field: 'http.request.method', + include_unmapped: true, + }, + { + field: 'http.request.body.bytes', + include_unmapped: true, + }, + { + field: 'http.request.body.content', + include_unmapped: true, + }, + { + field: 'http.request.referrer', + include_unmapped: true, + }, + { + field: 'http.response.status_code', + include_unmapped: true, + }, + { + field: 'http.response.body.bytes', + include_unmapped: true, + }, + { + field: 'http.response.body.content', + include_unmapped: true, + }, + { + field: 'tls.client_certificate.fingerprint.sha1', + include_unmapped: true, + }, + { + field: 'tls.fingerprints.ja3.hash', + include_unmapped: true, + }, + { + field: 'tls.server_certificate.fingerprint.sha1', + include_unmapped: true, + }, + { + field: 'user.domain', + include_unmapped: true, + }, + { + field: 'winlog.event_id', + include_unmapped: true, + }, + { + field: 'process.exit_code', + include_unmapped: true, + }, + { + field: 'process.hash.md5', + include_unmapped: true, + }, + { + field: 'process.hash.sha1', + include_unmapped: true, + }, + { + field: 'process.hash.sha256', + include_unmapped: true, + }, + { + field: 'process.parent.name', + include_unmapped: true, + }, + { + field: 'process.parent.pid', + include_unmapped: true, + }, + { + field: 'process.pid', + include_unmapped: true, + }, + { + field: 'process.name', + include_unmapped: true, + }, + { + field: 'process.ppid', + include_unmapped: true, + }, + { + field: 'process.args', + include_unmapped: true, + }, + { + field: 'process.entity_id', + include_unmapped: true, + }, + { + field: 'process.executable', + include_unmapped: true, + }, + { + field: 'process.title', + include_unmapped: true, + }, + { + field: 'process.working_directory', + include_unmapped: true, + }, + { + field: 'zeek.session_id', + include_unmapped: true, + }, + { + field: 'zeek.connection.local_resp', + include_unmapped: true, + }, + { + field: 'zeek.connection.local_orig', + include_unmapped: true, + }, + { + field: 'zeek.connection.missed_bytes', + include_unmapped: true, + }, + { + field: 'zeek.connection.state', + include_unmapped: true, + }, + { + field: 'zeek.connection.history', + include_unmapped: true, + }, + { + field: 'zeek.notice.suppress_for', + include_unmapped: true, + }, + { + field: 'zeek.notice.msg', + include_unmapped: true, + }, + { + field: 'zeek.notice.note', + include_unmapped: true, + }, + { + field: 'zeek.notice.sub', + include_unmapped: true, + }, + { + field: 'zeek.notice.dst', + include_unmapped: true, + }, + { + field: 'zeek.notice.dropped', + include_unmapped: true, + }, + { + field: 'zeek.notice.peer_descr', + include_unmapped: true, + }, + { + field: 'zeek.dns.AA', + include_unmapped: true, + }, + { + field: 'zeek.dns.qclass_name', + include_unmapped: true, + }, + { + field: 'zeek.dns.RD', + include_unmapped: true, + }, + { + field: 'zeek.dns.qtype_name', + include_unmapped: true, + }, + { + field: 'zeek.dns.qtype', + include_unmapped: true, + }, + { + field: 'zeek.dns.query', + include_unmapped: true, + }, + { + field: 'zeek.dns.trans_id', + include_unmapped: true, + }, + { + field: 'zeek.dns.qclass', + include_unmapped: true, + }, + { + field: 'zeek.dns.RA', + include_unmapped: true, + }, + { + field: 'zeek.dns.TC', + include_unmapped: true, + }, + { + field: 'zeek.http.resp_mime_types', + include_unmapped: true, + }, + { + field: 'zeek.http.trans_depth', + include_unmapped: true, + }, + { + field: 'zeek.http.status_msg', + include_unmapped: true, + }, + { + field: 'zeek.http.resp_fuids', + include_unmapped: true, + }, + { + field: 'zeek.http.tags', + include_unmapped: true, + }, + { + field: 'zeek.files.session_ids', + include_unmapped: true, + }, + { + field: 'zeek.files.timedout', + include_unmapped: true, + }, + { + field: 'zeek.files.local_orig', + include_unmapped: true, + }, + { + field: 'zeek.files.tx_host', + include_unmapped: true, + }, + { + field: 'zeek.files.source', + include_unmapped: true, + }, + { + field: 'zeek.files.is_orig', + include_unmapped: true, + }, + { + field: 'zeek.files.overflow_bytes', + include_unmapped: true, + }, + { + field: 'zeek.files.sha1', + include_unmapped: true, + }, + { + field: 'zeek.files.duration', + include_unmapped: true, + }, + { + field: 'zeek.files.depth', + include_unmapped: true, + }, + { + field: 'zeek.files.analyzers', + include_unmapped: true, + }, + { + field: 'zeek.files.mime_type', + include_unmapped: true, + }, + { + field: 'zeek.files.rx_host', + include_unmapped: true, + }, + { + field: 'zeek.files.total_bytes', + include_unmapped: true, + }, + { + field: 'zeek.files.fuid', + include_unmapped: true, + }, + { + field: 'zeek.files.seen_bytes', + include_unmapped: true, + }, + { + field: 'zeek.files.missing_bytes', + include_unmapped: true, + }, + { + field: 'zeek.files.md5', + include_unmapped: true, + }, + { + field: 'zeek.ssl.cipher', + include_unmapped: true, + }, + { + field: 'zeek.ssl.established', + include_unmapped: true, + }, + { + field: 'zeek.ssl.resumed', + include_unmapped: true, + }, + { + field: 'zeek.ssl.version', + include_unmapped: true, + }, + { + field: 'threat.indicator.matched.atomic', + include_unmapped: true, + }, + { + field: 'threat.indicator.matched.type', + include_unmapped: true, + }, + { + field: 'threat.indicator.event.dataset', + include_unmapped: true, + }, + { + field: 'threat.indicator.event.reference', + include_unmapped: true, + }, + { + field: 'threat.indicator.provider', + include_unmapped: true, + }, + ]); + }); + + it('remove internal attributes starting with _', async () => { + const res = await buildFieldsRequest([ + '@timestamp', + '_id', + 'host.name', + 'destination.ip', + 'source.ip', + 'source.geo.location', + '_type', + 'threat.indicator.matched.field', + ]); + expect(res.some((f) => f.field === '_id')).toEqual(false); + expect(res.some((f) => f.field === '_type')).toEqual(false); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts index 6c20843058ff1..8e0e5e9655193 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts @@ -19,6 +19,7 @@ import { getDataFromFieldsHits, getDataSafety, } from '../../../../../../common/utils/field_formatters'; +import { TIMELINE_EVENTS_FIELDS } from './constants'; const getTimestamp = (hit: EventHit): string => { if (hit.fields && hit.fields['@timestamp']) { @@ -29,6 +30,12 @@ const getTimestamp = (hit: EventHit): string => { return ''; }; +export const buildFieldsRequest = (fields: string[]) => + uniq([...fields.filter((f) => !f.startsWith('_')), ...TIMELINE_EVENTS_FIELDS]).map((field) => ({ + field, + include_unmapped: true, + })); + export const formatTimelineData = async ( dataFields: readonly string[], ecsFields: readonly string[], diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts index 172c864f7ee4f..a26fbe05f7051 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { cloneDeep, uniq } from 'lodash/fp'; +import { cloneDeep } from 'lodash/fp'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; @@ -20,7 +20,7 @@ import { inspectStringifyObject } from '../../../../../utils/build_query'; import { SecuritySolutionTimelineFactory } from '../../types'; import { buildTimelineEventsAllQuery } from './query.events_all.dsl'; import { TIMELINE_EVENTS_FIELDS } from './constants'; -import { formatTimelineData } from './helpers'; +import { buildFieldsRequest, formatTimelineData } from './helpers'; export const timelineEventsAll: SecuritySolutionTimelineFactory = { buildDsl: (options: TimelineEventsAllRequestOptions) => { @@ -28,7 +28,7 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory ): Promise => { const { fieldRequested, ...queryOptions } = cloneDeep(options); - queryOptions.fields = uniq([...fieldRequested, ...TIMELINE_EVENTS_FIELDS]); + queryOptions.fields = buildFieldsRequest(fieldRequested); const { activePage, querySize } = options.pagination; const totalCount = response.rawResponse.hits.total || 0; const hits = response.rawResponse.hits.hits; diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts index 4545a3a3e136b..3572a2b9c497e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts @@ -40,7 +40,10 @@ describe('buildTimelineDetailsQuery', () => { }, ], "fields": Array [ - "*", + Object { + "field": "*", + "include_unmapped": true, + }, ], "query": Object { "terms": Object { diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts index c624eb14ae969..757b004d23f42 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts @@ -22,7 +22,7 @@ export const buildTimelineDetailsQuery = ( _id: [id], }, }, - fields: ['*'], + fields: [{ field: '*', include_unmapped: true }], _source: true, }, size: 1, diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts index caa4549fbf31d..e0cc62e294406 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts @@ -6,10 +6,12 @@ */ import { SavedObjectsFindResponse } from 'src/core/server'; -import { AgentEventSOAttributes } from './../../../../fleet/common/types/models/agent'; -import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../../../fleet/common/constants/agent'; + import { Agent } from '../../../../fleet/common'; -import { FLEET_ENDPOINT_PACKAGE_CONSTANT } from './fleet_saved_objects'; +import { + FLEET_ENDPOINT_PACKAGE_CONSTANT, + AGENT_EVENT_SAVED_OBJECT_TYPE, +} from './fleet_saved_objects'; const testAgentId = 'testAgentId'; const testAgentPolicyId = 'testAgentPolicyId'; @@ -45,7 +47,6 @@ export const mockFleetObjectsResponse = ( type: 'PERMANENT', user_provided_metadata: {}, enrolled_at: lastCheckIn, - current_error_events: [], local_metadata: { elastic: { agent: { @@ -74,7 +75,6 @@ export const mockFleetObjectsResponse = ( type: 'PERMANENT', user_provided_metadata: {}, enrolled_at: lastCheckIn, - current_error_events: [], local_metadata: { elastic: { agent: { @@ -215,7 +215,7 @@ export const mockFleetEventsObjectsResponse = ( updatedDate = new Date().toISOString(), policyStatus: 'success' | 'failure' = running ? 'success' : 'failure', policyMode: 'prevent' | 'detect' | 'off' = 'prevent' -): SavedObjectsFindResponse => { +): SavedObjectsFindResponse => { return { page: 1, per_page: 20, diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts index 1541cb128f60c..11e16c2f9e781 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts @@ -14,7 +14,6 @@ import { MockOSVersion, } from './endpoint.mocks'; import { SavedObjectsClientContract, SavedObjectsFindResponse } from 'src/core/server'; -import { AgentEventSOAttributes } from '../../../../fleet/common/types/models/agent'; import { Agent } from '../../../../fleet/common'; import * as endpointTelemetry from './index'; import * as fleetSavedObjects from './fleet_saved_objects'; @@ -28,9 +27,7 @@ describe('test security solution endpoint telemetry', () => { let getEndpointIntegratedFleetMetadataSpy: jest.SpyInstance< Promise<{ agents: Agent[]; total: number; page: number; perPage: number } | undefined> >; - let getLatestFleetEndpointEventSpy: jest.SpyInstance< - Promise> - >; + let getLatestFleetEndpointEventSpy: jest.SpyInstance>; beforeAll(() => { getLatestFleetEndpointEventSpy = jest.spyOn(fleetSavedObjects, 'getLatestFleetEndpointEvent'); diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts index 7e3620ec0ae04..f5b4c6c0fdba9 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts @@ -5,14 +5,18 @@ * 2.0. */ -import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; +import { + ElasticsearchClient, + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from 'src/core/server'; import { AgentService } from '../../../../fleet/server'; -import { AgentEventSOAttributes } from './../../../../fleet/common/types/models/agent'; -import { AGENT_EVENT_SAVED_OBJECT_TYPE } from './../../../../fleet/common/constants/agent'; import { defaultPackages as FleetDefaultPackages } from '../../../../fleet/common'; export const FLEET_ENDPOINT_PACKAGE_CONSTANT = FleetDefaultPackages.Endpoint; +export const AGENT_EVENT_SAVED_OBJECT_TYPE = 'donotexistsanymore-since-7.13'; + export const getEndpointIntegratedFleetMetadata = async ( agentService: AgentService | undefined, esClient: ElasticsearchClient @@ -36,15 +40,6 @@ export const getEndpointIntegratedFleetMetadata = async ( export const getLatestFleetEndpointEvent = async ( savedObjectsClient: SavedObjectsClientContract, agentId: string -) => - savedObjectsClient.find({ - // Get the most recent endpoint event. - type: AGENT_EVENT_SAVED_OBJECT_TYPE, - fields: ['agent_id', 'subtype', 'payload'], - filter: `${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.message: "${FLEET_ENDPOINT_PACKAGE_CONSTANT}"`, - perPage: 1, - sortField: 'timestamp', - sortOrder: 'desc', - search: agentId, - searchFields: ['agent_id'], - }); +): Promise => + // Agent events saved object do not exists in Fleet anymore + ({ total: 0, saved_objects: [], page: 0, per_page: 0 }); diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts index 94ff168ffffc8..ed8db14e46605 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts @@ -8,7 +8,7 @@ import { cloneDeep } from 'lodash'; import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { SavedObject } from './../../../../../../src/core/types/saved_objects'; -import { Agent, NewAgentEvent } from './../../../../fleet/common/types/models/agent'; +import { Agent } from './../../../../fleet/common/types/models/agent'; import { AgentMetadata } from '../../../../fleet/common/types/models/agent'; import { getEndpointIntegratedFleetMetadata, @@ -112,7 +112,8 @@ export const updateEndpointOSTelemetry = ( * the same time span. */ export const updateEndpointDailyActiveCount = ( - latestEndpointEvent: SavedObject, // TODO: This information will be lost in 7.13, need to find an alternative route. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + latestEndpointEvent: SavedObject, // TODO: This information will be lost in 7.13, need to find an alternative route. lastAgentCheckin: Agent['last_checkin'], currentCount: number ) => { @@ -130,7 +131,8 @@ export const updateEndpointDailyActiveCount = ( * to populate the success of it's application. The policy is provided in the agent health checks. */ export const updateEndpointPolicyTelemetry = ( - latestEndpointEvent: SavedObject, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + latestEndpointEvent: SavedObject, policiesTracker: PoliciesTelemetry ): PoliciesTelemetry => { const policyHostTypeToPolicyType = { diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts index 4f1dc0079b236..e308c8866c9d3 100644 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts +++ b/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts @@ -55,6 +55,15 @@ export const fieldsBeat: BeatFields = { name: 'tags', type: 'keyword', }, + 'agent.build.original': { + category: 'agent', + description: + 'Extended build information for the agent. This field is intended to contain any build information that a data source may provide, no specific formatting is required.', + example: + 'metricbeat version 7.6.0 (amd64), libbeat 7.6.0 [6a23e8f8f30f5001ba344e4e54d8d9cb82cb107c built 2020-02-05 23:10:10 +0000 UTC]', + name: 'agent.build.original', + type: 'keyword', + }, 'agent.ephemeral_id': { category: 'agent', description: @@ -82,7 +91,7 @@ export const fieldsBeat: BeatFields = { 'agent.type': { category: 'agent', description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', + 'Type of the agent. The agent type always stays the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'keyword', @@ -204,7 +213,7 @@ export const fieldsBeat: BeatFields = { }, 'client.ip': { category: 'client', - description: 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', + description: 'IP address of the client (IPv4 or IPv6).', name: 'client.ip', type: 'ip', }, @@ -246,15 +255,23 @@ export const fieldsBeat: BeatFields = { 'client.registered_domain': { category: 'client', description: - 'The highest registered client domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered client domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'client.registered_domain', type: 'keyword', }, + 'client.subdomain': { + category: 'client', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'east', + name: 'client.subdomain', + type: 'keyword', + }, 'client.top_level_domain': { category: 'client', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'client.top_level_domain', type: 'keyword', @@ -307,7 +324,7 @@ export const fieldsBeat: BeatFields = { }, 'client.user.id': { category: 'client', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'client.user.id', type: 'keyword', }, @@ -318,6 +335,13 @@ export const fieldsBeat: BeatFields = { name: 'client.user.name', type: 'keyword', }, + 'client.user.roles': { + category: 'client', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'client.user.roles', + type: 'keyword', + }, 'cloud.account.id': { category: 'cloud', description: @@ -326,6 +350,14 @@ export const fieldsBeat: BeatFields = { name: 'cloud.account.id', type: 'keyword', }, + 'cloud.account.name': { + category: 'cloud', + description: + 'The cloud account name or alias used to identify different entities in a multi-tenant environment. Examples: AWS account name, Google Cloud ORG display name.', + example: 'elastic-dev', + name: 'cloud.account.name', + type: 'keyword', + }, 'cloud.availability_zone': { category: 'cloud', description: 'Availability zone in which this host is running.', @@ -353,6 +385,21 @@ export const fieldsBeat: BeatFields = { name: 'cloud.machine.type', type: 'keyword', }, + 'cloud.project.id': { + category: 'cloud', + description: + 'The cloud project identifier. Examples: Google Cloud Project id, Azure Project id.', + example: 'my-project', + name: 'cloud.project.id', + type: 'keyword', + }, + 'cloud.project.name': { + category: 'cloud', + description: 'The cloud project name. Examples: Google Cloud Project name, Azure Project name.', + example: 'my project', + name: 'cloud.project.name', + type: 'keyword', + }, 'cloud.provider': { category: 'cloud', description: 'Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean.', @@ -537,7 +584,7 @@ export const fieldsBeat: BeatFields = { }, 'destination.ip': { category: 'destination', - description: 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + description: 'IP address of the destination (IPv4 or IPv6).', name: 'destination.ip', type: 'ip', }, @@ -579,15 +626,23 @@ export const fieldsBeat: BeatFields = { 'destination.registered_domain': { category: 'destination', description: - 'The highest registered destination domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered destination domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'destination.registered_domain', type: 'keyword', }, + 'destination.subdomain': { + category: 'destination', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'east', + name: 'destination.subdomain', + type: 'keyword', + }, 'destination.top_level_domain': { category: 'destination', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'destination.top_level_domain', type: 'keyword', @@ -640,7 +695,7 @@ export const fieldsBeat: BeatFields = { }, 'destination.user.id': { category: 'destination', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'destination.user.id', type: 'keyword', }, @@ -651,6 +706,13 @@ export const fieldsBeat: BeatFields = { name: 'destination.user.name', type: 'keyword', }, + 'destination.user.roles': { + category: 'destination', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'destination.user.roles', + type: 'keyword', + }, 'dll.code_signature.exists': { category: 'dll', description: 'Boolean to capture if a signature is present.', @@ -727,6 +789,13 @@ export const fieldsBeat: BeatFields = { name: 'dll.path', type: 'keyword', }, + 'dll.pe.architecture': { + category: 'dll', + description: 'CPU architecture target for the file.', + example: 'x64', + name: 'dll.pe.architecture', + type: 'keyword', + }, 'dll.pe.company': { category: 'dll', description: 'Internal company name of the file, provided at compile-time.', @@ -748,6 +817,14 @@ export const fieldsBeat: BeatFields = { name: 'dll.pe.file_version', type: 'keyword', }, + 'dll.pe.imphash': { + category: 'dll', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'dll.pe.imphash', + type: 'keyword', + }, 'dll.pe.original_file_name': { category: 'dll', description: 'Internal name of the file, provided at compile-time.', @@ -788,7 +865,7 @@ export const fieldsBeat: BeatFields = { category: 'dns', description: "The domain name to which this resource record pertains. If a chain of CNAME is being resolved, each answer's `name` should be the one that corresponds with the answer's `data`. It should not simply be the original `question.name` repeated.", - example: 'www.google.com', + example: 'www.example.com', name: 'dns.answers.name', type: 'keyword', }, @@ -811,7 +888,7 @@ export const fieldsBeat: BeatFields = { category: 'dns', description: 'Array of 2 letter DNS header flags. Expected values are: AA, TC, RD, RA, AD, CD, DO.', - example: '["RD","RA"]', + example: '["RD", "RA"]', name: 'dns.header_flags', type: 'keyword', }, @@ -842,15 +919,15 @@ export const fieldsBeat: BeatFields = { category: 'dns', description: 'The name being queried. If the name field contains non-printable characters (below 32 or above 126), those characters should be represented as escaped base 10 integers (\\DDD). Back slashes and quotes should be escaped. Tabs, carriage returns, and line feeds should be converted to \\t, \\r, and \\n respectively.', - example: 'www.google.com', + example: 'www.example.com', name: 'dns.question.name', type: 'keyword', }, 'dns.question.registered_domain': { category: 'dns', description: - 'The highest registered domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'dns.question.registered_domain', type: 'keyword', }, @@ -865,7 +942,7 @@ export const fieldsBeat: BeatFields = { 'dns.question.top_level_domain': { category: 'dns', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'dns.question.top_level_domain', type: 'keyword', @@ -881,7 +958,7 @@ export const fieldsBeat: BeatFields = { category: 'dns', description: 'Array containing all IPs seen in `answers.data`. The `answers` array can be difficult to use, because of the variety of data formats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip` makes it possible to index them as IP addresses, and makes them easier to visualize and query for.', - example: '["10.10.10.10","10.10.10.11"]', + example: '["10.10.10.10", "10.10.10.11"]', name: 'dns.resolved_ip', type: 'ip', }, @@ -1036,7 +1113,7 @@ export const fieldsBeat: BeatFields = { 'event.original': { category: 'event', description: - 'Raw text message of entire event. Used to demonstrate log integrity. This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`.', + 'Raw text message of entire event. Used to demonstrate log integrity. This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`. If users wish to override this and index this field, consider using the wildcard data type.', example: 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100| worm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', name: 'event.original', @@ -1058,11 +1135,19 @@ export const fieldsBeat: BeatFields = { name: 'event.provider', type: 'keyword', }, + 'event.reason': { + category: 'event', + description: + 'Reason why this event happened, according to the source. This describes the why of a particular action or outcome captured in the event. Where `event.action` captures the action from the event, `event.reason` describes why that action was taken. For example, a web proxy with an `event.action` which denied the request may also populate `event.reason` with the reason why (e.g. `blocked site`).', + example: 'Terminated an unexpected process', + name: 'event.reason', + type: 'keyword', + }, 'event.reference': { category: 'event', description: - 'Reference URL linking to additional information about this event. This URL links to a static definition of the this event. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', - example: 'https://system.vendor.com/event/#0001234', + 'Reference URL linking to additional information about this event. This URL links to a static definition of this event. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', + example: 'https://system.example.com/event/#0001234', name: 'event.reference', type: 'keyword', }, @@ -1121,8 +1206,8 @@ export const fieldsBeat: BeatFields = { 'event.url': { category: 'event', description: - 'URL linking to an external system to continue investigation of this event. This URL links to another system where in-depth investigation of the specific occurence of this event can take place. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', - example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', + 'URL linking to an external system to continue investigation of this event. This URL links to another system where in-depth investigation of the specific occurrence of this event can take place. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', + example: 'https://mysystem.example.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', name: 'event.url', type: 'keyword', }, @@ -1217,7 +1302,8 @@ export const fieldsBeat: BeatFields = { }, 'file.extension': { category: 'file', - description: 'File extension.', + description: + 'File extension, excluding the leading dot. Note that when the file name has multiple extensions (example.tar.gz), only the last one should be captured ("gz", not "tar.gz").', example: 'png', name: 'file.extension', type: 'keyword', @@ -1309,6 +1395,13 @@ export const fieldsBeat: BeatFields = { name: 'file.path', type: 'keyword', }, + 'file.pe.architecture': { + category: 'file', + description: 'CPU architecture target for the file.', + example: 'x64', + name: 'file.pe.architecture', + type: 'keyword', + }, 'file.pe.company': { category: 'file', description: 'Internal company name of the file, provided at compile-time.', @@ -1330,6 +1423,14 @@ export const fieldsBeat: BeatFields = { name: 'file.pe.file_version', type: 'keyword', }, + 'file.pe.imphash': { + category: 'file', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'file.pe.imphash', + type: 'keyword', + }, 'file.pe.original_file_name': { category: 'file', description: 'Internal name of the file, provided at compile-time.', @@ -1371,6 +1472,177 @@ export const fieldsBeat: BeatFields = { name: 'file.uid', type: 'keyword', }, + 'file.x509.alternative_names': { + category: 'file', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'file.x509.alternative_names', + type: 'keyword', + }, + 'file.x509.issuer.common_name': { + category: 'file', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'Example SHA2 High Assurance Server CA', + name: 'file.x509.issuer.common_name', + type: 'keyword', + }, + 'file.x509.issuer.country': { + category: 'file', + description: 'List of country (C) codes', + example: 'US', + name: 'file.x509.issuer.country', + type: 'keyword', + }, + 'file.x509.issuer.distinguished_name': { + category: 'file', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=Example Inc, OU=www.example.com, CN=Example SHA2 High Assurance Server CA', + name: 'file.x509.issuer.distinguished_name', + type: 'keyword', + }, + 'file.x509.issuer.locality': { + category: 'file', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'file.x509.issuer.locality', + type: 'keyword', + }, + 'file.x509.issuer.organization': { + category: 'file', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'Example Inc', + name: 'file.x509.issuer.organization', + type: 'keyword', + }, + 'file.x509.issuer.organizational_unit': { + category: 'file', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.example.com', + name: 'file.x509.issuer.organizational_unit', + type: 'keyword', + }, + 'file.x509.issuer.state_or_province': { + category: 'file', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'file.x509.issuer.state_or_province', + type: 'keyword', + }, + 'file.x509.not_after': { + category: 'file', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'file.x509.not_after', + type: 'date', + }, + 'file.x509.not_before': { + category: 'file', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'file.x509.not_before', + type: 'date', + }, + 'file.x509.public_key_algorithm': { + category: 'file', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'file.x509.public_key_algorithm', + type: 'keyword', + }, + 'file.x509.public_key_curve': { + category: 'file', + description: + 'The curve used by the elliptic curve public key algorithm. This is algorithm specific.', + example: 'nistp521', + name: 'file.x509.public_key_curve', + type: 'keyword', + }, + 'file.x509.public_key_exponent': { + category: 'file', + description: 'Exponent used to derive the public key. This is algorithm specific.', + example: 65537, + name: 'file.x509.public_key_exponent', + type: 'long', + }, + 'file.x509.public_key_size': { + category: 'file', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'file.x509.public_key_size', + type: 'long', + }, + 'file.x509.serial_number': { + category: 'file', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters.', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'file.x509.serial_number', + type: 'keyword', + }, + 'file.x509.signature_algorithm': { + category: 'file', + description: + 'Identifier for certificate signature algorithm. We recommend using names found in Go Lang Crypto library. See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353.', + example: 'SHA256-RSA', + name: 'file.x509.signature_algorithm', + type: 'keyword', + }, + 'file.x509.subject.common_name': { + category: 'file', + description: 'List of common names (CN) of subject.', + example: 'shared.global.example.net', + name: 'file.x509.subject.common_name', + type: 'keyword', + }, + 'file.x509.subject.country': { + category: 'file', + description: 'List of country (C) code', + example: 'US', + name: 'file.x509.subject.country', + type: 'keyword', + }, + 'file.x509.subject.distinguished_name': { + category: 'file', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Example, Inc., CN=shared.global.example.net', + name: 'file.x509.subject.distinguished_name', + type: 'keyword', + }, + 'file.x509.subject.locality': { + category: 'file', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'file.x509.subject.locality', + type: 'keyword', + }, + 'file.x509.subject.organization': { + category: 'file', + description: 'List of organizations (O) of subject.', + example: 'Example, Inc.', + name: 'file.x509.subject.organization', + type: 'keyword', + }, + 'file.x509.subject.organizational_unit': { + category: 'file', + description: 'List of organizational units (OU) of subject.', + name: 'file.x509.subject.organizational_unit', + type: 'keyword', + }, + 'file.x509.subject.state_or_province': { + category: 'file', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'file.x509.subject.state_or_province', + type: 'keyword', + }, + 'file.x509.version_number': { + category: 'file', + description: 'Version of x509 format.', + example: 3, + name: 'file.x509.version_number', + type: 'keyword', + }, 'geo.city_name': { category: 'geo', description: 'City name.', @@ -1611,6 +1883,14 @@ export const fieldsBeat: BeatFields = { name: 'host.os.platform', type: 'keyword', }, + 'host.os.type': { + category: 'host', + description: + "Use the `os.type` field to categorize the operating system into one of the broad commercial families. One of these following values should be used (lowercase): linux, macos, unix, windows. If the OS you're dealing with is not in the list, the field should not be populated. Please let us know by opening an issue with ECS, to propose its addition.", + example: 'macos', + name: 'host.os.type', + type: 'keyword', + }, 'host.os.version': { category: 'host', description: 'Operating system version as a raw string.', @@ -1680,7 +1960,7 @@ export const fieldsBeat: BeatFields = { }, 'host.user.id': { category: 'host', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'host.user.id', type: 'keyword', }, @@ -1691,6 +1971,13 @@ export const fieldsBeat: BeatFields = { name: 'host.user.name', type: 'keyword', }, + 'host.user.roles': { + category: 'host', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'host.user.roles', + type: 'keyword', + }, 'http.request.body.bytes': { category: 'http', description: 'Size in bytes of the request body.', @@ -1717,11 +2004,19 @@ export const fieldsBeat: BeatFields = { 'http.request.method': { category: 'http', description: - 'HTTP request method. The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', - example: 'get, post, put', + 'HTTP request method. Prior to ECS 1.6.0 the following guidance was provided: "The field value must be normalized to lowercase for querying." As of ECS 1.6.0, the guidance is deprecated because the original case of the method may be useful in anomaly detection. Original case will be mandated in ECS 2.0.0', + example: 'GET, POST, PUT, PoST', name: 'http.request.method', type: 'keyword', }, + 'http.request.mime_type': { + category: 'http', + description: + "Mime type of the body of the request. This value must only be populated based on the content of the request body, not on the `Content-Type` header. Comparing the mime type of a request with the request's Content-Type header can be helpful in detecting threats or misconfigured clients.", + example: 'image/gif', + name: 'http.request.mime_type', + type: 'keyword', + }, 'http.request.referrer': { category: 'http', description: 'Referrer for this HTTP request.', @@ -1752,6 +2047,14 @@ export const fieldsBeat: BeatFields = { type: 'long', format: 'bytes', }, + 'http.response.mime_type': { + category: 'http', + description: + "Mime type of the body of the response. This value must only be populated based on the content of the response body, not on the `Content-Type` header. Comparing the mime type of a response with the response's Content-Type header can be helpful in detecting misconfigured servers.", + example: 'image/gif', + name: 'http.response.mime_type', + type: 'keyword', + }, 'http.response.status_code': { category: 'http', description: 'HTTP response status code.', @@ -1789,6 +2092,14 @@ export const fieldsBeat: BeatFields = { name: 'interface.name', type: 'keyword', }, + 'log.file.path': { + category: 'log', + description: + "Full path to the log file this event came from, including the file name. It should include the drive letter, when appropriate. If the event wasn't read from a log file, do not populate this field.", + example: '/var/log/fun-times.log', + name: 'log.file.path', + type: 'keyword', + }, 'log.level': { category: 'log', description: @@ -1816,7 +2127,7 @@ export const fieldsBeat: BeatFields = { 'log.origin.file.name': { category: 'log', description: - 'The name of the file containing the source code which originated the log event. Note that this is not the name of the log file.', + 'The name of the file containing the source code which originated the log event. Note that this field is not meant to capture the log file. The correct field to capture the log file is `log.file.path`.', example: 'Bootstrap.java', name: 'log.origin.file.name', type: 'keyword', @@ -1912,7 +2223,7 @@ export const fieldsBeat: BeatFields = { 'network.direction': { category: 'network', description: - "Direction of the network traffic. Recommended values are: * inbound * outbound * internal * external * unknown When mapping events from a host-based monitoring context, populate this field from the host's point of view. When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", + 'Direction of the network traffic. Recommended values are: * ingress * egress * inbound * outbound * internal * external * unknown When mapping events from a host-based monitoring context, populate this field from the host\'s point of view, using the values "ingress" or "egress". When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of the network perimeter, using the values "inbound", "outbound", "internal" or "external". Note that "internal" is not crossing perimeter boundaries, and is meant to describe communication between two hosts within the perimeter. Note also that "external" is meant to describe traffic between two hosts that are external to the perimeter. This could for example be useful for ISPs or VPN service providers.', example: 'inbound', name: 'network.direction', type: 'keyword', @@ -1935,7 +2246,7 @@ export const fieldsBeat: BeatFields = { 'network.inner': { category: 'network', description: - 'Network.inner fields are added in addition to network.vlan fields to describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed fields include vlan.id and vlan.name. Inner vlan fields are typically used when sending traffic with multiple 802.1q encapsulations to a network sensor (e.g. Zeek, Wireshark.)', + 'Network.inner fields are added in addition to network.vlan fields to describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed fields include vlan.id and vlan.name. Inner vlan fields are typically used when sending traffic with multiple 802.1q encapsulations to a network sensor (e.g. Zeek, Wireshark.)', name: 'network.inner', type: 'object', }, @@ -2226,6 +2537,14 @@ export const fieldsBeat: BeatFields = { name: 'observer.os.platform', type: 'keyword', }, + 'observer.os.type': { + category: 'observer', + description: + "Use the `os.type` field to categorize the operating system into one of the broad commercial families. One of these following values should be used (lowercase): linux, macos, unix, windows. If the OS you're dealing with is not in the list, the field should not be populated. Please let us know by opening an issue with ECS, to propose its addition.", + example: 'macos', + name: 'observer.os.type', + type: 'keyword', + }, 'observer.os.version': { category: 'observer', description: 'Operating system version as a raw string.', @@ -2314,6 +2633,14 @@ export const fieldsBeat: BeatFields = { name: 'os.platform', type: 'keyword', }, + 'os.type': { + category: 'os', + description: + "Use the `os.type` field to categorize the operating system into one of the broad commercial families. One of these following values should be used (lowercase): linux, macos, unix, windows. If the OS you're dealing with is not in the list, the field should not be populated. Please let us know by opening an issue with ECS, to propose its addition.", + example: 'macos', + name: 'os.type', + type: 'keyword', + }, 'os.version': { category: 'os', description: 'Operating system version as a raw string.', @@ -2415,6 +2742,13 @@ export const fieldsBeat: BeatFields = { name: 'package.version', type: 'keyword', }, + 'pe.architecture': { + category: 'pe', + description: 'CPU architecture target for the file.', + example: 'x64', + name: 'pe.architecture', + type: 'keyword', + }, 'pe.company': { category: 'pe', description: 'Internal company name of the file, provided at compile-time.', @@ -2436,6 +2770,14 @@ export const fieldsBeat: BeatFields = { name: 'pe.file_version', type: 'keyword', }, + 'pe.imphash': { + category: 'pe', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'pe.imphash', + type: 'keyword', + }, 'pe.original_file_name': { category: 'pe', description: 'Internal name of the file, provided at compile-time.', @@ -2454,7 +2796,7 @@ export const fieldsBeat: BeatFields = { category: 'process', description: 'Array of process arguments, starting with the absolute path to the executable. May be filtered to protect sensitive information.', - example: '["/usr/bin/ssh","-l","user","10.0.0.16"]', + example: '["/usr/bin/ssh", "-l", "user", "10.0.0.16"]', name: 'process.args', type: 'keyword', }, @@ -2568,8 +2910,9 @@ export const fieldsBeat: BeatFields = { }, 'process.parent.args': { category: 'process', - description: 'Array of process arguments. May be filtered to protect sensitive information.', - example: '["ssh","-l","user","10.0.0.16"]', + description: + 'Array of process arguments, starting with the absolute path to the executable. May be filtered to protect sensitive information.', + example: '["/usr/bin/ssh", "-l", "user", "10.0.0.16"]', name: 'process.parent.args', type: 'keyword', }, @@ -2681,6 +3024,56 @@ export const fieldsBeat: BeatFields = { name: 'process.parent.name', type: 'keyword', }, + 'process.parent.pe.architecture': { + category: 'process', + description: 'CPU architecture target for the file.', + example: 'x64', + name: 'process.parent.pe.architecture', + type: 'keyword', + }, + 'process.parent.pe.company': { + category: 'process', + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + name: 'process.parent.pe.company', + type: 'keyword', + }, + 'process.parent.pe.description': { + category: 'process', + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + name: 'process.parent.pe.description', + type: 'keyword', + }, + 'process.parent.pe.file_version': { + category: 'process', + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + name: 'process.parent.pe.file_version', + type: 'keyword', + }, + 'process.parent.pe.imphash': { + category: 'process', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'process.parent.pe.imphash', + type: 'keyword', + }, + 'process.parent.pe.original_file_name': { + category: 'process', + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + name: 'process.parent.pe.original_file_name', + type: 'keyword', + }, + 'process.parent.pe.product': { + category: 'process', + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + name: 'process.parent.pe.product', + type: 'keyword', + }, 'process.parent.pgid': { category: 'process', description: 'Identifier of the group of processes the process belongs to.', @@ -2747,6 +3140,13 @@ export const fieldsBeat: BeatFields = { name: 'process.parent.working_directory', type: 'keyword', }, + 'process.pe.architecture': { + category: 'process', + description: 'CPU architecture target for the file.', + example: 'x64', + name: 'process.pe.architecture', + type: 'keyword', + }, 'process.pe.company': { category: 'process', description: 'Internal company name of the file, provided at compile-time.', @@ -2768,6 +3168,14 @@ export const fieldsBeat: BeatFields = { name: 'process.pe.file_version', type: 'keyword', }, + 'process.pe.imphash': { + category: 'process', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'process.pe.imphash', + type: 'keyword', + }, 'process.pe.original_file_name': { category: 'process', description: 'Internal name of the file, provided at compile-time.', @@ -2908,6 +3316,13 @@ export const fieldsBeat: BeatFields = { name: 'related.hash', type: 'keyword', }, + 'related.hosts': { + category: 'related', + description: + 'All hostnames or other host identifiers seen on your event. Example identifiers include FQDNs, domain names, workstation names, or aliases.', + name: 'related.hosts', + type: 'keyword', + }, 'related.ip': { category: 'related', description: 'All of the IPs seen on your event.', @@ -3092,7 +3507,7 @@ export const fieldsBeat: BeatFields = { }, 'server.ip': { category: 'server', - description: 'IP address of the server. Can be one or multiple IPv4 or IPv6 addresses.', + description: 'IP address of the server (IPv4 or IPv6).', name: 'server.ip', type: 'ip', }, @@ -3134,15 +3549,23 @@ export const fieldsBeat: BeatFields = { 'server.registered_domain': { category: 'server', description: - 'The highest registered server domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered server domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'server.registered_domain', type: 'keyword', }, + 'server.subdomain': { + category: 'server', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'east', + name: 'server.subdomain', + type: 'keyword', + }, 'server.top_level_domain': { category: 'server', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'server.top_level_domain', type: 'keyword', @@ -3195,7 +3618,7 @@ export const fieldsBeat: BeatFields = { }, 'server.user.id': { category: 'server', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'server.user.id', type: 'keyword', }, @@ -3206,6 +3629,13 @@ export const fieldsBeat: BeatFields = { name: 'server.user.name', type: 'keyword', }, + 'server.user.roles': { + category: 'server', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'server.user.roles', + type: 'keyword', + }, 'service.ephemeral_id': { category: 'service', description: @@ -3355,7 +3785,7 @@ export const fieldsBeat: BeatFields = { }, 'source.ip': { category: 'source', - description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + description: 'IP address of the source (IPv4 or IPv6).', name: 'source.ip', type: 'ip', }, @@ -3397,15 +3827,23 @@ export const fieldsBeat: BeatFields = { 'source.registered_domain': { category: 'source', description: - 'The highest registered source domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered source domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'source.registered_domain', type: 'keyword', }, + 'source.subdomain': { + category: 'source', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'east', + name: 'source.subdomain', + type: 'keyword', + }, 'source.top_level_domain': { category: 'source', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'source.top_level_domain', type: 'keyword', @@ -3458,7 +3896,7 @@ export const fieldsBeat: BeatFields = { }, 'source.user.id': { category: 'source', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'source.user.id', type: 'keyword', }, @@ -3469,6 +3907,13 @@ export const fieldsBeat: BeatFields = { name: 'source.user.name', type: 'keyword', }, + 'source.user.roles': { + category: 'source', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'source.user.roles', + type: 'keyword', + }, 'threat.framework': { category: 'threat', description: @@ -3480,51 +3925,75 @@ export const fieldsBeat: BeatFields = { 'threat.tactic.id': { category: 'threat', description: - 'The id of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', - example: 'TA0040', + 'The id of tactic used by this threat. You can use a MITRE ATT&CK® tactic, for example. (ex. https://attack.mitre.org/tactics/TA0002/ )', + example: 'TA0002', name: 'threat.tactic.id', type: 'keyword', }, 'threat.tactic.name': { category: 'threat', description: - 'Name of the type of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', - example: 'impact', + 'Name of the type of tactic used by this threat. You can use a MITRE ATT&CK® tactic, for example. (ex. https://attack.mitre.org/tactics/TA0002/)', + example: 'Execution', name: 'threat.tactic.name', type: 'keyword', }, 'threat.tactic.reference': { category: 'threat', description: - 'The reference url of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', - example: 'https://attack.mitre.org/tactics/TA0040/', + 'The reference url of tactic used by this threat. You can use a MITRE ATT&CK® tactic, for example. (ex. https://attack.mitre.org/tactics/TA0002/ )', + example: 'https://attack.mitre.org/tactics/TA0002/', name: 'threat.tactic.reference', type: 'keyword', }, 'threat.technique.id': { category: 'threat', description: - 'The id of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', - example: 'T1499', + 'The id of technique used by this threat. You can use a MITRE ATT&CK® technique, for example. (ex. https://attack.mitre.org/techniques/T1059/)', + example: 'T1059', name: 'threat.technique.id', type: 'keyword', }, 'threat.technique.name': { category: 'threat', description: - 'The name of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', - example: 'endpoint denial of service', + 'The name of technique used by this threat. You can use a MITRE ATT&CK® technique, for example. (ex. https://attack.mitre.org/techniques/T1059/)', + example: 'Command and Scripting Interpreter', name: 'threat.technique.name', type: 'keyword', }, 'threat.technique.reference': { category: 'threat', description: - 'The reference url of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', - example: 'https://attack.mitre.org/techniques/T1499/', + 'The reference url of technique used by this threat. You can use a MITRE ATT&CK® technique, for example. (ex. https://attack.mitre.org/techniques/T1059/)', + example: 'https://attack.mitre.org/techniques/T1059/', name: 'threat.technique.reference', type: 'keyword', }, + 'threat.technique.subtechnique.id': { + category: 'threat', + description: + 'The full id of subtechnique used by this threat. You can use a MITRE ATT&CK® subtechnique, for example. (ex. https://attack.mitre.org/techniques/T1059/001/)', + example: 'T1059.001', + name: 'threat.technique.subtechnique.id', + type: 'keyword', + }, + 'threat.technique.subtechnique.name': { + category: 'threat', + description: + 'The name of subtechnique used by this threat. You can use a MITRE ATT&CK® subtechnique, for example. (ex. https://attack.mitre.org/techniques/T1059/001/)', + example: 'PowerShell', + name: 'threat.technique.subtechnique.name', + type: 'keyword', + }, + 'threat.technique.subtechnique.reference': { + category: 'threat', + description: + 'The reference url of subtechnique used by this threat. You can use a MITRE ATT&CK® subtechnique, for example. (ex. https://attack.mitre.org/techniques/T1059/001/)', + example: 'https://attack.mitre.org/techniques/T1059/001/', + name: 'threat.technique.subtechnique.reference', + type: 'keyword', + }, 'tls.cipher': { category: 'tls', description: 'String indicating the cipher used during the current connection.', @@ -3544,7 +4013,7 @@ export const fieldsBeat: BeatFields = { category: 'tls', description: 'Array of PEM-encoded certificates that make up the certificate chain offered by the client. This is usually mutually-exclusive of `client.certificate` since that value should be the first certificate in the chain.', - example: '["MII...","MII..."]', + example: '["MII...", "MII..."]', name: 'tls.client.certificate_chain', type: 'keyword', }, @@ -3576,7 +4045,7 @@ export const fieldsBeat: BeatFields = { category: 'tls', description: 'Distinguished name of subject of the issuer of the x.509 certificate presented by the client.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', + example: 'CN=Example Root CA, OU=Infrastructure Team, DC=example, DC=com', name: 'tls.client.issuer', type: 'keyword', }, @@ -3604,7 +4073,7 @@ export const fieldsBeat: BeatFields = { 'tls.client.server_name': { category: 'tls', description: - 'Also called an SNI, this tells the server which hostname to which the client is attempting to connect. When this value is available, it should get copied to `destination.domain`.', + 'Also called an SNI, this tells the server which hostname to which the client is attempting to connect to. When this value is available, it should get copied to `destination.domain`.', example: 'www.elastic.co', name: 'tls.client.server_name', type: 'keyword', @@ -3612,7 +4081,7 @@ export const fieldsBeat: BeatFields = { 'tls.client.subject': { category: 'tls', description: 'Distinguished name of subject of the x.509 certificate presented by the client.', - example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', + example: 'CN=myclient, OU=Documentation Team, DC=example, DC=com', name: 'tls.client.subject', type: 'keyword', }, @@ -3620,10 +4089,181 @@ export const fieldsBeat: BeatFields = { category: 'tls', description: 'Array of ciphers offered by the client during the client hello.', example: - '["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","..."]', + '["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "..."]', name: 'tls.client.supported_ciphers', type: 'keyword', }, + 'tls.client.x509.alternative_names': { + category: 'tls', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'tls.client.x509.alternative_names', + type: 'keyword', + }, + 'tls.client.x509.issuer.common_name': { + category: 'tls', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'Example SHA2 High Assurance Server CA', + name: 'tls.client.x509.issuer.common_name', + type: 'keyword', + }, + 'tls.client.x509.issuer.country': { + category: 'tls', + description: 'List of country (C) codes', + example: 'US', + name: 'tls.client.x509.issuer.country', + type: 'keyword', + }, + 'tls.client.x509.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=Example Inc, OU=www.example.com, CN=Example SHA2 High Assurance Server CA', + name: 'tls.client.x509.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.client.x509.issuer.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'tls.client.x509.issuer.locality', + type: 'keyword', + }, + 'tls.client.x509.issuer.organization': { + category: 'tls', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'Example Inc', + name: 'tls.client.x509.issuer.organization', + type: 'keyword', + }, + 'tls.client.x509.issuer.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.example.com', + name: 'tls.client.x509.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.client.x509.issuer.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.client.x509.issuer.state_or_province', + type: 'keyword', + }, + 'tls.client.x509.not_after': { + category: 'tls', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'tls.client.x509.not_after', + type: 'date', + }, + 'tls.client.x509.not_before': { + category: 'tls', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'tls.client.x509.not_before', + type: 'date', + }, + 'tls.client.x509.public_key_algorithm': { + category: 'tls', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'tls.client.x509.public_key_algorithm', + type: 'keyword', + }, + 'tls.client.x509.public_key_curve': { + category: 'tls', + description: + 'The curve used by the elliptic curve public key algorithm. This is algorithm specific.', + example: 'nistp521', + name: 'tls.client.x509.public_key_curve', + type: 'keyword', + }, + 'tls.client.x509.public_key_exponent': { + category: 'tls', + description: 'Exponent used to derive the public key. This is algorithm specific.', + example: 65537, + name: 'tls.client.x509.public_key_exponent', + type: 'long', + }, + 'tls.client.x509.public_key_size': { + category: 'tls', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'tls.client.x509.public_key_size', + type: 'long', + }, + 'tls.client.x509.serial_number': { + category: 'tls', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters.', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'tls.client.x509.serial_number', + type: 'keyword', + }, + 'tls.client.x509.signature_algorithm': { + category: 'tls', + description: + 'Identifier for certificate signature algorithm. We recommend using names found in Go Lang Crypto library. See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353.', + example: 'SHA256-RSA', + name: 'tls.client.x509.signature_algorithm', + type: 'keyword', + }, + 'tls.client.x509.subject.common_name': { + category: 'tls', + description: 'List of common names (CN) of subject.', + example: 'shared.global.example.net', + name: 'tls.client.x509.subject.common_name', + type: 'keyword', + }, + 'tls.client.x509.subject.country': { + category: 'tls', + description: 'List of country (C) code', + example: 'US', + name: 'tls.client.x509.subject.country', + type: 'keyword', + }, + 'tls.client.x509.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Example, Inc., CN=shared.global.example.net', + name: 'tls.client.x509.subject.distinguished_name', + type: 'keyword', + }, + 'tls.client.x509.subject.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'tls.client.x509.subject.locality', + type: 'keyword', + }, + 'tls.client.x509.subject.organization': { + category: 'tls', + description: 'List of organizations (O) of subject.', + example: 'Example, Inc.', + name: 'tls.client.x509.subject.organization', + type: 'keyword', + }, + 'tls.client.x509.subject.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of subject.', + name: 'tls.client.x509.subject.organizational_unit', + type: 'keyword', + }, + 'tls.client.x509.subject.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.client.x509.subject.state_or_province', + type: 'keyword', + }, + 'tls.client.x509.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.client.x509.version_number', + type: 'keyword', + }, 'tls.curve': { category: 'tls', description: 'String indicating the curve used for the given cipher, when applicable.', @@ -3665,7 +4305,7 @@ export const fieldsBeat: BeatFields = { category: 'tls', description: 'Array of PEM-encoded certificates that make up the certificate chain offered by the server. This is usually mutually-exclusive of `server.certificate` since that value should be the first certificate in the chain.', - example: '["MII...","MII..."]', + example: '["MII...", "MII..."]', name: 'tls.server.certificate_chain', type: 'keyword', }, @@ -3696,7 +4336,7 @@ export const fieldsBeat: BeatFields = { 'tls.server.issuer': { category: 'tls', description: 'Subject of the issuer of the x.509 certificate presented by the server.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', + example: 'CN=Example Root CA, OU=Infrastructure Team, DC=example, DC=com', name: 'tls.server.issuer', type: 'keyword', }, @@ -3724,10 +4364,181 @@ export const fieldsBeat: BeatFields = { 'tls.server.subject': { category: 'tls', description: 'Subject of the x.509 certificate presented by the server.', - example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', + example: 'CN=www.example.com, OU=Infrastructure Team, DC=example, DC=com', name: 'tls.server.subject', type: 'keyword', }, + 'tls.server.x509.alternative_names': { + category: 'tls', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'tls.server.x509.alternative_names', + type: 'keyword', + }, + 'tls.server.x509.issuer.common_name': { + category: 'tls', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'Example SHA2 High Assurance Server CA', + name: 'tls.server.x509.issuer.common_name', + type: 'keyword', + }, + 'tls.server.x509.issuer.country': { + category: 'tls', + description: 'List of country (C) codes', + example: 'US', + name: 'tls.server.x509.issuer.country', + type: 'keyword', + }, + 'tls.server.x509.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=Example Inc, OU=www.example.com, CN=Example SHA2 High Assurance Server CA', + name: 'tls.server.x509.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.server.x509.issuer.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'tls.server.x509.issuer.locality', + type: 'keyword', + }, + 'tls.server.x509.issuer.organization': { + category: 'tls', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'Example Inc', + name: 'tls.server.x509.issuer.organization', + type: 'keyword', + }, + 'tls.server.x509.issuer.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.example.com', + name: 'tls.server.x509.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.server.x509.issuer.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.server.x509.issuer.state_or_province', + type: 'keyword', + }, + 'tls.server.x509.not_after': { + category: 'tls', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'tls.server.x509.not_after', + type: 'date', + }, + 'tls.server.x509.not_before': { + category: 'tls', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'tls.server.x509.not_before', + type: 'date', + }, + 'tls.server.x509.public_key_algorithm': { + category: 'tls', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'tls.server.x509.public_key_algorithm', + type: 'keyword', + }, + 'tls.server.x509.public_key_curve': { + category: 'tls', + description: + 'The curve used by the elliptic curve public key algorithm. This is algorithm specific.', + example: 'nistp521', + name: 'tls.server.x509.public_key_curve', + type: 'keyword', + }, + 'tls.server.x509.public_key_exponent': { + category: 'tls', + description: 'Exponent used to derive the public key. This is algorithm specific.', + example: 65537, + name: 'tls.server.x509.public_key_exponent', + type: 'long', + }, + 'tls.server.x509.public_key_size': { + category: 'tls', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'tls.server.x509.public_key_size', + type: 'long', + }, + 'tls.server.x509.serial_number': { + category: 'tls', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters.', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'tls.server.x509.serial_number', + type: 'keyword', + }, + 'tls.server.x509.signature_algorithm': { + category: 'tls', + description: + 'Identifier for certificate signature algorithm. We recommend using names found in Go Lang Crypto library. See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353.', + example: 'SHA256-RSA', + name: 'tls.server.x509.signature_algorithm', + type: 'keyword', + }, + 'tls.server.x509.subject.common_name': { + category: 'tls', + description: 'List of common names (CN) of subject.', + example: 'shared.global.example.net', + name: 'tls.server.x509.subject.common_name', + type: 'keyword', + }, + 'tls.server.x509.subject.country': { + category: 'tls', + description: 'List of country (C) code', + example: 'US', + name: 'tls.server.x509.subject.country', + type: 'keyword', + }, + 'tls.server.x509.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Example, Inc., CN=shared.global.example.net', + name: 'tls.server.x509.subject.distinguished_name', + type: 'keyword', + }, + 'tls.server.x509.subject.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'tls.server.x509.subject.locality', + type: 'keyword', + }, + 'tls.server.x509.subject.organization': { + category: 'tls', + description: 'List of organizations (O) of subject.', + example: 'Example, Inc.', + name: 'tls.server.x509.subject.organization', + type: 'keyword', + }, + 'tls.server.x509.subject.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of subject.', + name: 'tls.server.x509.subject.organizational_unit', + type: 'keyword', + }, + 'tls.server.x509.subject.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.server.x509.subject.state_or_province', + type: 'keyword', + }, + 'tls.server.x509.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.server.x509.version_number', + type: 'keyword', + }, 'tls.version': { category: 'tls', description: 'Numeric part of the version parsed from the original string.', @@ -3742,26 +4553,34 @@ export const fieldsBeat: BeatFields = { name: 'tls.version_protocol', type: 'keyword', }, - 'tracing.trace.id': { - category: 'tracing', + 'span.id': { + category: 'span', + description: + 'Unique identifier of the span within the scope of its trace. A span represents an operation within a transaction, such as a request to another service, or a database query.', + example: '3ff9a8981b7ccd5a', + name: 'span.id', + type: 'keyword', + }, + 'trace.id': { + category: 'trace', description: 'Unique identifier of the trace. A trace groups multiple events like transactions that belong together. For example, a user request handled by multiple inter-connected services.', example: '4bf92f3577b34da6a3ce929d0e0e4736', - name: 'tracing.trace.id', + name: 'trace.id', type: 'keyword', }, - 'tracing.transaction.id': { - category: 'tracing', + 'transaction.id': { + category: 'transaction', description: - 'Unique identifier of the transaction. A transaction is the highest level of work measured within a service, such as a request to a server.', + 'Unique identifier of the transaction within the scope of its trace. A transaction is the highest level of work measured within a service, such as a request to a server.', example: '00f067aa0ba902b7', - name: 'tracing.transaction.id', + name: 'transaction.id', type: 'keyword', }, 'url.domain': { category: 'url', description: - 'Domain of the url, such as "www.elastic.co". In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field.', + 'Domain of the url, such as "www.elastic.co". In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field. If the URL contains a literal IPv6 address enclosed by `[` and `]` (IETF RFC 2732), the `[` and `]` characters should also be captured in the `domain` field.', example: 'www.elastic.co', name: 'url.domain', type: 'keyword', @@ -3769,7 +4588,7 @@ export const fieldsBeat: BeatFields = { 'url.extension': { category: 'url', description: - 'The field contains the file extension from the original request url. The file extension is only set if it exists, as not every url has a file extension. The leading period must not be included. For example, the value must be "png", not ".png".', + 'The field contains the file extension from the original request url, excluding the leading dot. The file extension is only set if it exists, as not every url has a file extension. The leading period must not be included. For example, the value must be "png", not ".png". Note that when the file name has multiple extensions (example.tar.gz), only the last one should be captured ("gz", not "tar.gz").', example: 'png', name: 'url.extension', type: 'keyword', @@ -3827,8 +4646,8 @@ export const fieldsBeat: BeatFields = { 'url.registered_domain': { category: 'url', description: - 'The highest registered url domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', + 'The highest registered url domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'example.com', name: 'url.registered_domain', type: 'keyword', }, @@ -3839,10 +4658,18 @@ export const fieldsBeat: BeatFields = { name: 'url.scheme', type: 'keyword', }, + 'url.subdomain': { + category: 'url', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'east', + name: 'url.subdomain', + type: 'keyword', + }, 'url.top_level_domain': { category: 'url', description: - 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', example: 'co.uk', name: 'url.top_level_domain', type: 'keyword', @@ -3853,6 +4680,72 @@ export const fieldsBeat: BeatFields = { name: 'url.username', type: 'keyword', }, + 'user.changes.domain': { + category: 'user', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.changes.domain', + type: 'keyword', + }, + 'user.changes.email': { + category: 'user', + description: 'User email address.', + name: 'user.changes.email', + type: 'keyword', + }, + 'user.changes.full_name': { + category: 'user', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'user.changes.full_name', + type: 'keyword', + }, + 'user.changes.group.domain': { + category: 'user', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.changes.group.domain', + type: 'keyword', + }, + 'user.changes.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform.', + name: 'user.changes.group.id', + type: 'keyword', + }, + 'user.changes.group.name': { + category: 'user', + description: 'Name of the group.', + name: 'user.changes.group.name', + type: 'keyword', + }, + 'user.changes.hash': { + category: 'user', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'user.changes.hash', + type: 'keyword', + }, + 'user.changes.id': { + category: 'user', + description: 'Unique identifier of the user.', + name: 'user.changes.id', + type: 'keyword', + }, + 'user.changes.name': { + category: 'user', + description: 'Short name or login of the user.', + example: 'albert', + name: 'user.changes.name', + type: 'keyword', + }, + 'user.changes.roles': { + category: 'user', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'user.changes.roles', + type: 'keyword', + }, 'user.domain': { category: 'user', description: @@ -3860,6 +4753,72 @@ export const fieldsBeat: BeatFields = { name: 'user.domain', type: 'keyword', }, + 'user.effective.domain': { + category: 'user', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.effective.domain', + type: 'keyword', + }, + 'user.effective.email': { + category: 'user', + description: 'User email address.', + name: 'user.effective.email', + type: 'keyword', + }, + 'user.effective.full_name': { + category: 'user', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'user.effective.full_name', + type: 'keyword', + }, + 'user.effective.group.domain': { + category: 'user', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.effective.group.domain', + type: 'keyword', + }, + 'user.effective.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform.', + name: 'user.effective.group.id', + type: 'keyword', + }, + 'user.effective.group.name': { + category: 'user', + description: 'Name of the group.', + name: 'user.effective.group.name', + type: 'keyword', + }, + 'user.effective.hash': { + category: 'user', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'user.effective.hash', + type: 'keyword', + }, + 'user.effective.id': { + category: 'user', + description: 'Unique identifier of the user.', + name: 'user.effective.id', + type: 'keyword', + }, + 'user.effective.name': { + category: 'user', + description: 'Short name or login of the user.', + example: 'albert', + name: 'user.effective.name', + type: 'keyword', + }, + 'user.effective.roles': { + category: 'user', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'user.effective.roles', + type: 'keyword', + }, 'user.email': { category: 'user', description: 'User email address.', @@ -3901,7 +4860,7 @@ export const fieldsBeat: BeatFields = { }, 'user.id': { category: 'user', - description: 'Unique identifiers of the user.', + description: 'Unique identifier of the user.', name: 'user.id', type: 'keyword', }, @@ -3912,6 +4871,79 @@ export const fieldsBeat: BeatFields = { name: 'user.name', type: 'keyword', }, + 'user.roles': { + category: 'user', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'user.roles', + type: 'keyword', + }, + 'user.target.domain': { + category: 'user', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.target.domain', + type: 'keyword', + }, + 'user.target.email': { + category: 'user', + description: 'User email address.', + name: 'user.target.email', + type: 'keyword', + }, + 'user.target.full_name': { + category: 'user', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'user.target.full_name', + type: 'keyword', + }, + 'user.target.group.domain': { + category: 'user', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.target.group.domain', + type: 'keyword', + }, + 'user.target.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform.', + name: 'user.target.group.id', + type: 'keyword', + }, + 'user.target.group.name': { + category: 'user', + description: 'Name of the group.', + name: 'user.target.group.name', + type: 'keyword', + }, + 'user.target.hash': { + category: 'user', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'user.target.hash', + type: 'keyword', + }, + 'user.target.id': { + category: 'user', + description: 'Unique identifier of the user.', + name: 'user.target.id', + type: 'keyword', + }, + 'user.target.name': { + category: 'user', + description: 'Short name or login of the user.', + example: 'albert', + name: 'user.target.name', + type: 'keyword', + }, + 'user.target.roles': { + category: 'user', + description: 'Array of user roles at the time of the event.', + example: '["kibana_admin", "reporting_user"]', + name: 'user.target.roles', + type: 'keyword', + }, 'user_agent.device.name': { category: 'user_agent', description: 'Name of the device.', @@ -3969,6 +5001,14 @@ export const fieldsBeat: BeatFields = { name: 'user_agent.os.platform', type: 'keyword', }, + 'user_agent.os.type': { + category: 'user_agent', + description: + "Use the `os.type` field to categorize the operating system into one of the broad commercial families. One of these following values should be used (lowercase): linux, macos, unix, windows. If the OS you're dealing with is not in the list, the field should not be populated. Please let us know by opening an issue with ECS, to propose its addition.", + example: 'macos', + name: 'user_agent.os.type', + type: 'keyword', + }, 'user_agent.os.version': { category: 'user_agent', description: 'Operating system version as a raw string.', @@ -4098,54 +5138,219 @@ export const fieldsBeat: BeatFields = { name: 'vulnerability.severity', type: 'keyword', }, - 'agent.hostname': { - category: 'agent', + 'x509.alternative_names': { + category: 'x509', description: - 'Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. ', - name: 'agent.hostname', + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'x509.alternative_names', type: 'keyword', }, - 'beat.timezone': { - category: 'beat', - name: 'beat.timezone', - type: 'alias', + 'x509.issuer.common_name': { + category: 'x509', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'Example SHA2 High Assurance Server CA', + name: 'x509.issuer.common_name', + type: 'keyword', }, - fields: { - category: 'base', - description: 'Contains user configurable fields. ', - name: 'fields', - type: 'object', + 'x509.issuer.country': { + category: 'x509', + description: 'List of country (C) codes', + example: 'US', + name: 'x509.issuer.country', + type: 'keyword', }, - 'beat.name': { - category: 'beat', - name: 'beat.name', - type: 'alias', + 'x509.issuer.distinguished_name': { + category: 'x509', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=Example Inc, OU=www.example.com, CN=Example SHA2 High Assurance Server CA', + name: 'x509.issuer.distinguished_name', + type: 'keyword', }, - 'beat.hostname': { - category: 'beat', - name: 'beat.hostname', - type: 'alias', + 'x509.issuer.locality': { + category: 'x509', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'x509.issuer.locality', + type: 'keyword', }, - 'timeseries.instance': { - category: 'timeseries', - description: 'Time series instance id', - name: 'timeseries.instance', + 'x509.issuer.organization': { + category: 'x509', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'Example Inc', + name: 'x509.issuer.organization', type: 'keyword', }, - 'cloud.project.id': { - category: 'cloud', - description: 'Name of the project in Google Cloud. ', - example: 'project-x', - name: 'cloud.project.id', + 'x509.issuer.organizational_unit': { + category: 'x509', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.example.com', + name: 'x509.issuer.organizational_unit', + type: 'keyword', }, - 'cloud.image.id': { - category: 'cloud', - description: 'Image ID for the cloud instance. ', - example: 'ami-abcd1234', - name: 'cloud.image.id', + 'x509.issuer.state_or_province': { + category: 'x509', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'x509.issuer.state_or_province', + type: 'keyword', }, - 'meta.cloud.provider': { - category: 'meta', + 'x509.not_after': { + category: 'x509', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'x509.not_after', + type: 'date', + }, + 'x509.not_before': { + category: 'x509', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'x509.not_before', + type: 'date', + }, + 'x509.public_key_algorithm': { + category: 'x509', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'x509.public_key_algorithm', + type: 'keyword', + }, + 'x509.public_key_curve': { + category: 'x509', + description: + 'The curve used by the elliptic curve public key algorithm. This is algorithm specific.', + example: 'nistp521', + name: 'x509.public_key_curve', + type: 'keyword', + }, + 'x509.public_key_exponent': { + category: 'x509', + description: 'Exponent used to derive the public key. This is algorithm specific.', + example: 65537, + name: 'x509.public_key_exponent', + type: 'long', + }, + 'x509.public_key_size': { + category: 'x509', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'x509.public_key_size', + type: 'long', + }, + 'x509.serial_number': { + category: 'x509', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters.', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'x509.serial_number', + type: 'keyword', + }, + 'x509.signature_algorithm': { + category: 'x509', + description: + 'Identifier for certificate signature algorithm. We recommend using names found in Go Lang Crypto library. See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353.', + example: 'SHA256-RSA', + name: 'x509.signature_algorithm', + type: 'keyword', + }, + 'x509.subject.common_name': { + category: 'x509', + description: 'List of common names (CN) of subject.', + example: 'shared.global.example.net', + name: 'x509.subject.common_name', + type: 'keyword', + }, + 'x509.subject.country': { + category: 'x509', + description: 'List of country (C) code', + example: 'US', + name: 'x509.subject.country', + type: 'keyword', + }, + 'x509.subject.distinguished_name': { + category: 'x509', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Example, Inc., CN=shared.global.example.net', + name: 'x509.subject.distinguished_name', + type: 'keyword', + }, + 'x509.subject.locality': { + category: 'x509', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'x509.subject.locality', + type: 'keyword', + }, + 'x509.subject.organization': { + category: 'x509', + description: 'List of organizations (O) of subject.', + example: 'Example, Inc.', + name: 'x509.subject.organization', + type: 'keyword', + }, + 'x509.subject.organizational_unit': { + category: 'x509', + description: 'List of organizational units (OU) of subject.', + name: 'x509.subject.organizational_unit', + type: 'keyword', + }, + 'x509.subject.state_or_province': { + category: 'x509', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'x509.subject.state_or_province', + type: 'keyword', + }, + 'x509.version_number': { + category: 'x509', + description: 'Version of x509 format.', + example: 3, + name: 'x509.version_number', + type: 'keyword', + }, + 'agent.hostname': { + category: 'agent', + description: + 'Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. ', + name: 'agent.hostname', + type: 'keyword', + }, + 'beat.timezone': { + category: 'beat', + name: 'beat.timezone', + type: 'alias', + }, + fields: { + category: 'base', + description: 'Contains user configurable fields. ', + name: 'fields', + type: 'object', + }, + 'beat.name': { + category: 'beat', + name: 'beat.name', + type: 'alias', + }, + 'beat.hostname': { + category: 'beat', + name: 'beat.hostname', + type: 'alias', + }, + 'timeseries.instance': { + category: 'timeseries', + description: 'Time series instance id', + name: 'timeseries.instance', + type: 'keyword', + }, + 'cloud.image.id': { + category: 'cloud', + description: 'Image ID for the cloud instance. ', + example: 'ami-abcd1234', + name: 'cloud.image.id', + }, + 'meta.cloud.provider': { + category: 'meta', name: 'meta.cloud.provider', type: 'alias', }, @@ -4244,6 +5449,12 @@ export const fieldsBeat: BeatFields = { name: 'kubernetes.node.name', type: 'keyword', }, + 'kubernetes.node.hostname': { + category: 'kubernetes', + description: 'Kubernetes hostname as reported by the node’s kernel ', + name: 'kubernetes.node.hostname', + type: 'keyword', + }, 'kubernetes.labels.*': { category: 'kubernetes', description: 'Kubernetes labels map ', @@ -4256,6 +5467,12 @@ export const fieldsBeat: BeatFields = { name: 'kubernetes.annotations.*', type: 'object', }, + 'kubernetes.service.selectors.*': { + category: 'kubernetes', + description: 'Kubernetes Service selectors map ', + name: 'kubernetes.service.selectors.*', + type: 'object', + }, 'kubernetes.replicaset.name': { category: 'kubernetes', description: 'Kubernetes replicaset name ', @@ -4392,30 +5609,6 @@ export const fieldsBeat: BeatFields = { name: 'user.audit.name', type: 'keyword', }, - 'user.effective.id': { - category: 'user', - description: 'Effective user ID.', - name: 'user.effective.id', - type: 'keyword', - }, - 'user.effective.name': { - category: 'user', - description: 'Effective user name.', - name: 'user.effective.name', - type: 'keyword', - }, - 'user.effective.group.id': { - category: 'user', - description: 'Effective group ID.', - name: 'user.effective.group.id', - type: 'keyword', - }, - 'user.effective.group.name': { - category: 'user', - description: 'Effective group name.', - name: 'user.effective.group.name', - type: 'keyword', - }, 'user.filesystem.id': { category: 'user', description: 'Filesystem user ID.', @@ -4474,11 +5667,6 @@ export const fieldsBeat: BeatFields = { name: 'user.uid', type: 'alias', }, - 'user.euid': { - category: 'user', - name: 'user.euid', - type: 'alias', - }, 'user.fsuid': { category: 'user', name: 'user.fsuid', @@ -4494,11 +5682,6 @@ export const fieldsBeat: BeatFields = { name: 'user.gid', type: 'alias', }, - 'user.egid': { - category: 'user', - name: 'user.egid', - type: 'alias', - }, 'user.sgid': { category: 'user', name: 'user.sgid', @@ -4519,11 +5702,6 @@ export const fieldsBeat: BeatFields = { name: 'user.name_map.uid', type: 'alias', }, - 'user.name_map.euid': { - category: 'user', - name: 'user.name_map.euid', - type: 'alias', - }, 'user.name_map.fsuid': { category: 'user', name: 'user.name_map.fsuid', @@ -4539,11 +5717,6 @@ export const fieldsBeat: BeatFields = { name: 'user.name_map.gid', type: 'alias', }, - 'user.name_map.egid': { - category: 'user', - name: 'user.name_map.egid', - type: 'alias', - }, 'user.name_map.sgid': { category: 'user', name: 'user.name_map.sgid', @@ -6273,6 +7446,12 @@ export const fieldsBeat: BeatFields = { name: 'system.audit.host.os.kernel', type: 'keyword', }, + 'system.audit.host.os.type': { + category: 'system', + description: 'OS type (see ECS os.type). ', + name: 'system.audit.host.os.type', + type: 'keyword', + }, 'system.audit.package.entity_id': { category: 'system', description: @@ -6394,13 +7573,6 @@ export const fieldsBeat: BeatFields = { name: 'system.audit.user.password.last_changed', type: 'date', }, - 'log.file.path': { - category: 'log', - description: - 'The file from which the line was read. This field contains the absolute path to the file. For example: `/var/log/system.log`. ', - name: 'log.file.path', - type: 'keyword', - }, 'log.source.address': { category: 'log', description: 'Source address from which the log event was read / sent from. ', @@ -7093,6 +8265,21 @@ export const fieldsBeat: BeatFields = { name: 'elasticsearch.audit.user.roles', type: 'keyword', }, + 'elasticsearch.audit.user.run_as.name': { + category: 'elasticsearch', + name: 'elasticsearch.audit.user.run_as.name', + type: 'keyword', + }, + 'elasticsearch.audit.user.run_as.realm': { + category: 'elasticsearch', + name: 'elasticsearch.audit.user.run_as.realm', + type: 'keyword', + }, + 'elasticsearch.audit.component': { + category: 'elasticsearch', + name: 'elasticsearch.audit.component', + type: 'keyword', + }, 'elasticsearch.audit.action': { category: 'elasticsearch', description: 'The name of the action that was executed', @@ -7152,6 +8339,11 @@ export const fieldsBeat: BeatFields = { name: 'elasticsearch.audit.message', type: 'text', }, + 'elasticsearch.audit.invalidate.apikeys.owned_by_authenticated_user': { + category: 'elasticsearch', + name: 'elasticsearch.audit.invalidate.apikeys.owned_by_authenticated_user', + type: 'boolean', + }, 'elasticsearch.deprecation': { category: 'elasticsearch', description: '', @@ -7970,6 +9162,77 @@ export const fieldsBeat: BeatFields = { name: 'kafka.log.trace.message', type: 'text', }, + 'kibana.session_id': { + category: 'kibana', + description: + 'The ID of the user session associated with this event. Each login attempt results in a unique session id.', + example: '123e4567-e89b-12d3-a456-426614174000', + name: 'kibana.session_id', + type: 'keyword', + }, + 'kibana.space_id': { + category: 'kibana', + description: 'The id of the space associated with this event.', + example: 'default', + name: 'kibana.space_id', + type: 'keyword', + }, + 'kibana.saved_object.type': { + category: 'kibana', + description: 'The type of the saved object associated with this event.', + example: 'dashboard', + name: 'kibana.saved_object.type', + type: 'keyword', + }, + 'kibana.saved_object.id': { + category: 'kibana', + description: 'The id of the saved object associated with this event.', + example: '6295bdd0-0a0e-11e7-825f-6748cda7d858', + name: 'kibana.saved_object.id', + type: 'keyword', + }, + 'kibana.add_to_spaces': { + category: 'kibana', + description: 'The set of space ids that a saved object was shared to.', + example: "['default', 'marketing']", + name: 'kibana.add_to_spaces', + type: 'keyword', + }, + 'kibana.delete_from_spaces': { + category: 'kibana', + description: 'The set of space ids that a saved object was removed from.', + example: "['default', 'marketing']", + name: 'kibana.delete_from_spaces', + type: 'keyword', + }, + 'kibana.authentication_provider': { + category: 'kibana', + description: 'The authentication provider associated with a login event.', + example: 'basic1', + name: 'kibana.authentication_provider', + type: 'keyword', + }, + 'kibana.authentication_type': { + category: 'kibana', + description: 'The authentication provider type associated with a login event.', + example: 'basic', + name: 'kibana.authentication_type', + type: 'keyword', + }, + 'kibana.authentication_realm': { + category: 'kibana', + description: 'The Elasticsearch authentication realm name which fulfilled a login event.', + example: 'native', + name: 'kibana.authentication_realm', + type: 'keyword', + }, + 'kibana.lookup_realm': { + category: 'kibana', + description: 'The Elasticsearch lookup realm which fulfilled a login event.', + example: 'native', + name: 'kibana.lookup_realm', + type: 'keyword', + }, 'kibana.log.tags': { category: 'kibana', description: 'Kibana logging tags. ', @@ -8040,6 +9303,11 @@ export const fieldsBeat: BeatFields = { name: 'logstash.log.log_event', type: 'object', }, + 'logstash.log.log_event.action': { + category: 'logstash', + name: 'logstash.log.log_event.action', + type: 'keyword', + }, 'logstash.log.pipeline_id': { category: 'logstash', description: 'The ID of the pipeline. ', @@ -8637,6 +9905,34 @@ export const fieldsBeat: BeatFields = { name: 'nginx.ingress_controller.remote_ip_list', type: 'array', }, + 'nginx.ingress_controller.upstream_address_list': { + category: 'nginx', + description: + 'An array of the upstream addresses. It is a list because it is common that several upstream servers were contacted during request processing. ', + name: 'nginx.ingress_controller.upstream_address_list', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.response.length_list': { + category: 'nginx', + description: + 'An array of upstream response lengths. It is a list because it is common that several upstream servers were contacted during request processing. ', + name: 'nginx.ingress_controller.upstream.response.length_list', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.response.time_list': { + category: 'nginx', + description: + 'An array of upstream response durations. It is a list because it is common that several upstream servers were contacted during request processing. ', + name: 'nginx.ingress_controller.upstream.response.time_list', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.response.status_code_list': { + category: 'nginx', + description: + 'An array of upstream response status codes. It is a list because it is common that several upstream servers were contacted during request processing. ', + name: 'nginx.ingress_controller.upstream.response.status_code_list', + type: 'keyword', + }, 'nginx.ingress_controller.http.request.length': { category: 'nginx', description: 'The request length (including request line, header, and request body) ', @@ -8665,7 +9961,8 @@ export const fieldsBeat: BeatFields = { }, 'nginx.ingress_controller.upstream.response.length': { category: 'nginx', - description: 'The length of the response obtained from the upstream server ', + description: + 'The length of the response obtained from the upstream server. If several servers were contacted during request process, the summary of the multiple response lengths is stored. ', name: 'nginx.ingress_controller.upstream.response.length', type: 'long', format: 'bytes', @@ -8673,36 +9970,38 @@ export const fieldsBeat: BeatFields = { 'nginx.ingress_controller.upstream.response.time': { category: 'nginx', description: - 'The time spent on receiving the response from the upstream server as seconds with millisecond resolution ', + 'The time spent on receiving the response from the upstream as seconds with millisecond resolution. If several servers were contacted during request process, the summary of the multiple response times is stored. ', name: 'nginx.ingress_controller.upstream.response.time', type: 'double', format: 'duration', }, 'nginx.ingress_controller.upstream.response.status_code': { category: 'nginx', - description: 'The status code of the response obtained from the upstream server ', + description: + 'The status code of the response obtained from the upstream server. If several servers were contacted during request process, only the status code of the response from the last one is stored in this field. ', name: 'nginx.ingress_controller.upstream.response.status_code', type: 'long', }, - 'nginx.ingress_controller.http.request.id': { - category: 'nginx', - description: 'The randomly generated ID of the request ', - name: 'nginx.ingress_controller.http.request.id', - type: 'keyword', - }, 'nginx.ingress_controller.upstream.ip': { category: 'nginx', description: - 'The IP address of the upstream server. If several servers were contacted during request processing, their addresses are separated by commas. ', + 'The IP address of the upstream server. If several servers were contacted during request process, only the last one is stored in this field. ', name: 'nginx.ingress_controller.upstream.ip', type: 'ip', }, 'nginx.ingress_controller.upstream.port': { category: 'nginx', - description: 'The port of the upstream server. ', + description: + 'The port of the upstream server. If several servers were contacted during request process, only the last one is stored in this field. ', name: 'nginx.ingress_controller.upstream.port', type: 'long', }, + 'nginx.ingress_controller.http.request.id': { + category: 'nginx', + description: 'The randomly generated ID of the request ', + name: 'nginx.ingress_controller.http.request.id', + type: 'keyword', + }, 'nginx.ingress_controller.body_sent.bytes': { category: 'nginx', name: 'nginx.ingress_controller.body_sent.bytes', @@ -8831,6 +10130,78 @@ export const fieldsBeat: BeatFields = { name: 'osquery.result.calendar_time', type: 'keyword', }, + 'pensando.dfw.action': { + category: 'pensando', + description: 'Action on the flow. ', + name: 'pensando.dfw.action', + type: 'keyword', + }, + 'pensando.dfw.app_id': { + category: 'pensando', + description: 'Application ID ', + name: 'pensando.dfw.app_id', + type: 'integer', + }, + 'pensando.dfw.destination_address': { + category: 'pensando', + description: 'Address of destination. ', + name: 'pensando.dfw.destination_address', + type: 'keyword', + }, + 'pensando.dfw.destination_port': { + category: 'pensando', + description: 'Port of destination. ', + name: 'pensando.dfw.destination_port', + type: 'integer', + }, + 'pensando.dfw.direction': { + category: 'pensando', + description: 'Direction of the flow ', + name: 'pensando.dfw.direction', + type: 'keyword', + }, + 'pensando.dfw.protocol': { + category: 'pensando', + description: 'Protocol of the flow ', + name: 'pensando.dfw.protocol', + type: 'keyword', + }, + 'pensando.dfw.rule_id': { + category: 'pensando', + description: 'Rule ID that was matched. ', + name: 'pensando.dfw.rule_id', + type: 'keyword', + }, + 'pensando.dfw.session_id': { + category: 'pensando', + description: 'Session ID of the flow ', + name: 'pensando.dfw.session_id', + type: 'integer', + }, + 'pensando.dfw.session_state': { + category: 'pensando', + description: 'Session state of the flow. ', + name: 'pensando.dfw.session_state', + type: 'keyword', + }, + 'pensando.dfw.source_address': { + category: 'pensando', + description: 'Source address of the flow. ', + name: 'pensando.dfw.source_address', + type: 'keyword', + }, + 'pensando.dfw.source_port': { + category: 'pensando', + description: 'Source port of the flow. ', + name: 'pensando.dfw.source_port', + type: 'integer', + }, + 'pensando.dfw.timestamp': { + category: 'pensando', + description: 'Timestamp of the log. ', + name: 'pensando.dfw.timestamp', + type: 'date', + }, 'postgresql.log.timestamp': { category: 'postgresql', description: 'The timestamp from the log line. ', @@ -8838,26 +10209,52 @@ export const fieldsBeat: BeatFields = { }, 'postgresql.log.core_id': { category: 'postgresql', - description: 'Core id ', + description: + 'Core id. (deprecated, there is no core_id in PostgreSQL logs, this is actually session_line_number). ', name: 'postgresql.log.core_id', + type: 'alias', + }, + 'postgresql.log.client_addr': { + category: 'postgresql', + description: 'Host where the connection originated from. ', + example: '127.0.0.1', + name: 'postgresql.log.client_addr', + }, + 'postgresql.log.client_port': { + category: 'postgresql', + description: 'Port where the connection originated from. ', + example: '59700', + name: 'postgresql.log.client_port', + }, + 'postgresql.log.session_id': { + category: 'postgresql', + description: 'PostgreSQL session. ', + example: '5ff1dd98.22', + name: 'postgresql.log.session_id', + }, + 'postgresql.log.session_line_number': { + category: 'postgresql', + description: 'Line number inside a session. (%l in `log_line_prefix`). ', + name: 'postgresql.log.session_line_number', type: 'long', }, 'postgresql.log.database': { category: 'postgresql', - description: 'Name of database ', - example: 'mydb', + description: 'Name of database. ', + example: 'postgres', name: 'postgresql.log.database', }, 'postgresql.log.query': { category: 'postgresql', - description: 'Query statement. ', + description: + 'Query statement. In the case of CSV parse, look at command_tag to get more context. ', example: 'SELECT * FROM users;', name: 'postgresql.log.query', }, 'postgresql.log.query_step': { category: 'postgresql', description: - 'Statement step when using extended query protocol (one of statement, parse, bind or execute) ', + 'Statement step when using extended query protocol (one of statement, parse, bind or execute). ', example: 'parse', name: 'postgresql.log.query_step', }, @@ -8868,20 +10265,98 @@ export const fieldsBeat: BeatFields = { example: 'pdo_stmt_00000001', name: 'postgresql.log.query_name', }, - 'postgresql.log.error.code': { + 'postgresql.log.command_tag': { category: 'postgresql', - description: 'Error code returned by Postgres (if any)', - name: 'postgresql.log.error.code', + description: + "Type of session's current command. The complete list can be found at: src/include/tcop/cmdtaglist.h ", + example: 'SELECT', + name: 'postgresql.log.command_tag', + }, + 'postgresql.log.session_start_time': { + category: 'postgresql', + description: 'Time when this session started. ', + name: 'postgresql.log.session_start_time', + type: 'date', + }, + 'postgresql.log.virtual_transaction_id': { + category: 'postgresql', + description: 'Backend local transaction id. ', + name: 'postgresql.log.virtual_transaction_id', + }, + 'postgresql.log.transaction_id': { + category: 'postgresql', + description: 'The id of current transaction. ', + name: 'postgresql.log.transaction_id', type: 'long', }, - 'postgresql.log.timezone': { + 'postgresql.log.sql_state_code': { category: 'postgresql', - name: 'postgresql.log.timezone', + description: + 'State code returned by Postgres (if any). See also https://www.postgresql.org/docs/current/errcodes-appendix.html ', + name: 'postgresql.log.sql_state_code', + type: 'keyword', + }, + 'postgresql.log.detail': { + category: 'postgresql', + description: + "More information about the message, parameters in case of a parametrized query. e.g. 'Role \\\"user\\\" does not exist.', 'parameters: $1 = 42', etc. ", + name: 'postgresql.log.detail', + }, + 'postgresql.log.hint': { + category: 'postgresql', + description: 'A possible solution to solve an error. ', + name: 'postgresql.log.hint', + }, + 'postgresql.log.internal_query': { + category: 'postgresql', + description: 'Internal query that led to the error (if any). ', + name: 'postgresql.log.internal_query', + }, + 'postgresql.log.internal_query_pos': { + category: 'postgresql', + description: 'Character count of the internal query (if any). ', + name: 'postgresql.log.internal_query_pos', + type: 'long', + }, + 'postgresql.log.context': { + category: 'postgresql', + description: 'Error context. ', + name: 'postgresql.log.context', + }, + 'postgresql.log.query_pos': { + category: 'postgresql', + description: 'Character count of the error position (if any). ', + name: 'postgresql.log.query_pos', + type: 'long', + }, + 'postgresql.log.location': { + category: 'postgresql', + description: + 'Location of the error in the PostgreSQL source code (if log_error_verbosity is set to verbose). ', + name: 'postgresql.log.location', + }, + 'postgresql.log.application_name': { + category: 'postgresql', + description: 'Name of the application of this event. It is defined by the client. ', + name: 'postgresql.log.application_name', + }, + 'postgresql.log.backend_type': { + category: 'postgresql', + description: + 'Type of backend of this event. Possible types are autovacuum launcher, autovacuum worker, logical replication launcher, logical replication worker, parallel worker, background writer, client backend, checkpointer, startup, walreceiver, walsender and walwriter. In addition, background workers registered by extensions may have additional types. ', + example: 'client backend', + name: 'postgresql.log.backend_type', + }, + 'postgresql.log.error.code': { + category: 'postgresql', + description: + 'Error code returned by Postgres (if any). Deprecated: errors can have letters. Use sql_state_code instead. ', + name: 'postgresql.log.error.code', type: 'alias', }, - 'postgresql.log.thread_id': { + 'postgresql.log.timezone': { category: 'postgresql', - name: 'postgresql.log.thread_id', + name: 'postgresql.log.timezone', type: 'alias', }, 'postgresql.log.user': { @@ -8891,6 +10366,9 @@ export const fieldsBeat: BeatFields = { }, 'postgresql.log.level': { category: 'postgresql', + description: + 'Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL, and PANIC. ', + example: 'LOG', name: 'postgresql.log.level', type: 'alias', }, @@ -9537,6 +11015,13 @@ export const fieldsBeat: BeatFields = { name: 'aws.cloudtrail.vpc_endpoint_id', type: 'keyword', }, + 'aws.cloudtrail.event_category': { + category: 'aws', + description: + 'Shows the event category that is used in LookupEvents calls. - For management events, the value is management. - For data events, the value is data. - For Insights events, the value is insight.', + name: 'aws.cloudtrail.event_category', + type: 'keyword', + }, 'aws.cloudtrail.console_login.additional_eventdata.mobile_version': { category: 'aws', description: 'Identifies whether ConsoleLogin was from mobile version', @@ -9580,6 +11065,86 @@ export const fieldsBeat: BeatFields = { name: 'aws.cloudtrail.flattened.service_event_details', type: 'flattened', }, + 'aws.cloudtrail.digest.log_files': { + category: 'aws', + description: 'A list of Logfiles contained in the digest.', + name: 'aws.cloudtrail.digest.log_files', + type: 'nested', + }, + 'aws.cloudtrail.digest.start_time': { + category: 'aws', + description: + 'The starting UTC time range that the digest file covers, taking as a reference the time in which log files have been delivered by CloudTrail.', + name: 'aws.cloudtrail.digest.start_time', + type: 'date', + }, + 'aws.cloudtrail.digest.end_time': { + category: 'aws', + description: + 'The ending UTC time range that the digest file covers, taking as a reference the time in which log files have been delivered by CloudTrail.', + name: 'aws.cloudtrail.digest.end_time', + type: 'date', + }, + 'aws.cloudtrail.digest.s3_bucket': { + category: 'aws', + description: + 'The name of the Amazon S3 bucket to which the current digest file has been delivered.', + name: 'aws.cloudtrail.digest.s3_bucket', + type: 'keyword', + }, + 'aws.cloudtrail.digest.s3_object': { + category: 'aws', + description: + 'The Amazon S3 object key (that is, the Amazon S3 bucket location) of the current digest file.', + name: 'aws.cloudtrail.digest.s3_object', + type: 'keyword', + }, + 'aws.cloudtrail.digest.newest_event_time': { + category: 'aws', + description: + 'The UTC time of the most recent event among all of the events in the log files in the digest.', + name: 'aws.cloudtrail.digest.newest_event_time', + type: 'date', + }, + 'aws.cloudtrail.digest.oldest_event_time': { + category: 'aws', + description: + 'The UTC time of the oldest event among all of the events in the log files in the digest.', + name: 'aws.cloudtrail.digest.oldest_event_time', + type: 'date', + }, + 'aws.cloudtrail.digest.previous_s3_bucket': { + category: 'aws', + description: 'The Amazon S3 bucket to which the previous digest file was delivered.', + name: 'aws.cloudtrail.digest.previous_s3_bucket', + type: 'keyword', + }, + 'aws.cloudtrail.digest.previous_hash_algorithm': { + category: 'aws', + description: 'The name of the hash algorithm that was used to hash the previous digest file.', + name: 'aws.cloudtrail.digest.previous_hash_algorithm', + type: 'keyword', + }, + 'aws.cloudtrail.digest.public_key_fingerprint': { + category: 'aws', + description: + 'The hexadecimal encoded fingerprint of the public key that matches the private key used to sign this digest file.', + name: 'aws.cloudtrail.digest.public_key_fingerprint', + type: 'keyword', + }, + 'aws.cloudtrail.digest.signature_algorithm': { + category: 'aws', + description: 'The algorithm used to sign the digest file.', + name: 'aws.cloudtrail.digest.signature_algorithm', + type: 'keyword', + }, + 'aws.cloudtrail.insight_details': { + category: 'aws', + description: + 'Shows information about the underlying triggers of an Insights event, such as event source, user agent, statistics, API name, and whether the event is the start or end of the Insights event.', + name: 'aws.cloudtrail.insight_details', + type: 'flattened', + }, 'aws.cloudwatch.message': { category: 'aws', description: 'CloudWatch log message. ', @@ -9746,6 +11311,30 @@ export const fieldsBeat: BeatFields = { name: 'aws.elb.error.reason', type: 'keyword', }, + 'aws.elb.target_port': { + category: 'aws', + description: 'List of IP addresses and ports for the targets that processed this request. ', + name: 'aws.elb.target_port', + type: 'keyword', + }, + 'aws.elb.target_status_code': { + category: 'aws', + description: 'List of status codes from the responses of the targets. ', + name: 'aws.elb.target_status_code', + type: 'keyword', + }, + 'aws.elb.classification': { + category: 'aws', + description: 'The classification for desync mitigation. ', + name: 'aws.elb.classification', + type: 'keyword', + }, + 'aws.elb.classification_reason': { + category: 'aws', + description: 'The classification reason code. ', + name: 'aws.elb.classification_reason', + type: 'keyword', + }, 'aws.s3access.bucket_owner': { category: 'aws', description: 'The canonical user ID of the owner of the source bucket. ', @@ -9962,6 +11551,12 @@ export const fieldsBeat: BeatFields = { name: 'aws.vpcflow.tcp_flags', type: 'keyword', }, + 'aws.vpcflow.tcp_flags_array': { + category: 'aws', + description: "List of TCP flags: 'fin, syn, rst, psh, ack, urg' ", + name: 'aws.vpcflow.tcp_flags_array', + type: 'keyword', + }, 'aws.vpcflow.type': { category: 'aws', description: 'The type of traffic: IPv4, IPv6, or EFA. ', @@ -10334,6 +11929,90 @@ export const fieldsBeat: BeatFields = { name: 'azure.auditlogs.properties.initiated_by.user.ipAddress', type: 'keyword', }, + 'azure.platformlogs.operation_name': { + category: 'azure', + description: 'Operation name ', + name: 'azure.platformlogs.operation_name', + type: 'keyword', + }, + 'azure.platformlogs.result_type': { + category: 'azure', + description: 'Result type ', + name: 'azure.platformlogs.result_type', + type: 'keyword', + }, + 'azure.platformlogs.result_signature': { + category: 'azure', + description: 'Result signature ', + name: 'azure.platformlogs.result_signature', + type: 'keyword', + }, + 'azure.platformlogs.category': { + category: 'azure', + description: 'Category ', + name: 'azure.platformlogs.category', + type: 'keyword', + }, + 'azure.platformlogs.event_category': { + category: 'azure', + description: 'Event Category ', + name: 'azure.platformlogs.event_category', + type: 'keyword', + }, + 'azure.platformlogs.status': { + category: 'azure', + description: 'Status ', + name: 'azure.platformlogs.status', + type: 'keyword', + }, + 'azure.platformlogs.ccpNamespace': { + category: 'azure', + description: 'ccpNamespace ', + name: 'azure.platformlogs.ccpNamespace', + type: 'keyword', + }, + 'azure.platformlogs.Cloud': { + category: 'azure', + description: 'Cloud ', + name: 'azure.platformlogs.Cloud', + type: 'keyword', + }, + 'azure.platformlogs.Environment': { + category: 'azure', + description: 'Environment ', + name: 'azure.platformlogs.Environment', + type: 'keyword', + }, + 'azure.platformlogs.EventTimeString': { + category: 'azure', + description: 'EventTimeString ', + name: 'azure.platformlogs.EventTimeString', + type: 'keyword', + }, + 'azure.platformlogs.Caller': { + category: 'azure', + description: 'Caller ', + name: 'azure.platformlogs.Caller', + type: 'keyword', + }, + 'azure.platformlogs.ScaleUnit': { + category: 'azure', + description: 'ScaleUnit ', + name: 'azure.platformlogs.ScaleUnit', + type: 'keyword', + }, + 'azure.platformlogs.ActivityId': { + category: 'azure', + description: 'ActivityId ', + name: 'azure.platformlogs.ActivityId', + type: 'keyword', + }, + 'azure.platformlogs.properties.*': { + category: 'azure', + description: 'Properties ', + name: 'azure.platformlogs.properties.*', + type: 'object', + }, 'azure.signinlogs.operation_name': { category: 'azure', description: 'The operation name ', @@ -17047,6 +18726,331 @@ export const fieldsBeat: BeatFields = { name: 'checkpoint.trusted_domain', type: 'keyword', }, + 'cisco.amp.timestamp_nanoseconds': { + category: 'cisco', + description: 'The timestamp in Epoch nanoseconds. ', + name: 'cisco.amp.timestamp_nanoseconds', + type: 'date', + }, + 'cisco.amp.event_type_id': { + category: 'cisco', + description: 'A sub ID of the event, depending on event type. ', + name: 'cisco.amp.event_type_id', + type: 'keyword', + }, + 'cisco.amp.detection': { + category: 'cisco', + description: 'The name of the malware detected. ', + name: 'cisco.amp.detection', + type: 'keyword', + }, + 'cisco.amp.detection_id': { + category: 'cisco', + description: 'The ID of the detection. ', + name: 'cisco.amp.detection_id', + type: 'keyword', + }, + 'cisco.amp.connector_guid': { + category: 'cisco', + description: 'The GUID of the connector sending information to AMP. ', + name: 'cisco.amp.connector_guid', + type: 'keyword', + }, + 'cisco.amp.group_guids': { + category: 'cisco', + description: 'An array of group GUIDS related to the connector sending information to AMP. ', + name: 'cisco.amp.group_guids', + type: 'keyword', + }, + 'cisco.amp.vulnerabilities': { + category: 'cisco', + description: 'An array of related vulnerabilities to the malicious event. ', + name: 'cisco.amp.vulnerabilities', + type: 'flattened', + }, + 'cisco.amp.scan.description': { + category: 'cisco', + description: + 'Description of an event related to a scan being initiated, for example the specific directory name. ', + name: 'cisco.amp.scan.description', + type: 'keyword', + }, + 'cisco.amp.scan.clean': { + category: 'cisco', + description: 'Boolean value if a scanned file was clean or not. ', + name: 'cisco.amp.scan.clean', + type: 'boolean', + }, + 'cisco.amp.scan.scanned_files': { + category: 'cisco', + description: 'Count of files scanned in a directory. ', + name: 'cisco.amp.scan.scanned_files', + type: 'long', + }, + 'cisco.amp.scan.scanned_processes': { + category: 'cisco', + description: 'Count of processes scanned related to a single scan event. ', + name: 'cisco.amp.scan.scanned_processes', + type: 'long', + }, + 'cisco.amp.scan.scanned_paths': { + category: 'cisco', + description: 'Count of different directories scanned related to a single scan event. ', + name: 'cisco.amp.scan.scanned_paths', + type: 'long', + }, + 'cisco.amp.scan.malicious_detections': { + category: 'cisco', + description: 'Count of malicious files or documents detected related to a single scan event. ', + name: 'cisco.amp.scan.malicious_detections', + type: 'long', + }, + 'cisco.amp.computer.connector_guid': { + category: 'cisco', + description: + 'The GUID of the connector, similar to top level connector_guid, but unique if multiple connectors are involved. ', + name: 'cisco.amp.computer.connector_guid', + type: 'keyword', + }, + 'cisco.amp.computer.external_ip': { + category: 'cisco', + description: 'The external IP of the related host. ', + name: 'cisco.amp.computer.external_ip', + type: 'ip', + }, + 'cisco.amp.computer.active': { + category: 'cisco', + description: 'If the current endpoint is active or not. ', + name: 'cisco.amp.computer.active', + type: 'boolean', + }, + 'cisco.amp.computer.network_addresses': { + category: 'cisco', + description: 'All network interface information on the related host. ', + name: 'cisco.amp.computer.network_addresses', + type: 'flattened', + }, + 'cisco.amp.file.disposition': { + category: 'cisco', + description: 'Categorization of file, for example "Malicious" or "Clean". ', + name: 'cisco.amp.file.disposition', + type: 'keyword', + }, + 'cisco.amp.network_info.disposition': { + category: 'cisco', + description: + 'Categorization of a network event related to a file, for example "Malicious" or "Clean". ', + name: 'cisco.amp.network_info.disposition', + type: 'keyword', + }, + 'cisco.amp.network_info.nfm.direction': { + category: 'cisco', + description: 'The current direction based on source and destination IP. ', + name: 'cisco.amp.network_info.nfm.direction', + type: 'keyword', + }, + 'cisco.amp.related.mac': { + category: 'cisco', + description: 'An array of all related MAC addresses. ', + name: 'cisco.amp.related.mac', + type: 'keyword', + }, + 'cisco.amp.related.cve': { + category: 'cisco', + description: 'An array of all related MAC addresses. ', + name: 'cisco.amp.related.cve', + type: 'keyword', + }, + 'cisco.amp.cloud_ioc.description': { + category: 'cisco', + description: 'Description of the related IOC for specific IOC events from AMP. ', + name: 'cisco.amp.cloud_ioc.description', + type: 'keyword', + }, + 'cisco.amp.cloud_ioc.short_description': { + category: 'cisco', + description: 'Short description of the related IOC for specific IOC events from AMP. ', + name: 'cisco.amp.cloud_ioc.short_description', + type: 'keyword', + }, + 'cisco.amp.network_info.parent.disposition': { + category: 'cisco', + description: 'Categorization of a IOC for example "Malicious" or "Clean". ', + name: 'cisco.amp.network_info.parent.disposition', + type: 'keyword', + }, + 'cisco.amp.network_info.parent.identity.md5': { + category: 'cisco', + description: 'MD5 hash of the related IOC. ', + name: 'cisco.amp.network_info.parent.identity.md5', + type: 'keyword', + }, + 'cisco.amp.network_info.parent.identity.sha1': { + category: 'cisco', + description: 'SHA1 hash of the related IOC. ', + name: 'cisco.amp.network_info.parent.identity.sha1', + type: 'keyword', + }, + 'cisco.amp.network_info.parent.identify.sha256': { + category: 'cisco', + description: 'SHA256 hash of the related IOC. ', + name: 'cisco.amp.network_info.parent.identify.sha256', + type: 'keyword', + }, + 'cisco.amp.file.archived_file.disposition': { + category: 'cisco', + description: + 'Categorization of a file archive related to a file, for example "Malicious" or "Clean". ', + name: 'cisco.amp.file.archived_file.disposition', + type: 'keyword', + }, + 'cisco.amp.file.archived_file.identity.md5': { + category: 'cisco', + description: 'MD5 hash of the archived file related to the malicious event. ', + name: 'cisco.amp.file.archived_file.identity.md5', + type: 'keyword', + }, + 'cisco.amp.file.archived_file.identity.sha1': { + category: 'cisco', + description: 'SHA1 hash of the archived file related to the malicious event. ', + name: 'cisco.amp.file.archived_file.identity.sha1', + type: 'keyword', + }, + 'cisco.amp.file.archived_file.identify.sha256': { + category: 'cisco', + description: 'SHA256 hash of the archived file related to the malicious event. ', + name: 'cisco.amp.file.archived_file.identify.sha256', + type: 'keyword', + }, + 'cisco.amp.file.attack_details.application': { + category: 'cisco', + description: 'The application name related to Exploit Prevention events. ', + name: 'cisco.amp.file.attack_details.application', + type: 'keyword', + }, + 'cisco.amp.file.attack_details.attacked_module': { + category: 'cisco', + description: + 'Path to the executable or dll that was attacked and detected by Exploit Prevention. ', + name: 'cisco.amp.file.attack_details.attacked_module', + type: 'keyword', + }, + 'cisco.amp.file.attack_details.base_address': { + category: 'cisco', + description: 'The base memory address related to the exploit detected. ', + name: 'cisco.amp.file.attack_details.base_address', + type: 'keyword', + }, + 'cisco.amp.file.attack_details.suspicious_files': { + category: 'cisco', + description: 'An array of related files when an attack is detected by Exploit Prevention. ', + name: 'cisco.amp.file.attack_details.suspicious_files', + type: 'keyword', + }, + 'cisco.amp.file.parent.disposition': { + category: 'cisco', + description: 'Categorization of parrent, for example "Malicious" or "Clean". ', + name: 'cisco.amp.file.parent.disposition', + type: 'keyword', + }, + 'cisco.amp.error.description': { + category: 'cisco', + description: 'Description of an endpoint error event. ', + name: 'cisco.amp.error.description', + type: 'keyword', + }, + 'cisco.amp.error.error_code': { + category: 'cisco', + description: 'The error code describing the related error event. ', + name: 'cisco.amp.error.error_code', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.severity': { + category: 'cisco', + description: + 'Severity result of the threat hunt registered to the malicious event. Can be Low-Critical. ', + name: 'cisco.amp.threat_hunting.severity', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_report_guid': { + category: 'cisco', + description: 'The GUID of the related threat hunting report. ', + name: 'cisco.amp.threat_hunting.incident_report_guid', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_hunt_guid': { + category: 'cisco', + description: 'The GUID of the related investigation tracking issue. ', + name: 'cisco.amp.threat_hunting.incident_hunt_guid', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_title': { + category: 'cisco', + description: 'Title of the incident related to the threat hunting activity. ', + name: 'cisco.amp.threat_hunting.incident_title', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_summary': { + category: 'cisco', + description: 'Summary of the outcome on the threat hunting activity. ', + name: 'cisco.amp.threat_hunting.incident_summary', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_remediation': { + category: 'cisco', + description: 'Recommendations to resolve the vulnerability or exploited host. ', + name: 'cisco.amp.threat_hunting.incident_remediation', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_id': { + category: 'cisco', + description: 'The id of the related incident for the threat hunting activity. ', + name: 'cisco.amp.threat_hunting.incident_id', + type: 'keyword', + }, + 'cisco.amp.threat_hunting.incident_end_time': { + category: 'cisco', + description: 'When the threat hunt finalized or closed. ', + name: 'cisco.amp.threat_hunting.incident_end_time', + type: 'date', + }, + 'cisco.amp.threat_hunting.incident_start_time': { + category: 'cisco', + description: 'When the threat hunt was initiated. ', + name: 'cisco.amp.threat_hunting.incident_start_time', + type: 'date', + }, + 'cisco.amp.file.attack_details.indicators': { + category: 'cisco', + description: + 'Different indicator types that matches the exploit detected, for example different MITRE tactics. ', + name: 'cisco.amp.file.attack_details.indicators', + type: 'flattened', + }, + 'cisco.amp.threat_hunting.tactics': { + category: 'cisco', + description: 'List of all MITRE tactics related to the incident found. ', + name: 'cisco.amp.threat_hunting.tactics', + type: 'flattened', + }, + 'cisco.amp.threat_hunting.techniques': { + category: 'cisco', + description: 'List of all MITRE techniques related to the incident found. ', + name: 'cisco.amp.threat_hunting.techniques', + type: 'flattened', + }, + 'cisco.amp.tactics': { + category: 'cisco', + description: 'List of all MITRE tactics related to the incident found. ', + name: 'cisco.amp.tactics', + type: 'flattened', + }, + 'cisco.amp.techniques': { + category: 'cisco', + description: 'List of all MITRE techniques related to the incident found. ', + name: 'cisco.amp.techniques', + type: 'flattened', + }, 'cisco.asa.message_id': { category: 'cisco', description: 'The Cisco ASA message identifier. ', @@ -17170,6 +19174,72 @@ export const fieldsBeat: BeatFields = { name: 'cisco.asa.dap_records', type: 'keyword', }, + 'cisco.asa.command_line_arguments': { + category: 'cisco', + description: 'The command line arguments logged by the local audit log ', + name: 'cisco.asa.command_line_arguments', + type: 'keyword', + }, + 'cisco.asa.assigned_ip': { + category: 'cisco', + description: 'The IP address assigned to a VPN client successfully connecting ', + name: 'cisco.asa.assigned_ip', + type: 'ip', + }, + 'cisco.asa.privilege.old': { + category: 'cisco', + description: 'When a users privilege is changed this is the old value ', + name: 'cisco.asa.privilege.old', + type: 'keyword', + }, + 'cisco.asa.privilege.new': { + category: 'cisco', + description: 'When a users privilege is changed this is the new value ', + name: 'cisco.asa.privilege.new', + type: 'keyword', + }, + 'cisco.asa.burst.object': { + category: 'cisco', + description: 'The related object for burst warnings ', + name: 'cisco.asa.burst.object', + type: 'keyword', + }, + 'cisco.asa.burst.id': { + category: 'cisco', + description: 'The related rate ID for burst warnings ', + name: 'cisco.asa.burst.id', + type: 'keyword', + }, + 'cisco.asa.burst.current_rate': { + category: 'cisco', + description: 'The current burst rate seen ', + name: 'cisco.asa.burst.current_rate', + type: 'keyword', + }, + 'cisco.asa.burst.configured_rate': { + category: 'cisco', + description: 'The current configured burst rate ', + name: 'cisco.asa.burst.configured_rate', + type: 'keyword', + }, + 'cisco.asa.burst.avg_rate': { + category: 'cisco', + description: 'The current average burst rate seen ', + name: 'cisco.asa.burst.avg_rate', + type: 'keyword', + }, + 'cisco.asa.burst.configured_avg_rate': { + category: 'cisco', + description: 'The current configured average burst rate allowed ', + name: 'cisco.asa.burst.configured_avg_rate', + type: 'keyword', + }, + 'cisco.asa.burst.cumulative_count': { + category: 'cisco', + description: 'The total count of burst rate hits since the object was created or cleared ', + name: 'cisco.asa.burst.cumulative_count', + type: 'keyword', + }, 'cisco.ftd.message_id': { category: 'cisco', description: 'The Cisco FTD message identifier. ', @@ -17313,6 +19383,96 @@ export const fieldsBeat: BeatFields = { name: 'cisco.ios.facility', type: 'keyword', }, + 'cisco.umbrella.identities': { + category: 'cisco', + description: 'An array of the different identities related to the event. ', + name: 'cisco.umbrella.identities', + type: 'keyword', + }, + 'cisco.umbrella.categories': { + category: 'cisco', + description: 'The security or content categories that the destination matches. ', + name: 'cisco.umbrella.categories', + type: 'keyword', + }, + 'cisco.umbrella.policy_identity_type': { + category: 'cisco', + description: + 'The first identity type matched with this request. Available in version 3 and above. ', + name: 'cisco.umbrella.policy_identity_type', + type: 'keyword', + }, + 'cisco.umbrella.identity_types': { + category: 'cisco', + description: + 'The type of identity that made the request. For example, Roaming Computer or Network. ', + name: 'cisco.umbrella.identity_types', + type: 'keyword', + }, + 'cisco.umbrella.blocked_categories': { + category: 'cisco', + description: + 'The categories that resulted in the destination being blocked. Available in version 4 and above. ', + name: 'cisco.umbrella.blocked_categories', + type: 'keyword', + }, + 'cisco.umbrella.content_type': { + category: 'cisco', + description: 'The type of web content, typically text/html. ', + name: 'cisco.umbrella.content_type', + type: 'keyword', + }, + 'cisco.umbrella.sha_sha256': { + category: 'cisco', + description: 'Hex digest of the response content. ', + name: 'cisco.umbrella.sha_sha256', + type: 'keyword', + }, + 'cisco.umbrella.av_detections': { + category: 'cisco', + description: 'The detection name according to the antivirus engine used in file inspection. ', + name: 'cisco.umbrella.av_detections', + type: 'keyword', + }, + 'cisco.umbrella.puas': { + category: 'cisco', + description: + 'A list of all potentially unwanted application (PUA) results for the proxied file as returned by the antivirus scanner. ', + name: 'cisco.umbrella.puas', + type: 'keyword', + }, + 'cisco.umbrella.amp_disposition': { + category: 'cisco', + description: + 'The status of the files proxied and scanned by Cisco Advanced Malware Protection (AMP) as part of the Umbrella File Inspection feature; can be Clean, Malicious or Unknown. ', + name: 'cisco.umbrella.amp_disposition', + type: 'keyword', + }, + 'cisco.umbrella.amp_malware_name': { + category: 'cisco', + description: 'If Malicious, the name of the malware according to AMP. ', + name: 'cisco.umbrella.amp_malware_name', + type: 'keyword', + }, + 'cisco.umbrella.amp_score': { + category: 'cisco', + description: + 'The score of the malware from AMP. This field is not currently used and will be blank. ', + name: 'cisco.umbrella.amp_score', + type: 'keyword', + }, + 'cisco.umbrella.datacenter': { + category: 'cisco', + description: 'The name of the Umbrella Data Center that processed the user-generated traffic. ', + name: 'cisco.umbrella.datacenter', + type: 'keyword', + }, + 'cisco.umbrella.origin_id': { + category: 'cisco', + description: 'The unique identity of the network tunnel. ', + name: 'cisco.umbrella.origin_id', + type: 'keyword', + }, 'coredns.id': { category: 'coredns', description: 'id of the DNS transaction ', @@ -19284,7 +21444,7 @@ export const fieldsBeat: BeatFields = { category: 'fortinet', description: 'Memory usage system statistics ', name: 'fortinet.firewall.mem', - type: 'keyword', + type: 'integer', }, 'fortinet.firewall.meshmode': { category: 'fortinet', @@ -20504,341 +22664,1174 @@ export const fieldsBeat: BeatFields = { name: 'fortinet.firewall.weakwepiv', type: 'keyword', }, - 'fortinet.firewall.xauthgroup': { - category: 'fortinet', - description: 'XAuth Group Name ', - name: 'fortinet.firewall.xauthgroup', + 'fortinet.firewall.xauthgroup': { + category: 'fortinet', + description: 'XAuth Group Name ', + name: 'fortinet.firewall.xauthgroup', + type: 'keyword', + }, + 'fortinet.firewall.xauthuser': { + category: 'fortinet', + description: 'XAuth User Name ', + name: 'fortinet.firewall.xauthuser', + type: 'keyword', + }, + 'fortinet.firewall.xid': { + category: 'fortinet', + description: 'Wireless X ID ', + name: 'fortinet.firewall.xid', + type: 'integer', + }, + 'googlecloud.destination.instance.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.destination.instance.project_id', + type: 'keyword', + }, + 'googlecloud.destination.instance.region': { + category: 'googlecloud', + description: 'Region of the VM. ', + name: 'googlecloud.destination.instance.region', + type: 'keyword', + }, + 'googlecloud.destination.instance.zone': { + category: 'googlecloud', + description: 'Zone of the VM. ', + name: 'googlecloud.destination.instance.zone', + type: 'keyword', + }, + 'googlecloud.destination.vpc.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.destination.vpc.project_id', + type: 'keyword', + }, + 'googlecloud.destination.vpc.vpc_name': { + category: 'googlecloud', + description: 'VPC on which the VM is operating. ', + name: 'googlecloud.destination.vpc.vpc_name', + type: 'keyword', + }, + 'googlecloud.destination.vpc.subnetwork_name': { + category: 'googlecloud', + description: 'Subnetwork on which the VM is operating. ', + name: 'googlecloud.destination.vpc.subnetwork_name', + type: 'keyword', + }, + 'googlecloud.source.instance.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.source.instance.project_id', + type: 'keyword', + }, + 'googlecloud.source.instance.region': { + category: 'googlecloud', + description: 'Region of the VM. ', + name: 'googlecloud.source.instance.region', + type: 'keyword', + }, + 'googlecloud.source.instance.zone': { + category: 'googlecloud', + description: 'Zone of the VM. ', + name: 'googlecloud.source.instance.zone', + type: 'keyword', + }, + 'googlecloud.source.vpc.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.source.vpc.project_id', + type: 'keyword', + }, + 'googlecloud.source.vpc.vpc_name': { + category: 'googlecloud', + description: 'VPC on which the VM is operating. ', + name: 'googlecloud.source.vpc.vpc_name', + type: 'keyword', + }, + 'googlecloud.source.vpc.subnetwork_name': { + category: 'googlecloud', + description: 'Subnetwork on which the VM is operating. ', + name: 'googlecloud.source.vpc.subnetwork_name', + type: 'keyword', + }, + 'googlecloud.audit.type': { + category: 'googlecloud', + description: 'Type property. ', + name: 'googlecloud.audit.type', + type: 'keyword', + }, + 'googlecloud.audit.authentication_info.principal_email': { + category: 'googlecloud', + description: 'The email address of the authenticated user making the request. ', + name: 'googlecloud.audit.authentication_info.principal_email', + type: 'keyword', + }, + 'googlecloud.audit.authentication_info.authority_selector': { + category: 'googlecloud', + description: + 'The authority selector specified by the requestor, if any. It is not guaranteed that the principal was allowed to use this authority. ', + name: 'googlecloud.audit.authentication_info.authority_selector', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.permission': { + category: 'googlecloud', + description: 'The required IAM permission. ', + name: 'googlecloud.audit.authorization_info.permission', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.granted': { + category: 'googlecloud', + description: 'Whether or not authorization for resource and permission was granted. ', + name: 'googlecloud.audit.authorization_info.granted', + type: 'boolean', + }, + 'googlecloud.audit.authorization_info.resource_attributes.service': { + category: 'googlecloud', + description: 'The name of the service. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.service', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.resource_attributes.name': { + category: 'googlecloud', + description: 'The name of the resource. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.name', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.resource_attributes.type': { + category: 'googlecloud', + description: 'The type of the resource. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.type', + type: 'keyword', + }, + 'googlecloud.audit.method_name': { + category: 'googlecloud', + description: + "The name of the service method or operation. For API calls, this should be the name of the API method. For example, 'google.datastore.v1.Datastore.RunQuery'. ", + name: 'googlecloud.audit.method_name', + type: 'keyword', + }, + 'googlecloud.audit.num_response_items': { + category: 'googlecloud', + description: 'The number of items returned from a List or Query API method, if applicable. ', + name: 'googlecloud.audit.num_response_items', + type: 'long', + }, + 'googlecloud.audit.request.proto_name': { + category: 'googlecloud', + description: 'Type property of the request. ', + name: 'googlecloud.audit.request.proto_name', + type: 'keyword', + }, + 'googlecloud.audit.request.filter': { + category: 'googlecloud', + description: 'Filter of the request. ', + name: 'googlecloud.audit.request.filter', + type: 'keyword', + }, + 'googlecloud.audit.request.name': { + category: 'googlecloud', + description: 'Name of the request. ', + name: 'googlecloud.audit.request.name', + type: 'keyword', + }, + 'googlecloud.audit.request.resource_name': { + category: 'googlecloud', + description: 'Name of the request resource. ', + name: 'googlecloud.audit.request.resource_name', + type: 'keyword', + }, + 'googlecloud.audit.request_metadata.caller_ip': { + category: 'googlecloud', + description: 'The IP address of the caller. ', + name: 'googlecloud.audit.request_metadata.caller_ip', + type: 'ip', + }, + 'googlecloud.audit.request_metadata.caller_supplied_user_agent': { + category: 'googlecloud', + description: + 'The user agent of the caller. This information is not authenticated and should be treated accordingly. ', + name: 'googlecloud.audit.request_metadata.caller_supplied_user_agent', + type: 'keyword', + }, + 'googlecloud.audit.response.proto_name': { + category: 'googlecloud', + description: 'Type property of the response. ', + name: 'googlecloud.audit.response.proto_name', + type: 'keyword', + }, + 'googlecloud.audit.response.details.group': { + category: 'googlecloud', + description: 'The name of the group. ', + name: 'googlecloud.audit.response.details.group', + type: 'keyword', + }, + 'googlecloud.audit.response.details.kind': { + category: 'googlecloud', + description: 'The kind of the response details. ', + name: 'googlecloud.audit.response.details.kind', + type: 'keyword', + }, + 'googlecloud.audit.response.details.name': { + category: 'googlecloud', + description: 'The name of the response details. ', + name: 'googlecloud.audit.response.details.name', + type: 'keyword', + }, + 'googlecloud.audit.response.details.uid': { + category: 'googlecloud', + description: 'The uid of the response details. ', + name: 'googlecloud.audit.response.details.uid', + type: 'keyword', + }, + 'googlecloud.audit.response.status': { + category: 'googlecloud', + description: 'Status of the response. ', + name: 'googlecloud.audit.response.status', + type: 'keyword', + }, + 'googlecloud.audit.resource_name': { + category: 'googlecloud', + description: + "The resource or collection that is the target of the operation. The name is a scheme-less URI, not including the API service name. For example, 'shelves/SHELF_ID/books'. ", + name: 'googlecloud.audit.resource_name', + type: 'keyword', + }, + 'googlecloud.audit.resource_location.current_locations': { + category: 'googlecloud', + description: 'Current locations of the resource. ', + name: 'googlecloud.audit.resource_location.current_locations', + type: 'keyword', + }, + 'googlecloud.audit.service_name': { + category: 'googlecloud', + description: + 'The name of the API service performing the operation. For example, datastore.googleapis.com. ', + name: 'googlecloud.audit.service_name', + type: 'keyword', + }, + 'googlecloud.audit.status.code': { + category: 'googlecloud', + description: 'The status code, which should be an enum value of google.rpc.Code. ', + name: 'googlecloud.audit.status.code', + type: 'integer', + }, + 'googlecloud.audit.status.message': { + category: 'googlecloud', + description: + 'A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client. ', + name: 'googlecloud.audit.status.message', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.priority': { + category: 'googlecloud', + description: 'The priority for the firewall rule.', + name: 'googlecloud.firewall.rule_details.priority', + type: 'long', + }, + 'googlecloud.firewall.rule_details.action': { + category: 'googlecloud', + description: 'Action that the rule performs on match.', + name: 'googlecloud.firewall.rule_details.action', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.direction': { + category: 'googlecloud', + description: 'Direction of traffic that matches this rule.', + name: 'googlecloud.firewall.rule_details.direction', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.reference': { + category: 'googlecloud', + description: 'Reference to the firewall rule.', + name: 'googlecloud.firewall.rule_details.reference', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.source_range': { + category: 'googlecloud', + description: 'List of source ranges that the firewall rule applies to.', + name: 'googlecloud.firewall.rule_details.source_range', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.destination_range': { + category: 'googlecloud', + description: 'List of destination ranges that the firewall applies to.', + name: 'googlecloud.firewall.rule_details.destination_range', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.source_tag': { + category: 'googlecloud', + description: 'List of all the source tags that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.source_tag', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.target_tag': { + category: 'googlecloud', + description: 'List of all the target tags that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.target_tag', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.ip_port_info': { + category: 'googlecloud', + description: 'List of ip protocols and applicable port ranges for rules. ', + name: 'googlecloud.firewall.rule_details.ip_port_info', + type: 'array', + }, + 'googlecloud.firewall.rule_details.source_service_account': { + category: 'googlecloud', + description: 'List of all the source service accounts that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.source_service_account', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.target_service_account': { + category: 'googlecloud', + description: 'List of all the target service accounts that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.target_service_account', + type: 'keyword', + }, + 'googlecloud.vpcflow.reporter': { + category: 'googlecloud', + description: "The side which reported the flow. Can be either 'SRC' or 'DEST'. ", + name: 'googlecloud.vpcflow.reporter', + type: 'keyword', + }, + 'googlecloud.vpcflow.rtt.ms': { + category: 'googlecloud', + description: + 'Latency as measured (for TCP flows only) during the time interval. This is the time elapsed between sending a SEQ and receiving a corresponding ACK and it contains the network RTT as well as the application related delay. ', + name: 'googlecloud.vpcflow.rtt.ms', + type: 'long', + }, + 'google_workspace.actor.type': { + category: 'google_workspace', + description: + 'The type of actor. Values can be: *USER*: Another user in the same domain. *EXTERNAL_USER*: A user outside the domain. *KEY*: A non-human actor. ', + name: 'google_workspace.actor.type', + type: 'keyword', + }, + 'google_workspace.actor.key': { + category: 'google_workspace', + description: + 'Only present when `actor.type` is `KEY`. Can be the `consumer_key` of the requestor for OAuth 2LO API requests or an identifier for robot accounts. ', + name: 'google_workspace.actor.key', + type: 'keyword', + }, + 'google_workspace.event.type': { + category: 'google_workspace', + description: + 'The type of Google Workspace event, mapped from `items[].events[].type` in the original payload. Each fileset can have a different set of values for it, more details can be found at https://developers.google.com/admin-sdk/reports/v1/reference/activities/list ', + example: 'audit#activity', + name: 'google_workspace.event.type', + type: 'keyword', + }, + 'google_workspace.kind': { + category: 'google_workspace', + description: + 'The type of API resource, mapped from `kind` in the original payload. More details can be found at https://developers.google.com/admin-sdk/reports/v1/reference/activities/list ', + example: 'audit#activity', + name: 'google_workspace.kind', + type: 'keyword', + }, + 'google_workspace.organization.domain': { + category: 'google_workspace', + description: "The domain that is affected by the report's event. ", + name: 'google_workspace.organization.domain', + type: 'keyword', + }, + 'google_workspace.admin.application.edition': { + category: 'google_workspace', + description: 'The Google Workspace edition.', + name: 'google_workspace.admin.application.edition', + type: 'keyword', + }, + 'google_workspace.admin.application.name': { + category: 'google_workspace', + description: "The application's name.", + name: 'google_workspace.admin.application.name', + type: 'keyword', + }, + 'google_workspace.admin.application.enabled': { + category: 'google_workspace', + description: 'The enabled application.', + name: 'google_workspace.admin.application.enabled', + type: 'keyword', + }, + 'google_workspace.admin.application.licences_order_number': { + category: 'google_workspace', + description: 'Order number used to redeem licenses.', + name: 'google_workspace.admin.application.licences_order_number', + type: 'keyword', + }, + 'google_workspace.admin.application.licences_purchased': { + category: 'google_workspace', + description: 'Number of licences purchased.', + name: 'google_workspace.admin.application.licences_purchased', + type: 'keyword', + }, + 'google_workspace.admin.application.id': { + category: 'google_workspace', + description: 'The application ID.', + name: 'google_workspace.admin.application.id', + type: 'keyword', + }, + 'google_workspace.admin.application.asp_id': { + category: 'google_workspace', + description: 'The application specific password ID.', + name: 'google_workspace.admin.application.asp_id', + type: 'keyword', + }, + 'google_workspace.admin.application.package_id': { + category: 'google_workspace', + description: 'The mobile application package ID.', + name: 'google_workspace.admin.application.package_id', + type: 'keyword', + }, + 'google_workspace.admin.group.email': { + category: 'google_workspace', + description: "The group's primary email address.", + name: 'google_workspace.admin.group.email', + type: 'keyword', + }, + 'google_workspace.admin.new_value': { + category: 'google_workspace', + description: 'The new value for the setting.', + name: 'google_workspace.admin.new_value', + type: 'keyword', + }, + 'google_workspace.admin.old_value': { + category: 'google_workspace', + description: 'The old value for the setting.', + name: 'google_workspace.admin.old_value', + type: 'keyword', + }, + 'google_workspace.admin.org_unit.name': { + category: 'google_workspace', + description: 'The organizational unit name.', + name: 'google_workspace.admin.org_unit.name', + type: 'keyword', + }, + 'google_workspace.admin.org_unit.full': { + category: 'google_workspace', + description: 'The org unit full path including the root org unit name.', + name: 'google_workspace.admin.org_unit.full', + type: 'keyword', + }, + 'google_workspace.admin.setting.name': { + category: 'google_workspace', + description: 'The setting name.', + name: 'google_workspace.admin.setting.name', + type: 'keyword', + }, + 'google_workspace.admin.user_defined_setting.name': { + category: 'google_workspace', + description: 'The name of the user-defined setting.', + name: 'google_workspace.admin.user_defined_setting.name', + type: 'keyword', + }, + 'google_workspace.admin.setting.description': { + category: 'google_workspace', + description: 'The setting name.', + name: 'google_workspace.admin.setting.description', + type: 'keyword', + }, + 'google_workspace.admin.group.priorities': { + category: 'google_workspace', + description: 'Group priorities.', + name: 'google_workspace.admin.group.priorities', + type: 'keyword', + }, + 'google_workspace.admin.domain.alias': { + category: 'google_workspace', + description: 'The domain alias.', + name: 'google_workspace.admin.domain.alias', + type: 'keyword', + }, + 'google_workspace.admin.domain.name': { + category: 'google_workspace', + description: 'The primary domain name.', + name: 'google_workspace.admin.domain.name', + type: 'keyword', + }, + 'google_workspace.admin.domain.secondary_name': { + category: 'google_workspace', + description: 'The secondary domain name.', + name: 'google_workspace.admin.domain.secondary_name', + type: 'keyword', + }, + 'google_workspace.admin.managed_configuration': { + category: 'google_workspace', + description: 'The name of the managed configuration.', + name: 'google_workspace.admin.managed_configuration', + type: 'keyword', + }, + 'google_workspace.admin.non_featured_services_selection': { + category: 'google_workspace', + description: + 'Non-featured services selection. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-application-settings#FLASHLIGHT_EDU_NON_FEATURED_SERVICES_SELECTED ', + name: 'google_workspace.admin.non_featured_services_selection', + type: 'keyword', + }, + 'google_workspace.admin.field': { + category: 'google_workspace', + description: 'The name of the field.', + name: 'google_workspace.admin.field', + type: 'keyword', + }, + 'google_workspace.admin.resource.id': { + category: 'google_workspace', + description: 'The name of the resource identifier.', + name: 'google_workspace.admin.resource.id', + type: 'keyword', + }, + 'google_workspace.admin.user.email': { + category: 'google_workspace', + description: "The user's primary email address.", + name: 'google_workspace.admin.user.email', + type: 'keyword', + }, + 'google_workspace.admin.user.nickname': { + category: 'google_workspace', + description: "The user's nickname.", + name: 'google_workspace.admin.user.nickname', + type: 'keyword', + }, + 'google_workspace.admin.user.birthdate': { + category: 'google_workspace', + description: "The user's birth date.", + name: 'google_workspace.admin.user.birthdate', + type: 'date', + }, + 'google_workspace.admin.gateway.name': { + category: 'google_workspace', + description: 'Gateway name. Present on some chat settings.', + name: 'google_workspace.admin.gateway.name', + type: 'keyword', + }, + 'google_workspace.admin.chrome_os.session_type': { + category: 'google_workspace', + description: 'Chrome OS session type.', + name: 'google_workspace.admin.chrome_os.session_type', + type: 'keyword', + }, + 'google_workspace.admin.device.serial_number': { + category: 'google_workspace', + description: 'Device serial number.', + name: 'google_workspace.admin.device.serial_number', + type: 'keyword', + }, + 'google_workspace.admin.device.id': { + category: 'google_workspace', + name: 'google_workspace.admin.device.id', + type: 'keyword', + }, + 'google_workspace.admin.device.type': { + category: 'google_workspace', + description: 'Device type.', + name: 'google_workspace.admin.device.type', + type: 'keyword', + }, + 'google_workspace.admin.print_server.name': { + category: 'google_workspace', + description: 'The name of the print server.', + name: 'google_workspace.admin.print_server.name', + type: 'keyword', + }, + 'google_workspace.admin.printer.name': { + category: 'google_workspace', + description: 'The name of the printer.', + name: 'google_workspace.admin.printer.name', + type: 'keyword', + }, + 'google_workspace.admin.device.command_details': { + category: 'google_workspace', + description: 'Command details.', + name: 'google_workspace.admin.device.command_details', + type: 'keyword', + }, + 'google_workspace.admin.role.id': { + category: 'google_workspace', + description: 'Unique identifier for this role privilege.', + name: 'google_workspace.admin.role.id', + type: 'keyword', + }, + 'google_workspace.admin.role.name': { + category: 'google_workspace', + description: + 'The role name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-delegated-admin-settings ', + name: 'google_workspace.admin.role.name', + type: 'keyword', + }, + 'google_workspace.admin.privilege.name': { + category: 'google_workspace', + description: 'Privilege name.', + name: 'google_workspace.admin.privilege.name', + type: 'keyword', + }, + 'google_workspace.admin.service.name': { + category: 'google_workspace', + description: 'The service name.', + name: 'google_workspace.admin.service.name', + type: 'keyword', + }, + 'google_workspace.admin.url.name': { + category: 'google_workspace', + description: 'The website name.', + name: 'google_workspace.admin.url.name', + type: 'keyword', + }, + 'google_workspace.admin.product.name': { + category: 'google_workspace', + description: 'The product name.', + name: 'google_workspace.admin.product.name', + type: 'keyword', + }, + 'google_workspace.admin.product.sku': { + category: 'google_workspace', + description: 'The product SKU.', + name: 'google_workspace.admin.product.sku', + type: 'keyword', + }, + 'google_workspace.admin.bulk_upload.failed': { + category: 'google_workspace', + description: 'Number of failed records in bulk upload operation.', + name: 'google_workspace.admin.bulk_upload.failed', + type: 'long', + }, + 'google_workspace.admin.bulk_upload.total': { + category: 'google_workspace', + description: 'Number of total records in bulk upload operation.', + name: 'google_workspace.admin.bulk_upload.total', + type: 'long', + }, + 'google_workspace.admin.group.allowed_list': { + category: 'google_workspace', + description: 'Names of allow-listed groups.', + name: 'google_workspace.admin.group.allowed_list', + type: 'keyword', + }, + 'google_workspace.admin.email.quarantine_name': { + category: 'google_workspace', + description: 'The name of the quarantine.', + name: 'google_workspace.admin.email.quarantine_name', + type: 'keyword', + }, + 'google_workspace.admin.email.log_search_filter.message_id': { + category: 'google_workspace', + description: "The log search filter's email message ID.", + name: 'google_workspace.admin.email.log_search_filter.message_id', + type: 'keyword', + }, + 'google_workspace.admin.email.log_search_filter.start_date': { + category: 'google_workspace', + description: "The log search filter's start date.", + name: 'google_workspace.admin.email.log_search_filter.start_date', + type: 'date', + }, + 'google_workspace.admin.email.log_search_filter.end_date': { + category: 'google_workspace', + description: "The log search filter's ending date.", + name: 'google_workspace.admin.email.log_search_filter.end_date', + type: 'date', + }, + 'google_workspace.admin.email.log_search_filter.recipient.value': { + category: 'google_workspace', + description: "The log search filter's email recipient.", + name: 'google_workspace.admin.email.log_search_filter.recipient.value', + type: 'keyword', + }, + 'google_workspace.admin.email.log_search_filter.sender.value': { + category: 'google_workspace', + description: "The log search filter's email sender.", + name: 'google_workspace.admin.email.log_search_filter.sender.value', + type: 'keyword', + }, + 'google_workspace.admin.email.log_search_filter.recipient.ip': { + category: 'google_workspace', + description: "The log search filter's email recipient's IP address.", + name: 'google_workspace.admin.email.log_search_filter.recipient.ip', + type: 'ip', + }, + 'google_workspace.admin.email.log_search_filter.sender.ip': { + category: 'google_workspace', + description: "The log search filter's email sender's IP address.", + name: 'google_workspace.admin.email.log_search_filter.sender.ip', + type: 'ip', + }, + 'google_workspace.admin.chrome_licenses.enabled': { + category: 'google_workspace', + description: + 'Licences enabled. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-org-settings ', + name: 'google_workspace.admin.chrome_licenses.enabled', + type: 'keyword', + }, + 'google_workspace.admin.chrome_licenses.allowed': { + category: 'google_workspace', + description: + 'Licences enabled. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-org-settings ', + name: 'google_workspace.admin.chrome_licenses.allowed', + type: 'keyword', + }, + 'google_workspace.admin.oauth2.service.name': { + category: 'google_workspace', + description: + 'OAuth2 service name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings ', + name: 'google_workspace.admin.oauth2.service.name', + type: 'keyword', + }, + 'google_workspace.admin.oauth2.application.id': { + category: 'google_workspace', + description: 'OAuth2 application ID.', + name: 'google_workspace.admin.oauth2.application.id', + type: 'keyword', + }, + 'google_workspace.admin.oauth2.application.name': { + category: 'google_workspace', + description: 'OAuth2 application name.', + name: 'google_workspace.admin.oauth2.application.name', + type: 'keyword', + }, + 'google_workspace.admin.oauth2.application.type': { + category: 'google_workspace', + description: + 'OAuth2 application type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings ', + name: 'google_workspace.admin.oauth2.application.type', + type: 'keyword', + }, + 'google_workspace.admin.verification_method': { + category: 'google_workspace', + description: + 'Related verification method. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings and https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-domain-settings ', + name: 'google_workspace.admin.verification_method', + type: 'keyword', + }, + 'google_workspace.admin.alert.name': { + category: 'google_workspace', + description: 'The alert name.', + name: 'google_workspace.admin.alert.name', + type: 'keyword', + }, + 'google_workspace.admin.rule.name': { + category: 'google_workspace', + description: 'The rule name.', + name: 'google_workspace.admin.rule.name', + type: 'keyword', + }, + 'google_workspace.admin.api.client.name': { + category: 'google_workspace', + description: 'The API client name.', + name: 'google_workspace.admin.api.client.name', + type: 'keyword', + }, + 'google_workspace.admin.api.scopes': { + category: 'google_workspace', + description: 'The API scopes.', + name: 'google_workspace.admin.api.scopes', + type: 'keyword', + }, + 'google_workspace.admin.mdm.token': { + category: 'google_workspace', + description: 'The MDM vendor enrollment token.', + name: 'google_workspace.admin.mdm.token', + type: 'keyword', + }, + 'google_workspace.admin.mdm.vendor': { + category: 'google_workspace', + description: "The MDM vendor's name.", + name: 'google_workspace.admin.mdm.vendor', + type: 'keyword', + }, + 'google_workspace.admin.info_type': { + category: 'google_workspace', + description: + 'This will be used to state what kind of information was changed. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-domain-settings ', + name: 'google_workspace.admin.info_type', + type: 'keyword', + }, + 'google_workspace.admin.email_monitor.dest_email': { + category: 'google_workspace', + description: 'The destination address of the email monitor.', + name: 'google_workspace.admin.email_monitor.dest_email', + type: 'keyword', + }, + 'google_workspace.admin.email_monitor.level.chat': { + category: 'google_workspace', + description: 'The chat email monitor level.', + name: 'google_workspace.admin.email_monitor.level.chat', + type: 'keyword', + }, + 'google_workspace.admin.email_monitor.level.draft': { + category: 'google_workspace', + description: 'The draft email monitor level.', + name: 'google_workspace.admin.email_monitor.level.draft', type: 'keyword', }, - 'fortinet.firewall.xauthuser': { - category: 'fortinet', - description: 'XAuth User Name ', - name: 'fortinet.firewall.xauthuser', + 'google_workspace.admin.email_monitor.level.incoming': { + category: 'google_workspace', + description: 'The incoming email monitor level.', + name: 'google_workspace.admin.email_monitor.level.incoming', type: 'keyword', }, - 'fortinet.firewall.xid': { - category: 'fortinet', - description: 'Wireless X ID ', - name: 'fortinet.firewall.xid', - type: 'integer', - }, - 'googlecloud.destination.instance.project_id': { - category: 'googlecloud', - description: 'ID of the project containing the VM. ', - name: 'googlecloud.destination.instance.project_id', + 'google_workspace.admin.email_monitor.level.outgoing': { + category: 'google_workspace', + description: 'The outgoing email monitor level.', + name: 'google_workspace.admin.email_monitor.level.outgoing', type: 'keyword', }, - 'googlecloud.destination.instance.region': { - category: 'googlecloud', - description: 'Region of the VM. ', - name: 'googlecloud.destination.instance.region', + 'google_workspace.admin.email_dump.include_deleted': { + category: 'google_workspace', + description: 'Indicates if deleted emails are included in the export.', + name: 'google_workspace.admin.email_dump.include_deleted', + type: 'boolean', + }, + 'google_workspace.admin.email_dump.package_content': { + category: 'google_workspace', + description: 'The contents of the mailbox package.', + name: 'google_workspace.admin.email_dump.package_content', type: 'keyword', }, - 'googlecloud.destination.instance.zone': { - category: 'googlecloud', - description: 'Zone of the VM. ', - name: 'googlecloud.destination.instance.zone', + 'google_workspace.admin.email_dump.query': { + category: 'google_workspace', + description: 'The search query used for the dump.', + name: 'google_workspace.admin.email_dump.query', type: 'keyword', }, - 'googlecloud.destination.vpc.project_id': { - category: 'googlecloud', - description: 'ID of the project containing the VM. ', - name: 'googlecloud.destination.vpc.project_id', + 'google_workspace.admin.request.id': { + category: 'google_workspace', + description: 'The request ID.', + name: 'google_workspace.admin.request.id', type: 'keyword', }, - 'googlecloud.destination.vpc.vpc_name': { - category: 'googlecloud', - description: 'VPC on which the VM is operating. ', - name: 'googlecloud.destination.vpc.vpc_name', + 'google_workspace.admin.mobile.action.id': { + category: 'google_workspace', + description: "The mobile device action's ID.", + name: 'google_workspace.admin.mobile.action.id', type: 'keyword', }, - 'googlecloud.destination.vpc.subnetwork_name': { - category: 'googlecloud', - description: 'Subnetwork on which the VM is operating. ', - name: 'googlecloud.destination.vpc.subnetwork_name', + 'google_workspace.admin.mobile.action.type': { + category: 'google_workspace', + description: + "The mobile device action's type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ", + name: 'google_workspace.admin.mobile.action.type', type: 'keyword', }, - 'googlecloud.source.instance.project_id': { - category: 'googlecloud', - description: 'ID of the project containing the VM. ', - name: 'googlecloud.source.instance.project_id', + 'google_workspace.admin.mobile.certificate.name': { + category: 'google_workspace', + description: 'The mobile certificate common name.', + name: 'google_workspace.admin.mobile.certificate.name', type: 'keyword', }, - 'googlecloud.source.instance.region': { - category: 'googlecloud', - description: 'Region of the VM. ', - name: 'googlecloud.source.instance.region', + 'google_workspace.admin.mobile.company_owned_devices': { + category: 'google_workspace', + description: 'The number of devices a company owns.', + name: 'google_workspace.admin.mobile.company_owned_devices', + type: 'long', + }, + 'google_workspace.admin.distribution.entity.name': { + category: 'google_workspace', + description: + 'The distribution entity value, which can be a group name or an org-unit name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ', + name: 'google_workspace.admin.distribution.entity.name', type: 'keyword', }, - 'googlecloud.source.instance.zone': { - category: 'googlecloud', - description: 'Zone of the VM. ', - name: 'googlecloud.source.instance.zone', + 'google_workspace.admin.distribution.entity.type': { + category: 'google_workspace', + description: + 'The distribution entity type, which can be a group or an org-unit. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ', + name: 'google_workspace.admin.distribution.entity.type', type: 'keyword', }, - 'googlecloud.source.vpc.project_id': { - category: 'googlecloud', - description: 'ID of the project containing the VM. ', - name: 'googlecloud.source.vpc.project_id', + 'google_workspace.drive.billable': { + category: 'google_workspace', + description: 'Whether this activity is billable.', + name: 'google_workspace.drive.billable', + type: 'boolean', + }, + 'google_workspace.drive.source_folder_id': { + category: 'google_workspace', + name: 'google_workspace.drive.source_folder_id', type: 'keyword', }, - 'googlecloud.source.vpc.vpc_name': { - category: 'googlecloud', - description: 'VPC on which the VM is operating. ', - name: 'googlecloud.source.vpc.vpc_name', + 'google_workspace.drive.source_folder_title': { + category: 'google_workspace', + name: 'google_workspace.drive.source_folder_title', type: 'keyword', }, - 'googlecloud.source.vpc.subnetwork_name': { - category: 'googlecloud', - description: 'Subnetwork on which the VM is operating. ', - name: 'googlecloud.source.vpc.subnetwork_name', + 'google_workspace.drive.destination_folder_id': { + category: 'google_workspace', + name: 'google_workspace.drive.destination_folder_id', type: 'keyword', }, - 'googlecloud.audit.type': { - category: 'googlecloud', - description: 'Type property. ', - name: 'googlecloud.audit.type', + 'google_workspace.drive.destination_folder_title': { + category: 'google_workspace', + name: 'google_workspace.drive.destination_folder_title', type: 'keyword', }, - 'googlecloud.audit.authentication_info.principal_email': { - category: 'googlecloud', - description: 'The email address of the authenticated user making the request. ', - name: 'googlecloud.audit.authentication_info.principal_email', + 'google_workspace.drive.file.id': { + category: 'google_workspace', + name: 'google_workspace.drive.file.id', type: 'keyword', }, - 'googlecloud.audit.authentication_info.authority_selector': { - category: 'googlecloud', + 'google_workspace.drive.file.type': { + category: 'google_workspace', description: - 'The authority selector specified by the requestor, if any. It is not guaranteed that the principal was allowed to use this authority. ', - name: 'googlecloud.audit.authentication_info.authority_selector', + 'Document Drive type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.file.type', type: 'keyword', }, - 'googlecloud.audit.authorization_info.permission': { - category: 'googlecloud', - description: 'The required IAM permission. ', - name: 'googlecloud.audit.authorization_info.permission', + 'google_workspace.drive.originating_app_id': { + category: 'google_workspace', + description: 'The Google Cloud Project ID of the application that performed the action. ', + name: 'google_workspace.drive.originating_app_id', type: 'keyword', }, - 'googlecloud.audit.authorization_info.granted': { - category: 'googlecloud', - description: 'Whether or not authorization for resource and permission was granted. ', - name: 'googlecloud.audit.authorization_info.granted', + 'google_workspace.drive.file.owner.email': { + category: 'google_workspace', + name: 'google_workspace.drive.file.owner.email', + type: 'keyword', + }, + 'google_workspace.drive.file.owner.is_shared_drive': { + category: 'google_workspace', + description: 'Boolean flag denoting whether owner is a shared drive. ', + name: 'google_workspace.drive.file.owner.is_shared_drive', type: 'boolean', }, - 'googlecloud.audit.authorization_info.resource_attributes.service': { - category: 'googlecloud', - description: 'The name of the service. ', - name: 'googlecloud.audit.authorization_info.resource_attributes.service', - type: 'keyword', + 'google_workspace.drive.primary_event': { + category: 'google_workspace', + description: + 'Whether this is a primary event. A single user action in Drive may generate several events. ', + name: 'google_workspace.drive.primary_event', + type: 'boolean', }, - 'googlecloud.audit.authorization_info.resource_attributes.name': { - category: 'googlecloud', - description: 'The name of the resource. ', - name: 'googlecloud.audit.authorization_info.resource_attributes.name', + 'google_workspace.drive.shared_drive_id': { + category: 'google_workspace', + description: + 'The unique identifier of the Team Drive. Only populated for for events relating to a Team Drive or item contained inside a Team Drive. ', + name: 'google_workspace.drive.shared_drive_id', type: 'keyword', }, - 'googlecloud.audit.authorization_info.resource_attributes.type': { - category: 'googlecloud', - description: 'The type of the resource. ', - name: 'googlecloud.audit.authorization_info.resource_attributes.type', + 'google_workspace.drive.visibility': { + category: 'google_workspace', + description: + 'Visibility of target file. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.visibility', type: 'keyword', }, - 'googlecloud.audit.method_name': { - category: 'googlecloud', + 'google_workspace.drive.new_value': { + category: 'google_workspace', description: - "The name of the service method or operation. For API calls, this should be the name of the API method. For example, 'google.datastore.v1.Datastore.RunQuery'. ", - name: 'googlecloud.audit.method_name', + 'When a setting or property of the file changes, the new value for it will appear here. ', + name: 'google_workspace.drive.new_value', type: 'keyword', }, - 'googlecloud.audit.num_response_items': { - category: 'googlecloud', - description: 'The number of items returned from a List or Query API method, if applicable. ', - name: 'googlecloud.audit.num_response_items', - type: 'long', + 'google_workspace.drive.old_value': { + category: 'google_workspace', + description: + 'When a setting or property of the file changes, the old value for it will appear here. ', + name: 'google_workspace.drive.old_value', + type: 'keyword', }, - 'googlecloud.audit.request.proto_name': { - category: 'googlecloud', - description: 'Type property of the request. ', - name: 'googlecloud.audit.request.proto_name', + 'google_workspace.drive.sheets_import_range_recipient_doc': { + category: 'google_workspace', + description: 'Doc ID of the recipient of a sheets import range.', + name: 'google_workspace.drive.sheets_import_range_recipient_doc', type: 'keyword', }, - 'googlecloud.audit.request.filter': { - category: 'googlecloud', - description: 'Filter of the request. ', - name: 'googlecloud.audit.request.filter', + 'google_workspace.drive.old_visibility': { + category: 'google_workspace', + description: 'When visibility changes, this holds the old value. ', + name: 'google_workspace.drive.old_visibility', type: 'keyword', }, - 'googlecloud.audit.request.name': { - category: 'googlecloud', - description: 'Name of the request. ', - name: 'googlecloud.audit.request.name', + 'google_workspace.drive.visibility_change': { + category: 'google_workspace', + description: 'When visibility changes, this holds the new overall visibility of the file. ', + name: 'google_workspace.drive.visibility_change', type: 'keyword', }, - 'googlecloud.audit.request.resource_name': { - category: 'googlecloud', - description: 'Name of the request resource. ', - name: 'googlecloud.audit.request.resource_name', + 'google_workspace.drive.target_domain': { + category: 'google_workspace', + description: + 'The domain for which the acccess scope was changed. This can also be the alias all to indicate the access scope was changed for all domains that have visibility for this document. ', + name: 'google_workspace.drive.target_domain', type: 'keyword', }, - 'googlecloud.audit.request_metadata.caller_ip': { - category: 'googlecloud', - description: 'The IP address of the caller. ', - name: 'googlecloud.audit.request_metadata.caller_ip', - type: 'ip', + 'google_workspace.drive.added_role': { + category: 'google_workspace', + description: + 'Added membership role of a user/group in a Team Drive. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.added_role', + type: 'keyword', }, - 'googlecloud.audit.request_metadata.caller_supplied_user_agent': { - category: 'googlecloud', + 'google_workspace.drive.membership_change_type': { + category: 'google_workspace', description: - 'The user agent of the caller. This information is not authenticated and should be treated accordingly. ', - name: 'googlecloud.audit.request_metadata.caller_supplied_user_agent', + 'Type of change in Team Drive membership of a user/group. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.membership_change_type', type: 'keyword', }, - 'googlecloud.audit.response.proto_name': { - category: 'googlecloud', - description: 'Type property of the response. ', - name: 'googlecloud.audit.response.proto_name', + 'google_workspace.drive.shared_drive_settings_change_type': { + category: 'google_workspace', + description: + 'Type of change in Team Drive settings. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.shared_drive_settings_change_type', type: 'keyword', }, - 'googlecloud.audit.response.details.group': { - category: 'googlecloud', - description: 'The name of the group. ', - name: 'googlecloud.audit.response.details.group', + 'google_workspace.drive.removed_role': { + category: 'google_workspace', + description: + 'Removed membership role of a user/group in a Team Drive. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'google_workspace.drive.removed_role', type: 'keyword', }, - 'googlecloud.audit.response.details.kind': { - category: 'googlecloud', - description: 'The kind of the response details. ', - name: 'googlecloud.audit.response.details.kind', + 'google_workspace.drive.target': { + category: 'google_workspace', + description: 'Target user or group.', + name: 'google_workspace.drive.target', type: 'keyword', }, - 'googlecloud.audit.response.details.name': { - category: 'googlecloud', - description: 'The name of the response details. ', - name: 'googlecloud.audit.response.details.name', + 'google_workspace.groups.acl_permission': { + category: 'google_workspace', + description: + 'Group permission setting updated. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'google_workspace.groups.acl_permission', type: 'keyword', }, - 'googlecloud.audit.response.details.uid': { - category: 'googlecloud', - description: 'The uid of the response details. ', - name: 'googlecloud.audit.response.details.uid', + 'google_workspace.groups.email': { + category: 'google_workspace', + description: 'Group email. ', + name: 'google_workspace.groups.email', type: 'keyword', }, - 'googlecloud.audit.response.status': { - category: 'googlecloud', - description: 'Status of the response. ', - name: 'googlecloud.audit.response.status', + 'google_workspace.groups.member.email': { + category: 'google_workspace', + description: 'Member email. ', + name: 'google_workspace.groups.member.email', type: 'keyword', }, - 'googlecloud.audit.resource_name': { - category: 'googlecloud', + 'google_workspace.groups.member.role': { + category: 'google_workspace', description: - "The resource or collection that is the target of the operation. The name is a scheme-less URI, not including the API service name. For example, 'shelves/SHELF_ID/books'. ", - name: 'googlecloud.audit.resource_name', + 'Member role. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'google_workspace.groups.member.role', type: 'keyword', }, - 'googlecloud.audit.resource_location.current_locations': { - category: 'googlecloud', - description: 'Current locations of the resource. ', - name: 'googlecloud.audit.resource_location.current_locations', + 'google_workspace.groups.setting': { + category: 'google_workspace', + description: + 'Group setting updated. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'google_workspace.groups.setting', type: 'keyword', }, - 'googlecloud.audit.service_name': { - category: 'googlecloud', + 'google_workspace.groups.new_value': { + category: 'google_workspace', description: - 'The name of the API service performing the operation. For example, datastore.googleapis.com. ', - name: 'googlecloud.audit.service_name', + 'New value(s) of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'google_workspace.groups.new_value', type: 'keyword', }, - 'googlecloud.audit.status.code': { - category: 'googlecloud', - description: 'The status code, which should be an enum value of google.rpc.Code. ', - name: 'googlecloud.audit.status.code', - type: 'integer', - }, - 'googlecloud.audit.status.message': { - category: 'googlecloud', + 'google_workspace.groups.old_value': { + category: 'google_workspace', description: - 'A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client. ', - name: 'googlecloud.audit.status.message', + 'Old value(s) of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups', + name: 'google_workspace.groups.old_value', type: 'keyword', }, - 'googlecloud.firewall.rule_details.priority': { - category: 'googlecloud', - description: 'The priority for the firewall rule.', - name: 'googlecloud.firewall.rule_details.priority', - type: 'long', + 'google_workspace.groups.value': { + category: 'google_workspace', + description: + 'Value of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'google_workspace.groups.value', + type: 'keyword', }, - 'googlecloud.firewall.rule_details.action': { - category: 'googlecloud', - description: 'Action that the rule performs on match.', - name: 'googlecloud.firewall.rule_details.action', + 'google_workspace.groups.message.id': { + category: 'google_workspace', + description: 'SMTP message Id of an email message. Present for moderation events. ', + name: 'google_workspace.groups.message.id', type: 'keyword', }, - 'googlecloud.firewall.rule_details.direction': { - category: 'googlecloud', - description: 'Direction of traffic that matches this rule.', - name: 'googlecloud.firewall.rule_details.direction', + 'google_workspace.groups.message.moderation_action': { + category: 'google_workspace', + description: 'Message moderation action. Possible values are `approved` and `rejected`. ', + name: 'google_workspace.groups.message.moderation_action', type: 'keyword', }, - 'googlecloud.firewall.rule_details.reference': { - category: 'googlecloud', - description: 'Reference to the firewall rule.', - name: 'googlecloud.firewall.rule_details.reference', + 'google_workspace.groups.status': { + category: 'google_workspace', + description: + 'A status describing the output of an operation. Possible values are `failed` and `succeeded`. ', + name: 'google_workspace.groups.status', type: 'keyword', }, - 'googlecloud.firewall.rule_details.source_range': { - category: 'googlecloud', - description: 'List of source ranges that the firewall rule applies to.', - name: 'googlecloud.firewall.rule_details.source_range', + 'google_workspace.login.affected_email_address': { + category: 'google_workspace', + name: 'google_workspace.login.affected_email_address', type: 'keyword', }, - 'googlecloud.firewall.rule_details.destination_range': { - category: 'googlecloud', - description: 'List of destination ranges that the firewall applies to.', - name: 'googlecloud.firewall.rule_details.destination_range', + 'google_workspace.login.challenge_method': { + category: 'google_workspace', + description: + 'Login challenge method. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'google_workspace.login.challenge_method', type: 'keyword', }, - 'googlecloud.firewall.rule_details.source_tag': { - category: 'googlecloud', - description: 'List of all the source tags that the firewall rule applies to. ', - name: 'googlecloud.firewall.rule_details.source_tag', + 'google_workspace.login.failure_type': { + category: 'google_workspace', + description: + 'Login failure type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'google_workspace.login.failure_type', type: 'keyword', }, - 'googlecloud.firewall.rule_details.target_tag': { - category: 'googlecloud', - description: 'List of all the target tags that the firewall rule applies to. ', - name: 'googlecloud.firewall.rule_details.target_tag', + 'google_workspace.login.type': { + category: 'google_workspace', + description: + 'Login credentials type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'google_workspace.login.type', type: 'keyword', }, - 'googlecloud.firewall.rule_details.ip_port_info': { - category: 'googlecloud', - description: 'List of ip protocols and applicable port ranges for rules. ', - name: 'googlecloud.firewall.rule_details.ip_port_info', - type: 'array', + 'google_workspace.login.is_second_factor': { + category: 'google_workspace', + name: 'google_workspace.login.is_second_factor', + type: 'boolean', }, - 'googlecloud.firewall.rule_details.source_service_account': { - category: 'googlecloud', - description: 'List of all the source service accounts that the firewall rule applies to. ', - name: 'googlecloud.firewall.rule_details.source_service_account', + 'google_workspace.login.is_suspicious': { + category: 'google_workspace', + name: 'google_workspace.login.is_suspicious', + type: 'boolean', + }, + 'google_workspace.saml.application_name': { + category: 'google_workspace', + description: 'Saml SP application name. ', + name: 'google_workspace.saml.application_name', type: 'keyword', }, - 'googlecloud.firewall.rule_details.target_service_account': { - category: 'googlecloud', - description: 'List of all the target service accounts that the firewall rule applies to. ', - name: 'googlecloud.firewall.rule_details.target_service_account', + 'google_workspace.saml.failure_type': { + category: 'google_workspace', + description: + 'Login failure type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/saml. ', + name: 'google_workspace.saml.failure_type', type: 'keyword', }, - 'googlecloud.vpcflow.reporter': { - category: 'googlecloud', - description: "The side which reported the flow. Can be either 'SRC' or 'DEST'. ", - name: 'googlecloud.vpcflow.reporter', + 'google_workspace.saml.initiated_by': { + category: 'google_workspace', + description: 'Requester of SAML authentication. ', + name: 'google_workspace.saml.initiated_by', type: 'keyword', }, - 'googlecloud.vpcflow.rtt.ms': { - category: 'googlecloud', - description: - 'Latency as measured (for TCP flows only) during the time interval. This is the time elapsed between sending a SEQ and receiving a corresponding ACK and it contains the network RTT as well as the application related delay. ', - name: 'googlecloud.vpcflow.rtt.ms', + 'google_workspace.saml.orgunit_path': { + category: 'google_workspace', + description: 'User orgunit. ', + name: 'google_workspace.saml.orgunit_path', + type: 'keyword', + }, + 'google_workspace.saml.status_code': { + category: 'google_workspace', + description: 'SAML status code. ', + name: 'google_workspace.saml.status_code', + type: 'long', + }, + 'google_workspace.saml.second_level_status_code': { + category: 'google_workspace', + description: 'SAML second level status code. ', + name: 'google_workspace.saml.second_level_status_code', type: 'long', }, 'gsuite.actor.type': { @@ -21893,6 +24886,582 @@ export const fieldsBeat: BeatFields = { name: 'iptables.ubiquiti.rule_set', type: 'keyword', }, + 'juniper.srx.reason': { + category: 'juniper', + description: 'reason ', + name: 'juniper.srx.reason', + type: 'keyword', + }, + 'juniper.srx.connection_tag': { + category: 'juniper', + description: 'connection tag ', + name: 'juniper.srx.connection_tag', + type: 'keyword', + }, + 'juniper.srx.service_name': { + category: 'juniper', + description: 'service name ', + name: 'juniper.srx.service_name', + type: 'keyword', + }, + 'juniper.srx.nat_connection_tag': { + category: 'juniper', + description: 'nat connection tag ', + name: 'juniper.srx.nat_connection_tag', + type: 'keyword', + }, + 'juniper.srx.src_nat_rule_type': { + category: 'juniper', + description: 'src nat rule type ', + name: 'juniper.srx.src_nat_rule_type', + type: 'keyword', + }, + 'juniper.srx.src_nat_rule_name': { + category: 'juniper', + description: 'src nat rule name ', + name: 'juniper.srx.src_nat_rule_name', + type: 'keyword', + }, + 'juniper.srx.dst_nat_rule_type': { + category: 'juniper', + description: 'dst nat rule type ', + name: 'juniper.srx.dst_nat_rule_type', + type: 'keyword', + }, + 'juniper.srx.dst_nat_rule_name': { + category: 'juniper', + description: 'dst nat rule name ', + name: 'juniper.srx.dst_nat_rule_name', + type: 'keyword', + }, + 'juniper.srx.protocol_id': { + category: 'juniper', + description: 'protocol id ', + name: 'juniper.srx.protocol_id', + type: 'keyword', + }, + 'juniper.srx.policy_name': { + category: 'juniper', + description: 'policy name ', + name: 'juniper.srx.policy_name', + type: 'keyword', + }, + 'juniper.srx.session_id_32': { + category: 'juniper', + description: 'session id 32 ', + name: 'juniper.srx.session_id_32', + type: 'keyword', + }, + 'juniper.srx.session_id': { + category: 'juniper', + description: 'session id ', + name: 'juniper.srx.session_id', + type: 'keyword', + }, + 'juniper.srx.outbound_packets': { + category: 'juniper', + description: 'packets from client ', + name: 'juniper.srx.outbound_packets', + type: 'integer', + }, + 'juniper.srx.outbound_bytes': { + category: 'juniper', + description: 'bytes from client ', + name: 'juniper.srx.outbound_bytes', + type: 'integer', + }, + 'juniper.srx.inbound_packets': { + category: 'juniper', + description: 'packets from server ', + name: 'juniper.srx.inbound_packets', + type: 'integer', + }, + 'juniper.srx.inbound_bytes': { + category: 'juniper', + description: 'bytes from server ', + name: 'juniper.srx.inbound_bytes', + type: 'integer', + }, + 'juniper.srx.elapsed_time': { + category: 'juniper', + description: 'elapsed time ', + name: 'juniper.srx.elapsed_time', + type: 'date', + }, + 'juniper.srx.application': { + category: 'juniper', + description: 'application ', + name: 'juniper.srx.application', + type: 'keyword', + }, + 'juniper.srx.nested_application': { + category: 'juniper', + description: 'nested application ', + name: 'juniper.srx.nested_application', + type: 'keyword', + }, + 'juniper.srx.username': { + category: 'juniper', + description: 'username ', + name: 'juniper.srx.username', + type: 'keyword', + }, + 'juniper.srx.roles': { + category: 'juniper', + description: 'roles ', + name: 'juniper.srx.roles', + type: 'keyword', + }, + 'juniper.srx.encrypted': { + category: 'juniper', + description: 'encrypted ', + name: 'juniper.srx.encrypted', + type: 'keyword', + }, + 'juniper.srx.application_category': { + category: 'juniper', + description: 'application category ', + name: 'juniper.srx.application_category', + type: 'keyword', + }, + 'juniper.srx.application_sub_category': { + category: 'juniper', + description: 'application sub category ', + name: 'juniper.srx.application_sub_category', + type: 'keyword', + }, + 'juniper.srx.application_characteristics': { + category: 'juniper', + description: 'application characteristics ', + name: 'juniper.srx.application_characteristics', + type: 'keyword', + }, + 'juniper.srx.secure_web_proxy_session_type': { + category: 'juniper', + description: 'secure web proxy session type ', + name: 'juniper.srx.secure_web_proxy_session_type', + type: 'keyword', + }, + 'juniper.srx.peer_session_id': { + category: 'juniper', + description: 'peer session id ', + name: 'juniper.srx.peer_session_id', + type: 'keyword', + }, + 'juniper.srx.peer_source_address': { + category: 'juniper', + description: 'peer source address ', + name: 'juniper.srx.peer_source_address', + type: 'ip', + }, + 'juniper.srx.peer_source_port': { + category: 'juniper', + description: 'peer source port ', + name: 'juniper.srx.peer_source_port', + type: 'integer', + }, + 'juniper.srx.peer_destination_address': { + category: 'juniper', + description: 'peer destination address ', + name: 'juniper.srx.peer_destination_address', + type: 'ip', + }, + 'juniper.srx.peer_destination_port': { + category: 'juniper', + description: 'peer destination port ', + name: 'juniper.srx.peer_destination_port', + type: 'integer', + }, + 'juniper.srx.hostname': { + category: 'juniper', + description: 'hostname ', + name: 'juniper.srx.hostname', + type: 'keyword', + }, + 'juniper.srx.src_vrf_grp': { + category: 'juniper', + description: 'src_vrf_grp ', + name: 'juniper.srx.src_vrf_grp', + type: 'keyword', + }, + 'juniper.srx.dst_vrf_grp': { + category: 'juniper', + description: 'dst_vrf_grp ', + name: 'juniper.srx.dst_vrf_grp', + type: 'keyword', + }, + 'juniper.srx.icmp_type': { + category: 'juniper', + description: 'icmp type ', + name: 'juniper.srx.icmp_type', + type: 'integer', + }, + 'juniper.srx.process': { + category: 'juniper', + description: 'process that generated the message ', + name: 'juniper.srx.process', + type: 'keyword', + }, + 'juniper.srx.apbr_rule_type': { + category: 'juniper', + description: 'apbr rule type ', + name: 'juniper.srx.apbr_rule_type', + type: 'keyword', + }, + 'juniper.srx.dscp_value': { + category: 'juniper', + description: 'apbr rule type ', + name: 'juniper.srx.dscp_value', + type: 'integer', + }, + 'juniper.srx.logical_system_name': { + category: 'juniper', + description: 'logical system name ', + name: 'juniper.srx.logical_system_name', + type: 'keyword', + }, + 'juniper.srx.profile_name': { + category: 'juniper', + description: 'profile name ', + name: 'juniper.srx.profile_name', + type: 'keyword', + }, + 'juniper.srx.routing_instance': { + category: 'juniper', + description: 'routing instance ', + name: 'juniper.srx.routing_instance', + type: 'keyword', + }, + 'juniper.srx.rule_name': { + category: 'juniper', + description: 'rule name ', + name: 'juniper.srx.rule_name', + type: 'keyword', + }, + 'juniper.srx.uplink_tx_bytes': { + category: 'juniper', + description: 'uplink tx bytes ', + name: 'juniper.srx.uplink_tx_bytes', + type: 'integer', + }, + 'juniper.srx.uplink_rx_bytes': { + category: 'juniper', + description: 'uplink rx bytes ', + name: 'juniper.srx.uplink_rx_bytes', + type: 'integer', + }, + 'juniper.srx.obj': { + category: 'juniper', + description: 'url path ', + name: 'juniper.srx.obj', + type: 'keyword', + }, + 'juniper.srx.url': { + category: 'juniper', + description: 'url domain ', + name: 'juniper.srx.url', + type: 'keyword', + }, + 'juniper.srx.profile': { + category: 'juniper', + description: 'filter profile ', + name: 'juniper.srx.profile', + type: 'keyword', + }, + 'juniper.srx.category': { + category: 'juniper', + description: 'filter category ', + name: 'juniper.srx.category', + type: 'keyword', + }, + 'juniper.srx.filename': { + category: 'juniper', + description: 'filename ', + name: 'juniper.srx.filename', + type: 'keyword', + }, + 'juniper.srx.temporary_filename': { + category: 'juniper', + description: 'temporary_filename ', + name: 'juniper.srx.temporary_filename', + type: 'keyword', + }, + 'juniper.srx.name': { + category: 'juniper', + description: 'name ', + name: 'juniper.srx.name', + type: 'keyword', + }, + 'juniper.srx.error_message': { + category: 'juniper', + description: 'error_message ', + name: 'juniper.srx.error_message', + type: 'keyword', + }, + 'juniper.srx.error_code': { + category: 'juniper', + description: 'error_code ', + name: 'juniper.srx.error_code', + type: 'keyword', + }, + 'juniper.srx.action': { + category: 'juniper', + description: 'action ', + name: 'juniper.srx.action', + type: 'keyword', + }, + 'juniper.srx.protocol': { + category: 'juniper', + description: 'protocol ', + name: 'juniper.srx.protocol', + type: 'keyword', + }, + 'juniper.srx.protocol_name': { + category: 'juniper', + description: 'protocol name ', + name: 'juniper.srx.protocol_name', + type: 'keyword', + }, + 'juniper.srx.type': { + category: 'juniper', + description: 'type ', + name: 'juniper.srx.type', + type: 'keyword', + }, + 'juniper.srx.repeat_count': { + category: 'juniper', + description: 'repeat count ', + name: 'juniper.srx.repeat_count', + type: 'integer', + }, + 'juniper.srx.alert': { + category: 'juniper', + description: 'repeat alert ', + name: 'juniper.srx.alert', + type: 'keyword', + }, + 'juniper.srx.message_type': { + category: 'juniper', + description: 'message type ', + name: 'juniper.srx.message_type', + type: 'keyword', + }, + 'juniper.srx.threat_severity': { + category: 'juniper', + description: 'threat severity ', + name: 'juniper.srx.threat_severity', + type: 'keyword', + }, + 'juniper.srx.application_name': { + category: 'juniper', + description: 'application name ', + name: 'juniper.srx.application_name', + type: 'keyword', + }, + 'juniper.srx.attack_name': { + category: 'juniper', + description: 'attack name ', + name: 'juniper.srx.attack_name', + type: 'keyword', + }, + 'juniper.srx.index': { + category: 'juniper', + description: 'index ', + name: 'juniper.srx.index', + type: 'keyword', + }, + 'juniper.srx.message': { + category: 'juniper', + description: 'mesagge ', + name: 'juniper.srx.message', + type: 'keyword', + }, + 'juniper.srx.epoch_time': { + category: 'juniper', + description: 'epoch time ', + name: 'juniper.srx.epoch_time', + type: 'date', + }, + 'juniper.srx.packet_log_id': { + category: 'juniper', + description: 'packet log id ', + name: 'juniper.srx.packet_log_id', + type: 'integer', + }, + 'juniper.srx.export_id': { + category: 'juniper', + description: 'packet log id ', + name: 'juniper.srx.export_id', + type: 'integer', + }, + 'juniper.srx.ddos_application_name': { + category: 'juniper', + description: 'ddos application name ', + name: 'juniper.srx.ddos_application_name', + type: 'keyword', + }, + 'juniper.srx.connection_hit_rate': { + category: 'juniper', + description: 'connection hit rate ', + name: 'juniper.srx.connection_hit_rate', + type: 'integer', + }, + 'juniper.srx.time_scope': { + category: 'juniper', + description: 'time scope ', + name: 'juniper.srx.time_scope', + type: 'keyword', + }, + 'juniper.srx.context_hit_rate': { + category: 'juniper', + description: 'context hit rate ', + name: 'juniper.srx.context_hit_rate', + type: 'integer', + }, + 'juniper.srx.context_value_hit_rate': { + category: 'juniper', + description: 'context value hit rate ', + name: 'juniper.srx.context_value_hit_rate', + type: 'integer', + }, + 'juniper.srx.time_count': { + category: 'juniper', + description: 'time count ', + name: 'juniper.srx.time_count', + type: 'integer', + }, + 'juniper.srx.time_period': { + category: 'juniper', + description: 'time period ', + name: 'juniper.srx.time_period', + type: 'integer', + }, + 'juniper.srx.context_value': { + category: 'juniper', + description: 'context value ', + name: 'juniper.srx.context_value', + type: 'keyword', + }, + 'juniper.srx.context_name': { + category: 'juniper', + description: 'context name ', + name: 'juniper.srx.context_name', + type: 'keyword', + }, + 'juniper.srx.ruleebase_name': { + category: 'juniper', + description: 'ruleebase name ', + name: 'juniper.srx.ruleebase_name', + type: 'keyword', + }, + 'juniper.srx.verdict_source': { + category: 'juniper', + description: 'verdict source ', + name: 'juniper.srx.verdict_source', + type: 'keyword', + }, + 'juniper.srx.verdict_number': { + category: 'juniper', + description: 'verdict number ', + name: 'juniper.srx.verdict_number', + type: 'integer', + }, + 'juniper.srx.file_category': { + category: 'juniper', + description: 'file category ', + name: 'juniper.srx.file_category', + type: 'keyword', + }, + 'juniper.srx.sample_sha256': { + category: 'juniper', + description: 'sample sha256 ', + name: 'juniper.srx.sample_sha256', + type: 'keyword', + }, + 'juniper.srx.malware_info': { + category: 'juniper', + description: 'malware info ', + name: 'juniper.srx.malware_info', + type: 'keyword', + }, + 'juniper.srx.client_ip': { + category: 'juniper', + description: 'client ip ', + name: 'juniper.srx.client_ip', + type: 'ip', + }, + 'juniper.srx.tenant_id': { + category: 'juniper', + description: 'tenant id ', + name: 'juniper.srx.tenant_id', + type: 'keyword', + }, + 'juniper.srx.timestamp': { + category: 'juniper', + description: 'timestamp ', + name: 'juniper.srx.timestamp', + type: 'date', + }, + 'juniper.srx.th': { + category: 'juniper', + description: 'th ', + name: 'juniper.srx.th', + type: 'keyword', + }, + 'juniper.srx.status': { + category: 'juniper', + description: 'status ', + name: 'juniper.srx.status', + type: 'keyword', + }, + 'juniper.srx.state': { + category: 'juniper', + description: 'state ', + name: 'juniper.srx.state', + type: 'keyword', + }, + 'juniper.srx.file_hash_lookup': { + category: 'juniper', + description: 'file hash lookup ', + name: 'juniper.srx.file_hash_lookup', + type: 'keyword', + }, + 'juniper.srx.file_name': { + category: 'juniper', + description: 'file name ', + name: 'juniper.srx.file_name', + type: 'keyword', + }, + 'juniper.srx.action_detail': { + category: 'juniper', + description: 'action detail ', + name: 'juniper.srx.action_detail', + type: 'keyword', + }, + 'juniper.srx.sub_category': { + category: 'juniper', + description: 'sub category ', + name: 'juniper.srx.sub_category', + type: 'keyword', + }, + 'juniper.srx.feed_name': { + category: 'juniper', + description: 'feed name ', + name: 'juniper.srx.feed_name', + type: 'keyword', + }, + 'juniper.srx.occur_count': { + category: 'juniper', + description: 'occur count ', + name: 'juniper.srx.occur_count', + type: 'integer', + }, + 'juniper.srx.tag': { + category: 'juniper', + description: 'system log message tag, which uniquely identifies the message. ', + name: 'juniper.srx.tag', + type: 'keyword', + }, 'microsoft.defender_atp.lastUpdateTime': { category: 'microsoft', description: 'The date and time (in UTC) the alert was last updated. ', @@ -21998,6 +25567,267 @@ export const fieldsBeat: BeatFields = { name: 'microsoft.defender_atp.evidence.userPrincipalName', type: 'keyword', }, + 'microsoft.m365_defender.incidentId': { + category: 'microsoft', + description: 'Unique identifier to represent the incident. ', + name: 'microsoft.m365_defender.incidentId', + type: 'keyword', + }, + 'microsoft.m365_defender.redirectIncidentId': { + category: 'microsoft', + description: + 'Only populated in case an incident is being grouped together with another incident, as part of the incident processing logic. ', + name: 'microsoft.m365_defender.redirectIncidentId', + type: 'keyword', + }, + 'microsoft.m365_defender.incidentName': { + category: 'microsoft', + description: 'Name of the Incident. ', + name: 'microsoft.m365_defender.incidentName', + type: 'keyword', + }, + 'microsoft.m365_defender.determination': { + category: 'microsoft', + description: + 'Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other. ', + name: 'microsoft.m365_defender.determination', + type: 'keyword', + }, + 'microsoft.m365_defender.investigationState': { + category: 'microsoft', + description: 'The current state of the Investigation. ', + name: 'microsoft.m365_defender.investigationState', + type: 'keyword', + }, + 'microsoft.m365_defender.assignedTo': { + category: 'microsoft', + description: 'Owner of the alert. ', + name: 'microsoft.m365_defender.assignedTo', + type: 'keyword', + }, + 'microsoft.m365_defender.tags': { + category: 'microsoft', + description: + 'Array of custom tags associated with an incident, for example to flag a group of incidents with a common characteristic. ', + name: 'microsoft.m365_defender.tags', + type: 'keyword', + }, + 'microsoft.m365_defender.status': { + category: 'microsoft', + description: + "Specifies the current status of the alert. Possible values are: 'Unknown', 'New', 'InProgress' and 'Resolved'. ", + name: 'microsoft.m365_defender.status', + type: 'keyword', + }, + 'microsoft.m365_defender.classification': { + category: 'microsoft', + description: + "Specification of the alert. Possible values are: 'Unknown', 'FalsePositive', 'TruePositive'. ", + name: 'microsoft.m365_defender.classification', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.incidentId': { + category: 'microsoft', + description: 'Unique identifier to represent the incident this alert is associated with. ', + name: 'microsoft.m365_defender.alerts.incidentId', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.resolvedTime': { + category: 'microsoft', + description: 'Time when alert was resolved. ', + name: 'microsoft.m365_defender.alerts.resolvedTime', + type: 'date', + }, + 'microsoft.m365_defender.alerts.status': { + category: 'microsoft', + description: 'Categorize alerts (as New, Active, or Resolved). ', + name: 'microsoft.m365_defender.alerts.status', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.severity': { + category: 'microsoft', + description: 'The severity of the related alert. ', + name: 'microsoft.m365_defender.alerts.severity', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.creationTime': { + category: 'microsoft', + description: 'Time when alert was first created. ', + name: 'microsoft.m365_defender.alerts.creationTime', + type: 'date', + }, + 'microsoft.m365_defender.alerts.lastUpdatedTime': { + category: 'microsoft', + description: 'Time when alert was last updated. ', + name: 'microsoft.m365_defender.alerts.lastUpdatedTime', + type: 'date', + }, + 'microsoft.m365_defender.alerts.investigationId': { + category: 'microsoft', + description: 'The automated investigation id triggered by this alert. ', + name: 'microsoft.m365_defender.alerts.investigationId', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.userSid': { + category: 'microsoft', + description: 'The SID of the related user ', + name: 'microsoft.m365_defender.alerts.userSid', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.detectionSource': { + category: 'microsoft', + description: 'The service that initially detected the threat. ', + name: 'microsoft.m365_defender.alerts.detectionSource', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.classification': { + category: 'microsoft', + description: + 'The specification for the incident. The property values are: Unknown, FalsePositive, TruePositive or null. ', + name: 'microsoft.m365_defender.alerts.classification', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.investigationState': { + category: 'microsoft', + description: "Information on the investigation's current status. ", + name: 'microsoft.m365_defender.alerts.investigationState', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.determination': { + category: 'microsoft', + description: + 'Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other or null ', + name: 'microsoft.m365_defender.alerts.determination', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.assignedTo': { + category: 'microsoft', + description: 'Owner of the incident, or null if no owner is assigned. ', + name: 'microsoft.m365_defender.alerts.assignedTo', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.actorName': { + category: 'microsoft', + description: 'The activity group, if any, the associated with this alert. ', + name: 'microsoft.m365_defender.alerts.actorName', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.threatFamilyName': { + category: 'microsoft', + description: 'Threat family associated with this alert. ', + name: 'microsoft.m365_defender.alerts.threatFamilyName', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.mitreTechniques': { + category: 'microsoft', + description: 'The attack techniques, as aligned with the MITRE ATT&CK™ framework. ', + name: 'microsoft.m365_defender.alerts.mitreTechniques', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.entityType': { + category: 'microsoft', + description: + 'Entities that have been identified to be part of, or related to, a given alert. The properties values are: User, Ip, Url, File, Process, MailBox, MailMessage, MailCluster, Registry. ', + name: 'microsoft.m365_defender.alerts.entities.entityType', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.accountName': { + category: 'microsoft', + description: 'Account name of the related user. ', + name: 'microsoft.m365_defender.alerts.entities.accountName', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.mailboxDisplayName': { + category: 'microsoft', + description: 'The display name of the related mailbox. ', + name: 'microsoft.m365_defender.alerts.entities.mailboxDisplayName', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.mailboxAddress': { + category: 'microsoft', + description: 'The mail address of the related mailbox. ', + name: 'microsoft.m365_defender.alerts.entities.mailboxAddress', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.clusterBy': { + category: 'microsoft', + description: 'A list of metadata if the entityType is MailCluster. ', + name: 'microsoft.m365_defender.alerts.entities.clusterBy', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.sender': { + category: 'microsoft', + description: 'The sender for the related email message. ', + name: 'microsoft.m365_defender.alerts.entities.sender', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.recipient': { + category: 'microsoft', + description: 'The recipient for the related email message. ', + name: 'microsoft.m365_defender.alerts.entities.recipient', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.subject': { + category: 'microsoft', + description: 'The subject for the related email message. ', + name: 'microsoft.m365_defender.alerts.entities.subject', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.deliveryAction': { + category: 'microsoft', + description: 'The delivery status for the related email message. ', + name: 'microsoft.m365_defender.alerts.entities.deliveryAction', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.securityGroupId': { + category: 'microsoft', + description: 'The Security Group ID for the user related to the email message. ', + name: 'microsoft.m365_defender.alerts.entities.securityGroupId', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.securityGroupName': { + category: 'microsoft', + description: 'The Security Group Name for the user related to the email message. ', + name: 'microsoft.m365_defender.alerts.entities.securityGroupName', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.registryHive': { + category: 'microsoft', + description: + 'Reference to which Hive in registry the event is related to, if eventType is registry. Example: HKEY_LOCAL_MACHINE. ', + name: 'microsoft.m365_defender.alerts.entities.registryHive', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.registryKey': { + category: 'microsoft', + description: 'Reference to the related registry key to the event. ', + name: 'microsoft.m365_defender.alerts.entities.registryKey', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.registryValueType': { + category: 'microsoft', + description: 'Value type of the registry key/value pair related to the event. ', + name: 'microsoft.m365_defender.alerts.entities.registryValueType', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.deviceId': { + category: 'microsoft', + description: 'The unique ID of the device related to the event. ', + name: 'microsoft.m365_defender.alerts.entities.deviceId', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.entities.ipAddress': { + category: 'microsoft', + description: 'The related IP address to the event. ', + name: 'microsoft.m365_defender.alerts.entities.ipAddress', + type: 'keyword', + }, + 'microsoft.m365_defender.alerts.devices': { + category: 'microsoft', + description: 'The devices related to the investigation. ', + name: 'microsoft.m365_defender.alerts.devices', + type: 'flattened', + }, 'misp.attack_pattern.id': { category: 'misp', description: 'Identifier of the threat indicator. ', @@ -22592,6 +26422,166 @@ export const fieldsBeat: BeatFields = { name: 'mssql.log.origin', type: 'keyword', }, + 'mysqlenterprise.audit.class': { + category: 'mysqlenterprise', + description: + 'A string representing the event class. The class defines the type of event, when taken together with the event item that specifies the event subclass. ', + name: 'mysqlenterprise.audit.class', + type: 'keyword', + }, + 'mysqlenterprise.audit.connection_id': { + category: 'mysqlenterprise', + description: + 'An integer representing the client connection identifier. This is the same as the value returned by the CONNECTION_ID() function within the session. ', + name: 'mysqlenterprise.audit.connection_id', + type: 'keyword', + }, + 'mysqlenterprise.audit.id': { + category: 'mysqlenterprise', + description: 'An unsigned integer representing an event ID. ', + name: 'mysqlenterprise.audit.id', + type: 'keyword', + }, + 'mysqlenterprise.audit.connection_data.connection_type': { + category: 'mysqlenterprise', + description: + 'The security state of the connection to the server. Permitted values are tcp/ip (TCP/IP connection established without encryption), ssl (TCP/IP connection established with encryption), socket (Unix socket file connection), named_pipe (Windows named pipe connection), and shared_memory (Windows shared memory connection). ', + name: 'mysqlenterprise.audit.connection_data.connection_type', + type: 'keyword', + }, + 'mysqlenterprise.audit.connection_data.status': { + category: 'mysqlenterprise', + description: + 'An integer representing the command status: 0 for success, nonzero if an error occurred. ', + name: 'mysqlenterprise.audit.connection_data.status', + type: 'long', + }, + 'mysqlenterprise.audit.connection_data.db': { + category: 'mysqlenterprise', + description: + 'A string representing a database name. For connection_data, it is the default database. For table_access_data, it is the table database. ', + name: 'mysqlenterprise.audit.connection_data.db', + type: 'keyword', + }, + 'mysqlenterprise.audit.connection_data.connection_attributes': { + category: 'mysqlenterprise', + description: 'Connection attributes that might be passed by different MySQL Clients. ', + name: 'mysqlenterprise.audit.connection_data.connection_attributes', + type: 'flattened', + }, + 'mysqlenterprise.audit.general_data.command': { + category: 'mysqlenterprise', + description: + 'A string representing the type of instruction that generated the audit event, such as a command that the server received from a client. ', + name: 'mysqlenterprise.audit.general_data.command', + type: 'keyword', + }, + 'mysqlenterprise.audit.general_data.sql_command': { + category: 'mysqlenterprise', + description: 'A string that indicates the SQL statement type. ', + name: 'mysqlenterprise.audit.general_data.sql_command', + type: 'keyword', + }, + 'mysqlenterprise.audit.general_data.query': { + category: 'mysqlenterprise', + description: + 'A string representing the text of an SQL statement. The value can be empty. Long values may be truncated. The string, like the audit log file itself, is written using UTF-8 (up to 4 bytes per character), so the value may be the result of conversion. ', + name: 'mysqlenterprise.audit.general_data.query', + type: 'keyword', + }, + 'mysqlenterprise.audit.general_data.status': { + category: 'mysqlenterprise', + description: + 'An integer representing the command status: 0 for success, nonzero if an error occurred. This is the same as the value of the mysql_errno() C API function. ', + name: 'mysqlenterprise.audit.general_data.status', + type: 'long', + }, + 'mysqlenterprise.audit.login.user': { + category: 'mysqlenterprise', + description: + 'A string representing the information indicating how a client connected to the server. ', + name: 'mysqlenterprise.audit.login.user', + type: 'keyword', + }, + 'mysqlenterprise.audit.login.proxy': { + category: 'mysqlenterprise', + description: + 'A string representing the proxy user. The value is empty if user proxying is not in effect. ', + name: 'mysqlenterprise.audit.login.proxy', + type: 'keyword', + }, + 'mysqlenterprise.audit.shutdown_data.server_id': { + category: 'mysqlenterprise', + description: + 'An integer representing the server ID. This is the same as the value of the server_id system variable. ', + name: 'mysqlenterprise.audit.shutdown_data.server_id', + type: 'keyword', + }, + 'mysqlenterprise.audit.startup_data.server_id': { + category: 'mysqlenterprise', + description: + 'An integer representing the server ID. This is the same as the value of the server_id system variable. ', + name: 'mysqlenterprise.audit.startup_data.server_id', + type: 'keyword', + }, + 'mysqlenterprise.audit.startup_data.mysql_version': { + category: 'mysqlenterprise', + description: + 'An integer representing the server ID. This is the same as the value of the server_id system variable. ', + name: 'mysqlenterprise.audit.startup_data.mysql_version', + type: 'keyword', + }, + 'mysqlenterprise.audit.table_access_data.db': { + category: 'mysqlenterprise', + description: + 'A string representing a database name. For connection_data, it is the default database. For table_access_data, it is the table database. ', + name: 'mysqlenterprise.audit.table_access_data.db', + type: 'keyword', + }, + 'mysqlenterprise.audit.table_access_data.table': { + category: 'mysqlenterprise', + description: 'A string representing a table name. ', + name: 'mysqlenterprise.audit.table_access_data.table', + type: 'keyword', + }, + 'mysqlenterprise.audit.table_access_data.query': { + category: 'mysqlenterprise', + description: + 'A string representing the text of an SQL statement. The value can be empty. Long values may be truncated. The string, like the audit log file itself, is written using UTF-8 (up to 4 bytes per character), so the value may be the result of conversion. ', + name: 'mysqlenterprise.audit.table_access_data.query', + type: 'keyword', + }, + 'mysqlenterprise.audit.table_access_data.sql_command': { + category: 'mysqlenterprise', + description: 'A string that indicates the SQL statement type. ', + name: 'mysqlenterprise.audit.table_access_data.sql_command', + type: 'keyword', + }, + 'mysqlenterprise.audit.account.user': { + category: 'mysqlenterprise', + description: + 'A string representing the user that the server authenticated the client as. This is the user name that the server uses for privilege checking. ', + name: 'mysqlenterprise.audit.account.user', + type: 'keyword', + }, + 'mysqlenterprise.audit.account.host': { + category: 'mysqlenterprise', + description: 'A string representing the client host name. ', + name: 'mysqlenterprise.audit.account.host', + type: 'keyword', + }, + 'mysqlenterprise.audit.login.os': { + category: 'mysqlenterprise', + description: + 'A string representing the external user name used during the authentication process, as set by the plugin used to authenticate the client. ', + name: 'mysqlenterprise.audit.login.os', + type: 'keyword', + }, + 'o365.audit.AADGroupId': { + category: 'o365', + name: 'o365.audit.AADGroupId', + type: 'keyword', + }, 'o365.audit.Actor.ID': { category: 'o365', name: 'o365.audit.Actor.ID', @@ -22697,6 +26687,11 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.Comments', type: 'text', }, + 'o365.audit.CommunicationType': { + category: 'o365', + name: 'o365.audit.CommunicationType', + type: 'keyword', + }, 'o365.audit.CorrelationId': { category: 'o365', name: 'o365.audit.CorrelationId', @@ -22722,11 +26717,21 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.DataType', type: 'keyword', }, + 'o365.audit.DoNotDistributeEvent': { + category: 'o365', + name: 'o365.audit.DoNotDistributeEvent', + type: 'boolean', + }, 'o365.audit.EntityType': { category: 'o365', name: 'o365.audit.EntityType', type: 'keyword', }, + 'o365.audit.ErrorNumber': { + category: 'o365', + name: 'o365.audit.ErrorNumber', + type: 'keyword', + }, 'o365.audit.EventData': { category: 'o365', name: 'o365.audit.EventData', @@ -22752,6 +26757,11 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.ExternalAccess', type: 'keyword', }, + 'o365.audit.FromApp': { + category: 'o365', + name: 'o365.audit.FromApp', + type: 'boolean', + }, 'o365.audit.GroupName': { category: 'o365', name: 'o365.audit.GroupName', @@ -22787,6 +26797,11 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.IntraSystemId', type: 'keyword', }, + 'o365.audit.IsDocLib': { + category: 'o365', + name: 'o365.audit.IsDocLib', + type: 'boolean', + }, 'o365.audit.Item.*': { category: 'o365', name: 'o365.audit.Item.*', @@ -22797,6 +26812,11 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.Item.*.*', type: 'object', }, + 'o365.audit.ItemCount': { + category: 'o365', + name: 'o365.audit.ItemCount', + type: 'long', + }, 'o365.audit.ItemName': { category: 'o365', name: 'o365.audit.ItemName', @@ -22807,11 +26827,36 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.ItemType', type: 'keyword', }, + 'o365.audit.ListBaseTemplateType': { + category: 'o365', + name: 'o365.audit.ListBaseTemplateType', + type: 'keyword', + }, + 'o365.audit.ListBaseType': { + category: 'o365', + name: 'o365.audit.ListBaseType', + type: 'keyword', + }, + 'o365.audit.ListColor': { + category: 'o365', + name: 'o365.audit.ListColor', + type: 'keyword', + }, + 'o365.audit.ListIcon': { + category: 'o365', + name: 'o365.audit.ListIcon', + type: 'keyword', + }, 'o365.audit.ListId': { category: 'o365', name: 'o365.audit.ListId', type: 'keyword', }, + 'o365.audit.ListTitle': { + category: 'o365', + name: 'o365.audit.ListTitle', + type: 'keyword', + }, 'o365.audit.ListItemUniqueId': { category: 'o365', name: 'o365.audit.ListItemUniqueId', @@ -23017,6 +27062,11 @@ export const fieldsBeat: BeatFields = { name: 'o365.audit.TeamGuid', type: 'keyword', }, + 'o365.audit.TemplateTypeId': { + category: 'o365', + name: 'o365.audit.TemplateTypeId', + type: 'keyword', + }, 'o365.audit.UniqueSharingId': { category: 'o365', name: 'o365.audit.UniqueSharingId', @@ -23366,6 +27416,89 @@ export const fieldsBeat: BeatFields = { name: 'okta.request.ip_chain.geographical_context.geolocation', type: 'geo_point', }, + 'oracle.database_audit.status': { + category: 'oracle', + description: 'Database Audit Status. ', + name: 'oracle.database_audit.status', + type: 'keyword', + }, + 'oracle.database_audit.session_id': { + category: 'oracle', + description: 'Indicates the audit session ID number. ', + name: 'oracle.database_audit.session_id', + type: 'keyword', + }, + 'oracle.database_audit.client.terminal': { + category: 'oracle', + description: 'If available, the client terminal type, for example "pty". ', + name: 'oracle.database_audit.client.terminal', + type: 'keyword', + }, + 'oracle.database_audit.client.address': { + category: 'oracle', + description: 'The IP Address or Domain used by the client. ', + name: 'oracle.database_audit.client.address', + type: 'keyword', + }, + 'oracle.database_audit.client.user': { + category: 'oracle', + description: 'The user running the client or connection to the database. ', + name: 'oracle.database_audit.client.user', + type: 'keyword', + }, + 'oracle.database_audit.database.user': { + category: 'oracle', + description: 'The database user used to authenticate. ', + name: 'oracle.database_audit.database.user', + type: 'keyword', + }, + 'oracle.database_audit.privilege': { + category: 'oracle', + description: 'The privilege group related to the database user. ', + name: 'oracle.database_audit.privilege', + type: 'keyword', + }, + 'oracle.database_audit.entry.id': { + category: 'oracle', + description: + 'Indicates the current audit entry number, assigned to each audit trail record. The audit entry.id sequence number is shared between fine-grained audit records and regular audit records. ', + name: 'oracle.database_audit.entry.id', + type: 'keyword', + }, + 'oracle.database_audit.database.host': { + category: 'oracle', + description: 'Client host machine name. ', + name: 'oracle.database_audit.database.host', + type: 'keyword', + }, + 'oracle.database_audit.action': { + category: 'oracle', + description: + 'The action performed during the audit event. This could for example be the raw query. ', + name: 'oracle.database_audit.action', + type: 'keyword', + }, + 'oracle.database_audit.action_number': { + category: 'oracle', + description: + 'Action is a numeric value representing the action the user performed. The corresponding name of the action type is in the AUDIT_ACTIONS table. For example, action 100 refers to LOGON. ', + name: 'oracle.database_audit.action_number', + type: 'keyword', + }, + 'oracle.database_audit.database.id': { + category: 'oracle', + description: + 'Database identifier calculated when the database is created. It corresponds to the DBID column of the V$DATABASE data dictionary view. ', + name: 'oracle.database_audit.database.id', + type: 'keyword', + }, + 'oracle.database_audit.length': { + category: 'oracle', + description: + 'Refers to the total number of bytes used in this audit record. This number includes the trailing newline bytes (\\n), if any, at the end of the audit record. ', + name: 'oracle.database_audit.length', + type: 'long', + }, 'panw.panos.ruleset': { category: 'panw', description: 'Name of the rule that matched this session. ', @@ -23420,6 +27553,12 @@ export const fieldsBeat: BeatFields = { name: 'panw.panos.destination.nat.port', type: 'long', }, + 'panw.panos.endreason': { + category: 'panw', + description: 'The reason a session terminated. ', + name: 'panw.panos.endreason', + type: 'keyword', + }, 'panw.panos.network.pcap_id': { category: 'panw', description: 'Packet capture ID for a threat. ', @@ -23482,6 +27621,16 @@ export const fieldsBeat: BeatFields = { name: 'panw.panos.action', type: 'keyword', }, + 'panw.panos.type': { + category: 'panw', + description: 'Specifies the type of the log', + name: 'panw.panos.type', + }, + 'panw.panos.sub_type': { + category: 'panw', + description: 'Specifies the sub type of the log', + name: 'panw.panos.sub_type', + }, 'rabbitmq.log.pid': { category: 'rabbitmq', description: 'The Erlang process id', @@ -23489,6 +27638,207 @@ export const fieldsBeat: BeatFields = { name: 'rabbitmq.log.pid', type: 'keyword', }, + 'snyk.projects': { + category: 'snyk', + description: 'Array with all related projects objects. ', + name: 'snyk.projects', + type: 'flattened', + }, + 'snyk.related.projects': { + category: 'snyk', + description: "Array of all the related project ID's. ", + name: 'snyk.related.projects', + type: 'keyword', + }, + 'snyk.audit.org_id': { + category: 'snyk', + description: 'ID of the related Organization related to the event. ', + name: 'snyk.audit.org_id', + type: 'keyword', + }, + 'snyk.audit.project_id': { + category: 'snyk', + description: 'ID of the project related to the event. ', + name: 'snyk.audit.project_id', + type: 'keyword', + }, + 'snyk.audit.content': { + category: 'snyk', + description: 'Overview of the content that was changed, both old and new values. ', + name: 'snyk.audit.content', + type: 'flattened', + }, + 'snyk.vulnerabilities.cvss3': { + category: 'snyk', + description: 'CSSv3 scores. ', + name: 'snyk.vulnerabilities.cvss3', + type: 'keyword', + }, + 'snyk.vulnerabilities.disclosure_time': { + category: 'snyk', + description: + 'The time this vulnerability was originally disclosed to the package maintainers. ', + name: 'snyk.vulnerabilities.disclosure_time', + type: 'date', + }, + 'snyk.vulnerabilities.exploit_maturity': { + category: 'snyk', + description: 'The Snyk exploit maturity level. ', + name: 'snyk.vulnerabilities.exploit_maturity', + type: 'keyword', + }, + 'snyk.vulnerabilities.id': { + category: 'snyk', + description: 'The vulnerability reference ID. ', + name: 'snyk.vulnerabilities.id', + type: 'keyword', + }, + 'snyk.vulnerabilities.is_ignored': { + category: 'snyk', + description: 'If the vulnerability report has been ignored. ', + name: 'snyk.vulnerabilities.is_ignored', + type: 'boolean', + }, + 'snyk.vulnerabilities.is_patchable': { + category: 'snyk', + description: 'If vulnerability is fixable by using a Snyk supplied patch. ', + name: 'snyk.vulnerabilities.is_patchable', + type: 'boolean', + }, + 'snyk.vulnerabilities.is_patched': { + category: 'snyk', + description: 'If the vulnerability has been patched. ', + name: 'snyk.vulnerabilities.is_patched', + type: 'boolean', + }, + 'snyk.vulnerabilities.is_pinnable': { + category: 'snyk', + description: 'If the vulnerability is fixable by pinning a transitive dependency. ', + name: 'snyk.vulnerabilities.is_pinnable', + type: 'boolean', + }, + 'snyk.vulnerabilities.is_upgradable': { + category: 'snyk', + description: 'If the vulnerability fixable by upgrading a dependency. ', + name: 'snyk.vulnerabilities.is_upgradable', + type: 'boolean', + }, + 'snyk.vulnerabilities.language': { + category: 'snyk', + description: "The package's programming language. ", + name: 'snyk.vulnerabilities.language', + type: 'keyword', + }, + 'snyk.vulnerabilities.package': { + category: 'snyk', + description: 'The package identifier according to its package manager. ', + name: 'snyk.vulnerabilities.package', + type: 'keyword', + }, + 'snyk.vulnerabilities.package_manager': { + category: 'snyk', + description: 'The package manager. ', + name: 'snyk.vulnerabilities.package_manager', + type: 'keyword', + }, + 'snyk.vulnerabilities.patches': { + category: 'snyk', + description: 'Patches required to resolve the issue created by Snyk. ', + name: 'snyk.vulnerabilities.patches', + type: 'flattened', + }, + 'snyk.vulnerabilities.priority_score': { + category: 'snyk', + description: 'The CVS priority score. ', + name: 'snyk.vulnerabilities.priority_score', + type: 'long', + }, + 'snyk.vulnerabilities.publication_time': { + category: 'snyk', + description: 'The vulnerability publication time. ', + name: 'snyk.vulnerabilities.publication_time', + type: 'date', + }, + 'snyk.vulnerabilities.jira_issue_url': { + category: 'snyk', + description: 'Link to the related Jira issue. ', + name: 'snyk.vulnerabilities.jira_issue_url', + type: 'keyword', + }, + 'snyk.vulnerabilities.original_severity': { + category: 'snyk', + description: 'The original severity of the vulnerability. ', + name: 'snyk.vulnerabilities.original_severity', + type: 'long', + }, + 'snyk.vulnerabilities.reachability': { + category: 'snyk', + description: + 'If the vulnerable function from the library is used in the code scanned. Can either be No Info, Potentially reachable and Reachable. ', + name: 'snyk.vulnerabilities.reachability', + type: 'keyword', + }, + 'snyk.vulnerabilities.title': { + category: 'snyk', + description: 'The issue title. ', + name: 'snyk.vulnerabilities.title', + type: 'keyword', + }, + 'snyk.vulnerabilities.type': { + category: 'snyk', + description: 'The issue type. Can be either "license" or "vulnerability". ', + name: 'snyk.vulnerabilities.type', + type: 'keyword', + }, + 'snyk.vulnerabilities.unique_severities_list': { + category: 'snyk', + description: 'A list of related unique severities. ', + name: 'snyk.vulnerabilities.unique_severities_list', + type: 'keyword', + }, + 'snyk.vulnerabilities.version': { + category: 'snyk', + description: 'The package version this issue is applicable to. ', + name: 'snyk.vulnerabilities.version', + type: 'keyword', + }, + 'snyk.vulnerabilities.introduced_date': { + category: 'snyk', + description: 'The date the vulnerability was initially found. ', + name: 'snyk.vulnerabilities.introduced_date', + type: 'date', + }, + 'snyk.vulnerabilities.is_fixed': { + category: 'snyk', + description: 'If the related vulnerability has been resolved. ', + name: 'snyk.vulnerabilities.is_fixed', + type: 'boolean', + }, + 'snyk.vulnerabilities.credit': { + category: 'snyk', + description: 'Reference to the person that original found the vulnerability. ', + name: 'snyk.vulnerabilities.credit', + type: 'keyword', + }, + 'snyk.vulnerabilities.semver': { + category: 'snyk', + description: + 'One or more semver ranges this issue is applicable to. The format varies according to package manager. ', + name: 'snyk.vulnerabilities.semver', + type: 'flattened', + }, + 'snyk.vulnerabilities.identifiers.alternative': { + category: 'snyk', + description: 'Additional vulnerability identifiers. ', + name: 'snyk.vulnerabilities.identifiers.alternative', + type: 'keyword', + }, + 'snyk.vulnerabilities.identifiers.cwe': { + category: 'snyk', + description: 'CWE vulnerability identifiers. ', + name: 'snyk.vulnerabilities.identifiers.cwe', + type: 'keyword', + }, 'sophos.xg.device': { category: 'sophos', description: 'device ', @@ -24845,16 +29195,17 @@ export const fieldsBeat: BeatFields = { name: 'suricata.eve.http.http_content_type', type: 'keyword', }, - 'suricata.eve.timestamp': { - category: 'suricata', - name: 'suricata.eve.timestamp', - type: 'alias', - }, 'suricata.eve.in_iface': { category: 'suricata', name: 'suricata.eve.in_iface', type: 'keyword', }, + 'suricata.eve.alert.metadata': { + category: 'suricata', + description: 'Metadata about the alert.', + name: 'suricata.eve.alert.metadata', + type: 'flattened', + }, 'suricata.eve.alert.category': { category: 'suricata', name: 'suricata.eve.alert.category', @@ -25600,11 +29951,6 @@ export const fieldsBeat: BeatFields = { name: 'suricata.eve.flow.pkts_toserver', type: 'alias', }, - 'suricata.eve.flow.end': { - category: 'suricata', - name: 'suricata.eve.flow.end', - type: 'date', - }, 'suricata.eve.flow.alerted': { category: 'suricata', name: 'suricata.eve.flow.alerted', @@ -25650,6 +29996,838 @@ export const fieldsBeat: BeatFields = { name: 'suricata.eve.flags', type: 'group', }, + 'threatintel.indicator.first_seen': { + category: 'threatintel', + description: + 'The date and time when intelligence source first reported sighting this indicator. ', + name: 'threatintel.indicator.first_seen', + type: 'keyword', + }, + 'threatintel.indicator.last_seen': { + category: 'threatintel', + description: + 'The date and time when intelligence source last reported sighting this indicator. ', + name: 'threatintel.indicator.last_seen', + type: 'date', + }, + 'threatintel.indicator.sightings': { + category: 'threatintel', + description: 'Number of times this indicator was observed conducting threat activity. ', + name: 'threatintel.indicator.sightings', + type: 'long', + }, + 'threatintel.indicator.type': { + category: 'threatintel', + description: + 'Type of indicator as represented by Cyber Observable in STIX 2.0. Expected values * autonomous-system * artifact * directory * domain-name * email-addr * file * ipv4-addr * ipv6-addr * mac-addr * mutex * process * software * url * user-account * windows-registry-key * x-509-certificate ', + name: 'threatintel.indicator.type', + type: 'keyword', + }, + 'threatintel.indicator.description': { + category: 'threatintel', + description: 'Describes the type of action conducted by the threat. ', + name: 'threatintel.indicator.description', + type: 'keyword', + }, + 'threatintel.indicator.scanner_stats': { + category: 'threatintel', + description: 'Count of AV/EDR vendors that successfully detected malicious file or URL. ', + name: 'threatintel.indicator.scanner_stats', + type: 'long', + }, + 'threatintel.indicator.provider': { + category: 'threatintel', + description: 'Identifies the name of the intelligence provider. ', + name: 'threatintel.indicator.provider', + type: 'keyword', + }, + 'threatintel.indicator.confidence': { + category: 'threatintel', + description: + 'Identifies the confidence rating assigned by the provider using STIX confidence scales. Expected values * Not Specified, None, Low, Medium, High * 0-10 * Admirality Scale (1-6) * DNI Scale (5-95) * WEP Scale (Impossible - Certain) ', + name: 'threatintel.indicator.confidence', + type: 'keyword', + }, + 'threatintel.indicator.module': { + category: 'threatintel', + description: 'Identifies the name of specific module this data is coming from. ', + name: 'threatintel.indicator.module', + type: 'keyword', + }, + 'threatintel.indicator.dataset': { + category: 'threatintel', + description: 'Identifies the name of specific dataset from the intelligence source. ', + name: 'threatintel.indicator.dataset', + type: 'keyword', + }, + 'threatintel.indicator.ip': { + category: 'threatintel', + description: 'Identifies a threat indicator as an IP address (irrespective of direction). ', + name: 'threatintel.indicator.ip', + type: 'ip', + }, + 'threatintel.indicator.domain': { + category: 'threatintel', + description: 'Identifies a threat indicator as a domain (irrespective of direction). ', + name: 'threatintel.indicator.domain', + type: 'keyword', + }, + 'threatintel.indicator.port': { + category: 'threatintel', + description: 'Identifies a threat indicator as a port number (irrespective of direction). ', + name: 'threatintel.indicator.port', + type: 'long', + }, + 'threatintel.indicator.email.address': { + category: 'threatintel', + description: 'Identifies a threat indicator as an email address (irrespective of direction). ', + name: 'threatintel.indicator.email.address', + type: 'keyword', + }, + 'threatintel.indicator.marking.tlp': { + category: 'threatintel', + description: + 'Traffic Light Protocol sharing markings. Expected values are: * White * Green * Amber * Red ', + name: 'threatintel.indicator.marking.tlp', + type: 'keyword', + }, + 'threatintel.indicator.matched.atomic': { + category: 'threatintel', + description: + 'Identifies the atomic indicator that matched a local environment endpoint or network event. ', + name: 'threatintel.indicator.matched.atomic', + type: 'keyword', + }, + 'threatintel.indicator.matched.field': { + category: 'threatintel', + description: + 'Identifies the field of the atomic indicator that matched a local environment endpoint or network event. ', + name: 'threatintel.indicator.matched.field', + type: 'keyword', + }, + 'threatintel.indicator.matched.type': { + category: 'threatintel', + description: + 'Identifies the type of the atomic indicator that matched a local environment endpoint or network event. ', + name: 'threatintel.indicator.matched.type', + type: 'keyword', + }, + 'threatintel.indicator.as.number': { + category: 'threatintel', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'threatintel.indicator.as.number', + type: 'long', + }, + 'threatintel.indicator.as.organization.name': { + category: 'threatintel', + description: 'Organization name.', + example: 'Google LLC', + name: 'threatintel.indicator.as.organization.name', + type: 'keyword', + }, + 'threatintel.indicator.registry.data.strings': { + category: 'threatintel', + description: + 'Content when writing string types. Populated as an array when writing string data to the registry. For single string registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with one string. For sequences of string with REG_MULTI_SZ, this array will be variable length. For numeric data, such as REG_DWORD and REG_QWORD, this should be populated with the decimal representation (e.g `"1"`). ', + example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', + name: 'threatintel.indicator.registry.data.strings', + type: 'keyword', + }, + 'threatintel.indicator.registry.path': { + category: 'threatintel', + description: 'Full path, including hive, key and value', + example: + 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe\\Debugger', + name: 'threatintel.indicator.registry.path', + type: 'keyword', + }, + 'threatintel.indicator.registry.value': { + category: 'threatintel', + description: 'Name of the value written.', + example: 'Debugger', + name: 'threatintel.indicator.registry.value', + type: 'keyword', + }, + 'threatintel.indicator.registry.key': { + category: 'threatintel', + description: 'Registry key value', + name: 'threatintel.indicator.registry.key', + type: 'keyword', + }, + 'threatintel.indicator.geo.geo.city_name': { + category: 'threatintel', + description: 'City name.', + example: 'Montreal', + name: 'threatintel.indicator.geo.geo.city_name', + type: 'keyword', + }, + 'threatintel.indicator.geo.geo.country_iso_code': { + category: 'threatintel', + description: 'Country ISO code.', + example: 'CA', + name: 'threatintel.indicator.geo.geo.country_iso_code', + type: 'keyword', + }, + 'threatintel.indicator.geo.geo.country_name': { + category: 'threatintel', + description: 'Country name.', + example: 'Canada', + name: 'threatintel.indicator.geo.geo.country_name', + type: 'keyword', + }, + 'threatintel.indicator.geo.geo.location': { + category: 'threatintel', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'threatintel.indicator.geo.geo.location', + type: 'geo_point', + }, + 'threatintel.indicator.geo.geo.region_iso_code': { + category: 'threatintel', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'threatintel.indicator.geo.geo.region_iso_code', + type: 'keyword', + }, + 'threatintel.indicator.geo.geo.region_name': { + category: 'threatintel', + description: 'Region name.', + example: 'Quebec', + name: 'threatintel.indicator.geo.geo.region_name', + type: 'keyword', + }, + 'threatintel.indicator.file.pe.imphash': { + category: 'threatintel', + description: + 'A hash of the imports in a PE file. An imphash -- or import hash -- can be used to fingerprint binaries even after recompilation or other code-level transformations have occurred, which would change more traditional hash values. Learn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', + example: '0c6803c4e922103c4dca5963aad36ddf', + name: 'threatintel.indicator.file.pe.imphash', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.tlsh': { + category: 'threatintel', + description: "The file's import tlsh, if available. ", + name: 'threatintel.indicator.file.hash.tlsh', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.ssdeep': { + category: 'threatintel', + description: "The file's ssdeep hash, if available. ", + name: 'threatintel.indicator.file.hash.ssdeep', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.md5': { + category: 'threatintel', + description: "The file's md5 hash, if available. ", + name: 'threatintel.indicator.file.hash.md5', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.sha1': { + category: 'threatintel', + description: "The file's sha1 hash, if available. ", + name: 'threatintel.indicator.file.hash.sha1', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.sha256': { + category: 'threatintel', + description: "The file's sha256 hash, if available. ", + name: 'threatintel.indicator.file.hash.sha256', + type: 'keyword', + }, + 'threatintel.indicator.file.hash.sha512': { + category: 'threatintel', + description: "The file's sha512 hash, if available. ", + name: 'threatintel.indicator.file.hash.sha512', + type: 'keyword', + }, + 'threatintel.indicator.file.type': { + category: 'threatintel', + description: 'The file type ', + name: 'threatintel.indicator.file.type', + type: 'keyword', + }, + 'threatintel.indicator.file.size': { + category: 'threatintel', + description: "The file's total size ", + name: 'threatintel.indicator.file.size', + type: 'long', + }, + 'threatintel.indicator.file.name': { + category: 'threatintel', + description: "The file's name ", + name: 'threatintel.indicator.file.name', + type: 'keyword', + }, + 'threatintel.indicator.url.domain': { + category: 'threatintel', + description: 'Domain of the url, such as "www.elastic.co". ', + name: 'threatintel.indicator.url.domain', + type: 'keyword', + }, + 'threatintel.indicator.url.extension': { + category: 'threatintel', + description: 'The field contains the file extension from the original request ', + name: 'threatintel.indicator.url.extension', + type: 'keyword', + }, + 'threatintel.indicator.url.fragment': { + category: 'threatintel', + description: 'Portion of the url after the `#`, such as "top". ', + name: 'threatintel.indicator.url.fragment', + type: 'keyword', + }, + 'threatintel.indicator.url.full': { + category: 'threatintel', + description: + 'If full URLs are important to your use case, they should be stored in `url.full`, whether this field is reconstructed or present in the event source. ', + name: 'threatintel.indicator.url.full', + type: 'keyword', + }, + 'threatintel.indicator.url.original': { + category: 'threatintel', + description: + 'Unmodified original url as seen in the event source. Note that in network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. This field is meant to represent the URL as it was observed, complete or not. ', + name: 'threatintel.indicator.url.original', + type: 'keyword', + }, + 'threatintel.indicator.url.password': { + category: 'threatintel', + description: 'Password of the request. ', + name: 'threatintel.indicator.url.password', + type: 'keyword', + }, + 'threatintel.indicator.url.path': { + category: 'threatintel', + description: 'Path of the request, such as "/search". ', + name: 'threatintel.indicator.url.path', + type: 'keyword', + }, + 'threatintel.indicator.url.port': { + category: 'threatintel', + description: 'Port of the request, such as 443. ', + name: 'threatintel.indicator.url.port', + type: 'long', + format: 'string', + }, + 'threatintel.indicator.url.query': { + category: 'threatintel', + description: + 'The query field describes the query string of the request, such as "q=elasticsearch". The `?` is excluded from the query string. If a URL contains no `?`, there is no query field. If there is a `?` but no query, the query field exists with an empty string. The `exists` query can be used to differentiate between the two cases. ', + name: 'threatintel.indicator.url.query', + type: 'keyword', + }, + 'threatintel.indicator.url.registered_domain': { + category: 'threatintel', + description: + 'The highest registered url domain, stripped of the subdomain. For example, the registered domain for "foo.example.com" is "example.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". ', + name: 'threatintel.indicator.url.registered_domain', + type: 'keyword', + }, + 'threatintel.indicator.url.scheme': { + category: 'threatintel', + description: 'Scheme of the request, such as "https". ', + name: 'threatintel.indicator.url.scheme', + type: 'keyword', + }, + 'threatintel.indicator.url.subdomain': { + category: 'threatintel', + description: + 'The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. For example the subdomain portion of "www.east.mydomain.co.uk" is "east". If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period. ', + name: 'threatintel.indicator.url.subdomain', + type: 'keyword', + }, + 'threatintel.indicator.url.top_level_domain': { + category: 'threatintel', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". ', + name: 'threatintel.indicator.url.top_level_domain', + type: 'keyword', + }, + 'threatintel.indicator.url.username': { + category: 'threatintel', + description: 'Username of the request. ', + name: 'threatintel.indicator.url.username', + type: 'keyword', + }, + 'threatintel.indicator.x509.serial_number': { + category: 'threatintel', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters.', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'threatintel.indicator.x509.serial_number', + type: 'keyword', + }, + 'threatintel.indicator.x509.issuer': { + category: 'threatintel', + description: + 'Name of issuing certificate authority. Could be either Distinguished Name (DN) or Common Name (CN), depending on source.', + example: 'C=US, O=Example Inc, OU=www.example.com, CN=Example SHA2 High Assurance Server CA', + name: 'threatintel.indicator.x509.issuer', + type: 'keyword', + }, + 'threatintel.indicator.x509.subject': { + category: 'threatintel', + description: + 'Name of the certificate subject entity. Could be either Distinguished Name (DN) or Common Name (CN), depending on source.', + example: 'C=US, ST=California, L=San Francisco, O=Example, Inc., CN=shared.global.example.net', + name: 'threatintel.indicator.x509.subject', + type: 'keyword', + }, + 'threatintel.indicator.x509.alternative_names': { + category: 'threatintel', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'threatintel.indicator.x509.alternative_names', + type: 'keyword', + }, + 'threatintel.abusemalware.file_type': { + category: 'threatintel', + description: 'File type guessed by URLhaus. ', + name: 'threatintel.abusemalware.file_type', + type: 'keyword', + }, + 'threatintel.abusemalware.signature': { + category: 'threatintel', + description: 'Malware familiy. ', + name: 'threatintel.abusemalware.signature', + type: 'keyword', + }, + 'threatintel.abusemalware.urlhaus_download': { + category: 'threatintel', + description: 'Location (URL) where you can download a copy of this file. ', + name: 'threatintel.abusemalware.urlhaus_download', + type: 'keyword', + }, + 'threatintel.abusemalware.virustotal.result': { + category: 'threatintel', + description: 'AV detection ration. ', + name: 'threatintel.abusemalware.virustotal.result', + type: 'keyword', + }, + 'threatintel.abusemalware.virustotal.percent': { + category: 'threatintel', + description: 'AV detection in percent. ', + name: 'threatintel.abusemalware.virustotal.percent', + type: 'float', + }, + 'threatintel.abusemalware.virustotal.link': { + category: 'threatintel', + description: 'Link to the Virustotal report. ', + name: 'threatintel.abusemalware.virustotal.link', + type: 'keyword', + }, + 'threatintel.abuseurl.id': { + category: 'threatintel', + description: 'The ID of the url. ', + name: 'threatintel.abuseurl.id', + type: 'keyword', + }, + 'threatintel.abuseurl.urlhaus_reference': { + category: 'threatintel', + description: 'Link to URLhaus entry. ', + name: 'threatintel.abuseurl.urlhaus_reference', + type: 'keyword', + }, + 'threatintel.abuseurl.url_status': { + category: 'threatintel', + description: + 'The current status of the URL. Possible values are: online, offline and unknown. ', + name: 'threatintel.abuseurl.url_status', + type: 'keyword', + }, + 'threatintel.abuseurl.threat': { + category: 'threatintel', + description: 'The threat corresponding to this malware URL. ', + name: 'threatintel.abuseurl.threat', + type: 'keyword', + }, + 'threatintel.abuseurl.blacklists.surbl': { + category: 'threatintel', + description: 'SURBL blacklist status. Possible values are: listed and not_listed ', + name: 'threatintel.abuseurl.blacklists.surbl', + type: 'keyword', + }, + 'threatintel.abuseurl.blacklists.spamhaus_dbl': { + category: 'threatintel', + description: 'Spamhaus DBL blacklist status. ', + name: 'threatintel.abuseurl.blacklists.spamhaus_dbl', + type: 'keyword', + }, + 'threatintel.abuseurl.reporter': { + category: 'threatintel', + description: + 'The Twitter handle of the reporter that has reported this malware URL (or anonymous). ', + name: 'threatintel.abuseurl.reporter', + type: 'keyword', + }, + 'threatintel.abuseurl.larted': { + category: 'threatintel', + description: + 'Indicates whether the malware URL has been reported to the hosting provider (true or false) ', + name: 'threatintel.abuseurl.larted', + type: 'boolean', + }, + 'threatintel.abuseurl.tags': { + category: 'threatintel', + description: 'A list of tags associated with the queried malware URL ', + name: 'threatintel.abuseurl.tags', + type: 'keyword', + }, + 'threatintel.anomali.id': { + category: 'threatintel', + description: 'The ID of the indicator. ', + name: 'threatintel.anomali.id', + type: 'keyword', + }, + 'threatintel.anomali.name': { + category: 'threatintel', + description: 'The name of the indicator. ', + name: 'threatintel.anomali.name', + type: 'keyword', + }, + 'threatintel.anomali.pattern': { + category: 'threatintel', + description: 'The pattern ID of the indicator. ', + name: 'threatintel.anomali.pattern', + type: 'keyword', + }, + 'threatintel.anomali.valid_from': { + category: 'threatintel', + description: 'When the indicator was first found or is considered valid. ', + name: 'threatintel.anomali.valid_from', + type: 'date', + }, + 'threatintel.anomali.modified': { + category: 'threatintel', + description: 'When the indicator was last modified ', + name: 'threatintel.anomali.modified', + type: 'date', + }, + 'threatintel.anomali.labels': { + category: 'threatintel', + description: 'The labels related to the indicator ', + name: 'threatintel.anomali.labels', + type: 'keyword', + }, + 'threatintel.anomali.indicator': { + category: 'threatintel', + description: + 'The value of the indicator, for example if the type is domain, this would be the value. ', + name: 'threatintel.anomali.indicator', + type: 'keyword', + }, + 'threatintel.anomali.description': { + category: 'threatintel', + description: 'A description of the indicator. ', + name: 'threatintel.anomali.description', + type: 'keyword', + }, + 'threatintel.anomali.title': { + category: 'threatintel', + description: 'Title describing the indicator. ', + name: 'threatintel.anomali.title', + type: 'keyword', + }, + 'threatintel.anomali.content': { + category: 'threatintel', + description: 'Extra text or descriptive content related to the indicator. ', + name: 'threatintel.anomali.content', + type: 'keyword', + }, + 'threatintel.anomali.type': { + category: 'threatintel', + description: 'The indicator type, can for example be "domain, email, FileHash-SHA256". ', + name: 'threatintel.anomali.type', + type: 'keyword', + }, + 'threatintel.anomali.object_marking_refs': { + category: 'threatintel', + description: 'The STIX reference object. ', + name: 'threatintel.anomali.object_marking_refs', + type: 'keyword', + }, + 'threatintel.misp.id': { + category: 'threatintel', + description: 'Attribute ID. ', + name: 'threatintel.misp.id', + type: 'keyword', + }, + 'threatintel.misp.orgc_id': { + category: 'threatintel', + description: 'Organization Community ID of the event. ', + name: 'threatintel.misp.orgc_id', + type: 'keyword', + }, + 'threatintel.misp.org_id': { + category: 'threatintel', + description: 'Organization ID of the event. ', + name: 'threatintel.misp.org_id', + type: 'keyword', + }, + 'threatintel.misp.threat_level_id': { + category: 'threatintel', + description: 'Threat level from 5 to 1, where 1 is the most critical. ', + name: 'threatintel.misp.threat_level_id', + type: 'long', + }, + 'threatintel.misp.info': { + category: 'threatintel', + description: 'Additional text or information related to the event. ', + name: 'threatintel.misp.info', + type: 'keyword', + }, + 'threatintel.misp.published': { + category: 'threatintel', + description: 'When the event was published. ', + name: 'threatintel.misp.published', + type: 'boolean', + }, + 'threatintel.misp.uuid': { + category: 'threatintel', + description: 'The UUID of the event object. ', + name: 'threatintel.misp.uuid', + type: 'keyword', + }, + 'threatintel.misp.date': { + category: 'threatintel', + description: 'The date of when the event object was created. ', + name: 'threatintel.misp.date', + type: 'date', + }, + 'threatintel.misp.attribute_count': { + category: 'threatintel', + description: 'How many attributes are included in a single event object. ', + name: 'threatintel.misp.attribute_count', + type: 'long', + }, + 'threatintel.misp.timestamp': { + category: 'threatintel', + description: 'The timestamp of when the event object was created. ', + name: 'threatintel.misp.timestamp', + type: 'date', + }, + 'threatintel.misp.distribution': { + category: 'threatintel', + description: 'Distribution type related to MISP. ', + name: 'threatintel.misp.distribution', + type: 'keyword', + }, + 'threatintel.misp.proposal_email_lock': { + category: 'threatintel', + description: 'Settings configured on MISP for email lock on this event object. ', + name: 'threatintel.misp.proposal_email_lock', + type: 'boolean', + }, + 'threatintel.misp.locked': { + category: 'threatintel', + description: 'If the current MISP event object is locked or not. ', + name: 'threatintel.misp.locked', + type: 'boolean', + }, + 'threatintel.misp.publish_timestamp': { + category: 'threatintel', + description: 'At what time the event object was published ', + name: 'threatintel.misp.publish_timestamp', + type: 'date', + }, + 'threatintel.misp.sharing_group_id': { + category: 'threatintel', + description: 'The ID of the grouped events or sources of the event. ', + name: 'threatintel.misp.sharing_group_id', + type: 'keyword', + }, + 'threatintel.misp.disable_correlation': { + category: 'threatintel', + description: 'If correlation is disabled on the MISP event object. ', + name: 'threatintel.misp.disable_correlation', + type: 'boolean', + }, + 'threatintel.misp.extends_uuid': { + category: 'threatintel', + description: 'The UUID of the event object it might extend. ', + name: 'threatintel.misp.extends_uuid', + type: 'keyword', + }, + 'threatintel.misp.org.id': { + category: 'threatintel', + description: 'The organization ID related to the event object. ', + name: 'threatintel.misp.org.id', + type: 'keyword', + }, + 'threatintel.misp.org.name': { + category: 'threatintel', + description: 'The organization name related to the event object. ', + name: 'threatintel.misp.org.name', + type: 'keyword', + }, + 'threatintel.misp.org.uuid': { + category: 'threatintel', + description: 'The UUID of the organization related to the event object. ', + name: 'threatintel.misp.org.uuid', + type: 'keyword', + }, + 'threatintel.misp.org.local': { + category: 'threatintel', + description: 'If the event object is local or from a remote source. ', + name: 'threatintel.misp.org.local', + type: 'boolean', + }, + 'threatintel.misp.orgc.id': { + category: 'threatintel', + description: 'The Organization Community ID in which the event object was reported from. ', + name: 'threatintel.misp.orgc.id', + type: 'keyword', + }, + 'threatintel.misp.orgc.name': { + category: 'threatintel', + description: 'The Organization Community name in which the event object was reported from. ', + name: 'threatintel.misp.orgc.name', + type: 'keyword', + }, + 'threatintel.misp.orgc.uuid': { + category: 'threatintel', + description: 'The Organization Community UUID in which the event object was reported from. ', + name: 'threatintel.misp.orgc.uuid', + type: 'keyword', + }, + 'threatintel.misp.orgc.local': { + category: 'threatintel', + description: 'If the Organization Community was local or synced from a remote source. ', + name: 'threatintel.misp.orgc.local', + type: 'boolean', + }, + 'threatintel.misp.attribute.id': { + category: 'threatintel', + description: 'The ID of the attribute related to the event object. ', + name: 'threatintel.misp.attribute.id', + type: 'keyword', + }, + 'threatintel.misp.attribute.type': { + category: 'threatintel', + description: + 'The type of the attribute related to the event object. For example email, ipv4, sha1 and such. ', + name: 'threatintel.misp.attribute.type', + type: 'keyword', + }, + 'threatintel.misp.attribute.category': { + category: 'threatintel', + description: + 'The category of the attribute related to the event object. For example "Network Activity". ', + name: 'threatintel.misp.attribute.category', + type: 'keyword', + }, + 'threatintel.misp.attribute.to_ids': { + category: 'threatintel', + description: 'If the attribute should be automatically synced with an IDS. ', + name: 'threatintel.misp.attribute.to_ids', + type: 'boolean', + }, + 'threatintel.misp.attribute.uuid': { + category: 'threatintel', + description: 'The UUID of the attribute related to the event. ', + name: 'threatintel.misp.attribute.uuid', + type: 'keyword', + }, + 'threatintel.misp.attribute.event_id': { + category: 'threatintel', + description: 'The local event ID of the attribute related to the event. ', + name: 'threatintel.misp.attribute.event_id', + type: 'keyword', + }, + 'threatintel.misp.attribute.distribution': { + category: 'threatintel', + description: 'How the attribute has been distributed, represented by integer numbers. ', + name: 'threatintel.misp.attribute.distribution', + type: 'long', + }, + 'threatintel.misp.attribute.timestamp': { + category: 'threatintel', + description: 'The timestamp in which the attribute was attached to the event object. ', + name: 'threatintel.misp.attribute.timestamp', + type: 'date', + }, + 'threatintel.misp.attribute.comment': { + category: 'threatintel', + description: 'Comments made to the attribute itself. ', + name: 'threatintel.misp.attribute.comment', + type: 'keyword', + }, + 'threatintel.misp.attribute.sharing_group_id': { + category: 'threatintel', + description: 'The group ID of the sharing group related to the specific attribute. ', + name: 'threatintel.misp.attribute.sharing_group_id', + type: 'keyword', + }, + 'threatintel.misp.attribute.deleted': { + category: 'threatintel', + description: 'If the attribute has been removed from the event object. ', + name: 'threatintel.misp.attribute.deleted', + type: 'boolean', + }, + 'threatintel.misp.attribute.disable_correlation': { + category: 'threatintel', + description: 'If correlation has been enabled on the attribute related to the event object. ', + name: 'threatintel.misp.attribute.disable_correlation', + type: 'boolean', + }, + 'threatintel.misp.attribute.object_id': { + category: 'threatintel', + description: 'The ID of the Object in which the attribute is attached. ', + name: 'threatintel.misp.attribute.object_id', + type: 'keyword', + }, + 'threatintel.misp.attribute.object_relation': { + category: 'threatintel', + description: 'The type of relation the attribute has with the event object itself. ', + name: 'threatintel.misp.attribute.object_relation', + type: 'keyword', + }, + 'threatintel.misp.attribute.value': { + category: 'threatintel', + description: 'The value of the attribute, depending on the type like "url, sha1, email-src". ', + name: 'threatintel.misp.attribute.value', + type: 'keyword', + }, + 'threatintel.otx.id': { + category: 'threatintel', + description: 'The ID of the indicator. ', + name: 'threatintel.otx.id', + type: 'keyword', + }, + 'threatintel.otx.indicator': { + category: 'threatintel', + description: + 'The value of the indicator, for example if the type is domain, this would be the value. ', + name: 'threatintel.otx.indicator', + type: 'keyword', + }, + 'threatintel.otx.description': { + category: 'threatintel', + description: 'A description of the indicator. ', + name: 'threatintel.otx.description', + type: 'keyword', + }, + 'threatintel.otx.title': { + category: 'threatintel', + description: 'Title describing the indicator. ', + name: 'threatintel.otx.title', + type: 'keyword', + }, + 'threatintel.otx.content': { + category: 'threatintel', + description: 'Extra text or descriptive content related to the indicator. ', + name: 'threatintel.otx.content', + type: 'keyword', + }, + 'threatintel.otx.type': { + category: 'threatintel', + description: 'The indicator type, can for example be "domain, email, FileHash-SHA256". ', + name: 'threatintel.otx.type', + type: 'keyword', + }, 'zeek.session_id': { category: 'zeek', description: 'A unique identifier of the session ', @@ -27358,6 +32536,42 @@ export const fieldsBeat: BeatFields = { name: 'zeek.rfb.height', type: 'integer', }, + 'zeek.signature.note': { + category: 'zeek', + description: 'Notice associated with signature event. ', + name: 'zeek.signature.note', + type: 'keyword', + }, + 'zeek.signature.sig_id': { + category: 'zeek', + description: 'The name of the signature that matched. ', + name: 'zeek.signature.sig_id', + type: 'keyword', + }, + 'zeek.signature.event_msg': { + category: 'zeek', + description: 'A more descriptive message of the signature-matching event. ', + name: 'zeek.signature.event_msg', + type: 'keyword', + }, + 'zeek.signature.sub_msg': { + category: 'zeek', + description: 'Extracted payload data or extra message. ', + name: 'zeek.signature.sub_msg', + type: 'keyword', + }, + 'zeek.signature.sig_count': { + category: 'zeek', + description: 'Number of sigs, usually from summary count. ', + name: 'zeek.signature.sig_count', + type: 'integer', + }, + 'zeek.signature.host_count': { + category: 'zeek', + description: 'Number of hosts, from a summary count. ', + name: 'zeek.signature.host_count', + type: 'integer', + }, 'zeek.sip.transaction_depth': { category: 'zeek', description: @@ -28650,22 +33864,965 @@ export const fieldsBeat: BeatFields = { name: 'zeek.x509.log_cert', type: 'boolean', }, - 'awscloudwatch.log_group': { - category: 'awscloudwatch', + 'zoom.master_account_id': { + category: 'zoom', + description: 'Master Account related to a specific Sub Account ', + name: 'zoom.master_account_id', + type: 'keyword', + }, + 'zoom.sub_account_id': { + category: 'zoom', + description: 'Related Sub Account ', + name: 'zoom.sub_account_id', + type: 'keyword', + }, + 'zoom.operator_id': { + category: 'zoom', + description: 'UserID that triggered the event ', + name: 'zoom.operator_id', + type: 'keyword', + }, + 'zoom.operator': { + category: 'zoom', + description: 'Username/Email related to the user that triggered the event ', + name: 'zoom.operator', + type: 'keyword', + }, + 'zoom.account_id': { + category: 'zoom', + description: 'Related accountID to the event ', + name: 'zoom.account_id', + type: 'keyword', + }, + 'zoom.timestamp': { + category: 'zoom', + description: 'Timestamp related to the event ', + name: 'zoom.timestamp', + type: 'date', + }, + 'zoom.creation_type': { + category: 'zoom', + description: 'Creation type ', + name: 'zoom.creation_type', + type: 'keyword', + }, + 'zoom.account.owner_id': { + category: 'zoom', + description: 'UserID of the user whose sub account was created/disassociated ', + name: 'zoom.account.owner_id', + type: 'keyword', + }, + 'zoom.account.email': { + category: 'zoom', + description: 'Email related to the user the action was performed on ', + name: 'zoom.account.email', + type: 'keyword', + }, + 'zoom.account.owner_email': { + category: 'zoom', + description: 'Email of the user whose sub account was created/disassociated ', + name: 'zoom.account.owner_email', + type: 'keyword', + }, + 'zoom.account.account_name': { + category: 'zoom', + description: 'When an account name is updated, this is the new value set ', + name: 'zoom.account.account_name', + type: 'keyword', + }, + 'zoom.account.account_alias': { + category: 'zoom', + description: 'When an account alias is updated, this is the new value set ', + name: 'zoom.account.account_alias', + type: 'keyword', + }, + 'zoom.account.account_support_name': { + category: 'zoom', + description: 'When an account support_name is updated, this is the new value set ', + name: 'zoom.account.account_support_name', + type: 'keyword', + }, + 'zoom.account.account_support_email': { + category: 'zoom', + description: 'When an account support_email is updated, this is the new value set ', + name: 'zoom.account.account_support_email', + type: 'keyword', + }, + 'zoom.chat_channel.name': { + category: 'zoom', + description: 'The name of the channel that has been added/modified/deleted ', + name: 'zoom.chat_channel.name', + type: 'keyword', + }, + 'zoom.chat_channel.id': { + category: 'zoom', + description: 'The ID of the channel that has been added/modified/deleted ', + name: 'zoom.chat_channel.id', + type: 'keyword', + }, + 'zoom.chat_channel.type': { + category: 'zoom', + description: + 'Type of channel related to the event. Can be 1(Invite-Only), 2(Private) or 3(Public) ', + name: 'zoom.chat_channel.type', + type: 'keyword', + }, + 'zoom.chat_message.id': { + category: 'zoom', + description: 'Unique ID of the related chat message ', + name: 'zoom.chat_message.id', + type: 'keyword', + }, + 'zoom.chat_message.type': { + category: 'zoom', + description: 'Type of message, can be either "to_contact" or "to_channel" ', + name: 'zoom.chat_message.type', + type: 'keyword', + }, + 'zoom.chat_message.session_id': { + category: 'zoom', + description: 'SessionID for the channel related to the message ', + name: 'zoom.chat_message.session_id', + type: 'keyword', + }, + 'zoom.chat_message.contact_email': { + category: 'zoom', + description: 'Email address related to the user sending the message ', + name: 'zoom.chat_message.contact_email', + type: 'keyword', + }, + 'zoom.chat_message.contact_id': { + category: 'zoom', + description: 'UserID belonging to the user receiving a message ', + name: 'zoom.chat_message.contact_id', + type: 'keyword', + }, + 'zoom.chat_message.channel_id': { + category: 'zoom', + description: 'ChannelID related to the message ', + name: 'zoom.chat_message.channel_id', + type: 'keyword', + }, + 'zoom.chat_message.channel_name': { + category: 'zoom', + description: 'Channel name related to the message ', + name: 'zoom.chat_message.channel_name', + type: 'keyword', + }, + 'zoom.chat_message.message': { + category: 'zoom', + description: 'A string containing the full message that was sent ', + name: 'zoom.chat_message.message', + type: 'keyword', + }, + 'zoom.meeting.id': { + category: 'zoom', + description: 'Unique ID of the related meeting ', + name: 'zoom.meeting.id', + type: 'keyword', + }, + 'zoom.meeting.uuid': { + category: 'zoom', + description: 'The UUID of the related meeting ', + name: 'zoom.meeting.uuid', + type: 'keyword', + }, + 'zoom.meeting.host_id': { + category: 'zoom', + description: 'The UserID of the configured meeting host ', + name: 'zoom.meeting.host_id', + type: 'keyword', + }, + 'zoom.meeting.topic': { + category: 'zoom', + description: 'Topic of the related meeting ', + name: 'zoom.meeting.topic', + type: 'keyword', + }, + 'zoom.meeting.type': { + category: 'zoom', + description: 'Type of meeting created ', + name: 'zoom.meeting.type', + type: 'keyword', + }, + 'zoom.meeting.start_time': { + category: 'zoom', + description: 'Date and time the meeting started ', + name: 'zoom.meeting.start_time', + type: 'date', + }, + 'zoom.meeting.timezone': { + category: 'zoom', + description: 'Which timezone is used for the meeting timestamps ', + name: 'zoom.meeting.timezone', + type: 'keyword', + }, + 'zoom.meeting.duration': { + category: 'zoom', + description: 'The duration of a meeting in minutes ', + name: 'zoom.meeting.duration', + type: 'long', + }, + 'zoom.meeting.issues': { + category: 'zoom', + description: + 'When a user reports an issue with the meeting, for example: "Unstable audio quality" ', + name: 'zoom.meeting.issues', + type: 'keyword', + }, + 'zoom.meeting.password': { + category: 'zoom', + description: 'Password related to the meeting ', + name: 'zoom.meeting.password', + type: 'keyword', + }, + 'zoom.phone.id': { + category: 'zoom', + description: 'Unique ID for the phone or conversation ', + name: 'zoom.phone.id', + type: 'keyword', + }, + 'zoom.phone.user_id': { + category: 'zoom', + description: 'UserID for the phone owner related to a Call Log being completed ', + name: 'zoom.phone.user_id', + type: 'keyword', + }, + 'zoom.phone.download_url': { + category: 'zoom', + description: 'Download URL for the voicemail ', + name: 'zoom.phone.download_url', + type: 'keyword', + }, + 'zoom.phone.ringing_start_time': { + category: 'zoom', + description: 'The timestamp when a ringtone was established to the callee ', + name: 'zoom.phone.ringing_start_time', + type: 'date', + }, + 'zoom.phone.connected_start_time': { + category: 'zoom', + description: 'The date and time when a ringtone was established to the callee ', + name: 'zoom.phone.connected_start_time', + type: 'date', + }, + 'zoom.phone.answer_start_time': { + category: 'zoom', + description: 'The date and time when the call was answered ', + name: 'zoom.phone.answer_start_time', + type: 'date', + }, + 'zoom.phone.call_end_time': { + category: 'zoom', + description: 'The date and time when the call ended ', + name: 'zoom.phone.call_end_time', + type: 'date', + }, + 'zoom.phone.call_id': { + category: 'zoom', + description: 'Unique ID of the related call ', + name: 'zoom.phone.call_id', + type: 'keyword', + }, + 'zoom.phone.duration': { + category: 'zoom', + description: 'Duration of a voicemail in minutes ', + name: 'zoom.phone.duration', + type: 'long', + }, + 'zoom.phone.caller.id': { + category: 'zoom', + description: 'UserID of the caller related to the voicemail/call ', + name: 'zoom.phone.caller.id', + type: 'keyword', + }, + 'zoom.phone.caller.user_id': { + category: 'zoom', + description: 'UserID of the person which initiated the call ', + name: 'zoom.phone.caller.user_id', + type: 'keyword', + }, + 'zoom.phone.caller.number_type': { + category: 'zoom', + description: 'The type of number, can be 1(Internal) or 2(External) ', + name: 'zoom.phone.caller.number_type', + type: 'keyword', + }, + 'zoom.phone.caller.name': { + category: 'zoom', + description: 'The name of the related callee ', + name: 'zoom.phone.caller.name', + type: 'keyword', + }, + 'zoom.phone.caller.phone_number': { + category: 'zoom', + description: 'Phone Number of the caller related to the call ', + name: 'zoom.phone.caller.phone_number', + type: 'keyword', + }, + 'zoom.phone.caller.extension_type': { + category: 'zoom', + description: + 'Extension type of the caller number, can be user, callQueue, autoReceptionist or shareLineGroup ', + name: 'zoom.phone.caller.extension_type', + type: 'keyword', + }, + 'zoom.phone.caller.extension_number': { + category: 'zoom', + description: 'Extension number of the caller ', + name: 'zoom.phone.caller.extension_number', + type: 'keyword', + }, + 'zoom.phone.caller.timezone': { + category: 'zoom', + description: 'Timezone of the caller ', + name: 'zoom.phone.caller.timezone', + type: 'keyword', + }, + 'zoom.phone.caller.device_type': { + category: 'zoom', + description: 'Device type used by the caller ', + name: 'zoom.phone.caller.device_type', + type: 'keyword', + }, + 'zoom.phone.callee.id': { + category: 'zoom', + description: 'UserID of the callee related to the voicemail/call ', + name: 'zoom.phone.callee.id', + type: 'keyword', + }, + 'zoom.phone.callee.user_id': { + category: 'zoom', + description: 'UserID of the related callee of a voicemail/call ', + name: 'zoom.phone.callee.user_id', + type: 'keyword', + }, + 'zoom.phone.callee.name': { + category: 'zoom', + description: 'The name of the related callee ', + name: 'zoom.phone.callee.name', + type: 'keyword', + }, + 'zoom.phone.callee.number_type': { + category: 'zoom', + description: 'The type of number, can be 1(Internal) or 2(External) ', + name: 'zoom.phone.callee.number_type', + type: 'keyword', + }, + 'zoom.phone.callee.phone_number': { + category: 'zoom', + description: 'Phone Number of the callee related to the call ', + name: 'zoom.phone.callee.phone_number', + type: 'keyword', + }, + 'zoom.phone.callee.extension_type': { + category: 'zoom', + description: + 'Extension type of the callee number, can be user, callQueue, autoReceptionist or shareLineGroup ', + name: 'zoom.phone.callee.extension_type', + type: 'keyword', + }, + 'zoom.phone.callee.extension_number': { + category: 'zoom', + description: 'Extension number of the callee related to the call ', + name: 'zoom.phone.callee.extension_number', + type: 'keyword', + }, + 'zoom.phone.callee.timezone': { + category: 'zoom', + description: 'Timezone of the callee related to the call ', + name: 'zoom.phone.callee.timezone', + type: 'keyword', + }, + 'zoom.phone.callee.device_type': { + category: 'zoom', + description: 'Device type used by the callee related to the call ', + name: 'zoom.phone.callee.device_type', + type: 'keyword', + }, + 'zoom.phone.date_time': { + category: 'zoom', + description: 'Date and time of the related phone event ', + name: 'zoom.phone.date_time', + type: 'date', + }, + 'zoom.recording.id': { + category: 'zoom', + description: 'Unique ID of the related recording ', + name: 'zoom.recording.id', + type: 'keyword', + }, + 'zoom.recording.uuid': { + category: 'zoom', + description: 'UUID of the related recording ', + name: 'zoom.recording.uuid', + type: 'keyword', + }, + 'zoom.recording.host_id': { + category: 'zoom', + description: 'UserID of the host of the meeting that was recorded ', + name: 'zoom.recording.host_id', + type: 'keyword', + }, + 'zoom.recording.topic': { + category: 'zoom', + description: 'Topic of the meeting related to the recording ', + name: 'zoom.recording.topic', + type: 'keyword', + }, + 'zoom.recording.type': { + category: 'zoom', + description: + 'Type of recording, can be multiple type of values, please check Zoom documentation ', + name: 'zoom.recording.type', + type: 'keyword', + }, + 'zoom.recording.start_time': { + category: 'zoom', + description: 'The date and time when the recording started ', + name: 'zoom.recording.start_time', + type: 'date', + }, + 'zoom.recording.timezone': { + category: 'zoom', + description: 'The timezone used for the recording date ', + name: 'zoom.recording.timezone', + type: 'keyword', + }, + 'zoom.recording.duration': { + category: 'zoom', + description: 'Duration of the recording in minutes ', + name: 'zoom.recording.duration', + type: 'long', + }, + 'zoom.recording.share_url': { + category: 'zoom', + description: 'The URL to access the recording ', + name: 'zoom.recording.share_url', + type: 'keyword', + }, + 'zoom.recording.total_size': { + category: 'zoom', + description: 'Total size of the recording in bytes ', + name: 'zoom.recording.total_size', + type: 'long', + }, + 'zoom.recording.recording_count': { + category: 'zoom', + description: 'Number of recording files related to the recording ', + name: 'zoom.recording.recording_count', + type: 'long', + }, + 'zoom.recording.recording_file.recording_start': { + category: 'zoom', + description: 'The date and time the recording started ', + name: 'zoom.recording.recording_file.recording_start', + type: 'date', + }, + 'zoom.recording.recording_file.recording_end': { + category: 'zoom', + description: 'The date and time the recording finished ', + name: 'zoom.recording.recording_file.recording_end', + type: 'date', + }, + 'zoom.recording.host_email': { + category: 'zoom', + description: 'Email address of the host related to the meeting that was recorded ', + name: 'zoom.recording.host_email', + type: 'keyword', + }, + 'zoom.user.id': { + category: 'zoom', + description: 'UserID related to the user event ', + name: 'zoom.user.id', + type: 'keyword', + }, + 'zoom.user.first_name': { + category: 'zoom', + description: 'User first name related to the user event ', + name: 'zoom.user.first_name', + type: 'keyword', + }, + 'zoom.user.last_name': { + category: 'zoom', + description: 'User last name related to the user event ', + name: 'zoom.user.last_name', + type: 'keyword', + }, + 'zoom.user.email': { + category: 'zoom', + description: 'User email related to the user event ', + name: 'zoom.user.email', + type: 'keyword', + }, + 'zoom.user.type': { + category: 'zoom', + description: 'User type related to the user event ', + name: 'zoom.user.type', + type: 'keyword', + }, + 'zoom.user.phone_number': { + category: 'zoom', + description: 'User phone number related to the user event ', + name: 'zoom.user.phone_number', + type: 'keyword', + }, + 'zoom.user.phone_country': { + category: 'zoom', + description: 'User country code related to the user event ', + name: 'zoom.user.phone_country', + type: 'keyword', + }, + 'zoom.user.company': { + category: 'zoom', + description: 'User company related to the user event ', + name: 'zoom.user.company', + type: 'keyword', + }, + 'zoom.user.pmi': { + category: 'zoom', + description: 'User personal meeting ID related to the user event ', + name: 'zoom.user.pmi', + type: 'keyword', + }, + 'zoom.user.use_pmi': { + category: 'zoom', + description: 'If a user has PMI enabled ', + name: 'zoom.user.use_pmi', + type: 'boolean', + }, + 'zoom.user.pic_url': { + category: 'zoom', + description: 'Full URL to the profile picture used by the user ', + name: 'zoom.user.pic_url', + type: 'keyword', + }, + 'zoom.user.vanity_name': { + category: 'zoom', + description: 'Name of the personal meeting room related to the user event ', + name: 'zoom.user.vanity_name', + type: 'keyword', + }, + 'zoom.user.timezone': { + category: 'zoom', + description: 'Timezone configured for the user ', + name: 'zoom.user.timezone', + type: 'keyword', + }, + 'zoom.user.language': { + category: 'zoom', + description: 'Language configured for the user ', + name: 'zoom.user.language', + type: 'keyword', + }, + 'zoom.user.host_key': { + category: 'zoom', + description: 'Host key set for the user ', + name: 'zoom.user.host_key', + type: 'keyword', + }, + 'zoom.user.role': { + category: 'zoom', + description: 'The configured role for the user ', + name: 'zoom.user.role', + type: 'keyword', + }, + 'zoom.user.dept': { + category: 'zoom', + description: 'The configured departement for the user ', + name: 'zoom.user.dept', + type: 'keyword', + }, + 'zoom.user.presence_status': { + category: 'zoom', + description: 'Current presence status of user ', + name: 'zoom.user.presence_status', + type: 'keyword', + }, + 'zoom.user.personal_notes': { + category: 'zoom', + description: 'Personal notes for the User ', + name: 'zoom.user.personal_notes', + type: 'keyword', + }, + 'zoom.user.client_type': { + category: 'zoom', + description: 'Type of client used by the user. Can be browser, mac, win, iphone or android ', + name: 'zoom.user.client_type', + type: 'keyword', + }, + 'zoom.user.version': { + category: 'zoom', + description: 'Version of the client used by the user ', + name: 'zoom.user.version', + type: 'keyword', + }, + 'zoom.webinar.id': { + category: 'zoom', + description: 'Unique ID for the related webinar ', + name: 'zoom.webinar.id', + type: 'keyword', + }, + 'zoom.webinar.join_url': { + category: 'zoom', + description: 'The URL configured to join the webinar ', + name: 'zoom.webinar.join_url', + type: 'keyword', + }, + 'zoom.webinar.uuid': { + category: 'zoom', + description: 'UUID for the related webinar ', + name: 'zoom.webinar.uuid', + type: 'keyword', + }, + 'zoom.webinar.host_id': { + category: 'zoom', + description: 'UserID for the configured host of the webinar ', + name: 'zoom.webinar.host_id', + type: 'keyword', + }, + 'zoom.webinar.topic': { + category: 'zoom', + description: 'Meeting topic of the related webinar ', + name: 'zoom.webinar.topic', + type: 'keyword', + }, + 'zoom.webinar.type': { + category: 'zoom', + description: + 'Type of webinar created. Can be either 5(Webinar), 6(Recurring webinar without fixed time) or 9(Recurring webinar with fixed time) ', + name: 'zoom.webinar.type', + type: 'keyword', + }, + 'zoom.webinar.start_time': { + category: 'zoom', + description: 'The date and time when the webinar started ', + name: 'zoom.webinar.start_time', + type: 'date', + }, + 'zoom.webinar.timezone': { + category: 'zoom', + description: 'Timezone used for the dates related to the webinar ', + name: 'zoom.webinar.timezone', + type: 'keyword', + }, + 'zoom.webinar.duration': { + category: 'zoom', + description: 'Duration of the webinar in minutes ', + name: 'zoom.webinar.duration', + type: 'long', + }, + 'zoom.webinar.agenda': { + category: 'zoom', + description: 'The configured agenda of the webinar ', + name: 'zoom.webinar.agenda', + type: 'keyword', + }, + 'zoom.webinar.password': { + category: 'zoom', + description: 'Password configured to access the webinar ', + name: 'zoom.webinar.password', + type: 'keyword', + }, + 'zoom.webinar.issues': { + category: 'zoom', + description: 'Any reported issues about a webinar is reported in this field ', + name: 'zoom.webinar.issues', + type: 'keyword', + }, + 'zoom.zoomroom.id': { + category: 'zoom', + description: 'Unique ID of the Zoom room ', + name: 'zoom.zoomroom.id', + type: 'keyword', + }, + 'zoom.zoomroom.room_name': { + category: 'zoom', + description: 'The configured name of the Zoom room ', + name: 'zoom.zoomroom.room_name', + type: 'keyword', + }, + 'zoom.zoomroom.calendar_name': { + category: 'zoom', + description: 'Calendar name of the Zoom room ', + name: 'zoom.zoomroom.calendar_name', + type: 'keyword', + }, + 'zoom.zoomroom.calendar_id': { + category: 'zoom', + description: 'Unique ID of the calendar used by the Zoom room ', + name: 'zoom.zoomroom.calendar_id', + type: 'keyword', + }, + 'zoom.zoomroom.event_id': { + category: 'zoom', + description: 'Unique ID of the calendar event associated with the Zoom Room ', + name: 'zoom.zoomroom.event_id', + type: 'keyword', + }, + 'zoom.zoomroom.change_key': { + category: 'zoom', + description: + 'Key used by Microsoft products integration that represents a specific version of a calendar ', + name: 'zoom.zoomroom.change_key', + type: 'keyword', + }, + 'zoom.zoomroom.resource_email': { + category: 'zoom', + description: 'Email address associated with the calendar in use by the Zoom room ', + name: 'zoom.zoomroom.resource_email', + type: 'keyword', + }, + 'zoom.zoomroom.email': { + category: 'zoom', + description: 'Email address associated with the Zoom room itself ', + name: 'zoom.zoomroom.email', + type: 'keyword', + }, + 'zoom.zoomroom.issue': { + category: 'zoom', + description: 'Any reported alerts or issues related to the Zoom room or its equipment ', + name: 'zoom.zoomroom.issue', + type: 'keyword', + }, + 'zoom.zoomroom.alert_type': { + category: 'zoom', + description: + 'An integer value representing the type of alert. The list of alert types can be found in the Zoom documentation ', + name: 'zoom.zoomroom.alert_type', + type: 'keyword', + }, + 'zoom.zoomroom.component': { + category: 'zoom', + description: + 'An integer value representing the type of equipment or component, The list of component types can be found in the Zoom documentation ', + name: 'zoom.zoomroom.component', + type: 'keyword', + }, + 'zoom.zoomroom.alert_kind': { + category: 'zoom', + description: + 'An integer value showing if the Zoom room alert has been either 1(Triggered) or 2(Cleared) ', + name: 'zoom.zoomroom.alert_kind', + type: 'keyword', + }, + 'zoom.registrant.id': { + category: 'zoom', + description: 'Unique ID of the user registering to a meeting or webinar ', + name: 'zoom.registrant.id', + type: 'keyword', + }, + 'zoom.registrant.status': { + category: 'zoom', + description: 'Status of the specific user registration ', + name: 'zoom.registrant.status', + type: 'keyword', + }, + 'zoom.registrant.email': { + category: 'zoom', + description: 'Email of the user registering to a meeting or webinar ', + name: 'zoom.registrant.email', + type: 'keyword', + }, + 'zoom.registrant.first_name': { + category: 'zoom', + description: 'First name of the user registering to a meeting or webinar ', + name: 'zoom.registrant.first_name', + type: 'keyword', + }, + 'zoom.registrant.last_name': { + category: 'zoom', + description: 'Last name of the user registering to a meeting or webinar ', + name: 'zoom.registrant.last_name', + type: 'keyword', + }, + 'zoom.registrant.address': { + category: 'zoom', + description: 'Address of the user registering to a meeting or webinar ', + name: 'zoom.registrant.address', + type: 'keyword', + }, + 'zoom.registrant.city': { + category: 'zoom', + description: 'City of the user registering to a meeting or webinar ', + name: 'zoom.registrant.city', + type: 'keyword', + }, + 'zoom.registrant.country': { + category: 'zoom', + description: 'Country of the user registering to a meeting or webinar ', + name: 'zoom.registrant.country', + type: 'keyword', + }, + 'zoom.registrant.zip': { + category: 'zoom', + description: 'Zip code of the user registering to a meeting or webinar ', + name: 'zoom.registrant.zip', + type: 'keyword', + }, + 'zoom.registrant.state': { + category: 'zoom', + description: 'State of the user registering to a meeting or webinar ', + name: 'zoom.registrant.state', + type: 'keyword', + }, + 'zoom.registrant.phone': { + category: 'zoom', + description: 'Phone number of the user registering to a meeting or webinar ', + name: 'zoom.registrant.phone', + type: 'keyword', + }, + 'zoom.registrant.industry': { + category: 'zoom', + description: 'Related industry of the user registering to a meeting or webinar ', + name: 'zoom.registrant.industry', + type: 'keyword', + }, + 'zoom.registrant.org': { + category: 'zoom', + description: 'Organization related to the user registering to a meeting or webinar ', + name: 'zoom.registrant.org', + type: 'keyword', + }, + 'zoom.registrant.job_title': { + category: 'zoom', + description: 'Job title of the user registering to a meeting or webinar ', + name: 'zoom.registrant.job_title', + type: 'keyword', + }, + 'zoom.registrant.purchasing_time_frame': { + category: 'zoom', + description: 'Choosen purchase timeframe of the user registering to a meeting or webinar ', + name: 'zoom.registrant.purchasing_time_frame', + type: 'keyword', + }, + 'zoom.registrant.role_in_purchase_process': { + category: 'zoom', + description: + 'Choosen role in a purchase process related to the user registering to a meeting or webinar ', + name: 'zoom.registrant.role_in_purchase_process', + type: 'keyword', + }, + 'zoom.registrant.no_of_employees': { + category: 'zoom', + description: 'Number of employees choosen by the user registering to a meeting or webinar ', + name: 'zoom.registrant.no_of_employees', + type: 'keyword', + }, + 'zoom.registrant.comments': { + category: 'zoom', + description: 'Comments left by the user registering to a meeting or webinar ', + name: 'zoom.registrant.comments', + type: 'keyword', + }, + 'zoom.registrant.join_url': { + category: 'zoom', + description: 'The URL that the registrant can use to join the webinar ', + name: 'zoom.registrant.join_url', + type: 'keyword', + }, + 'zoom.participant.id': { + category: 'zoom', + description: 'Unique ID of the participant related to a meeting ', + name: 'zoom.participant.id', + type: 'keyword', + }, + 'zoom.participant.user_id': { + category: 'zoom', + description: 'UserID of the participant related to a meeting ', + name: 'zoom.participant.user_id', + type: 'keyword', + }, + 'zoom.participant.user_name': { + category: 'zoom', + description: 'Username of the participant related to a meeting ', + name: 'zoom.participant.user_name', + type: 'keyword', + }, + 'zoom.participant.join_time': { + category: 'zoom', + description: 'The date and time a participant joined a meeting ', + name: 'zoom.participant.join_time', + type: 'date', + }, + 'zoom.participant.leave_time': { + category: 'zoom', + description: 'The date and time a participant left a meeting ', + name: 'zoom.participant.leave_time', + type: 'date', + }, + 'zoom.participant.sharing_details.link_source': { + category: 'zoom', + description: 'Method of sharing with dropbox integration ', + name: 'zoom.participant.sharing_details.link_source', + type: 'keyword', + }, + 'zoom.participant.sharing_details.content': { + category: 'zoom', + description: 'Type of content that was shared ', + name: 'zoom.participant.sharing_details.content', + type: 'keyword', + }, + 'zoom.participant.sharing_details.file_link': { + category: 'zoom', + description: 'The file link that was shared ', + name: 'zoom.participant.sharing_details.file_link', + type: 'keyword', + }, + 'zoom.participant.sharing_details.date_time': { + category: 'zoom', + description: 'Timestamp the sharing started ', + name: 'zoom.participant.sharing_details.date_time', + type: 'keyword', + }, + 'zoom.participant.sharing_details.source': { + category: 'zoom', + description: 'The file source that was share ', + name: 'zoom.participant.sharing_details.source', + type: 'keyword', + }, + 'zoom.old_values': { + category: 'zoom', + description: + 'Includes the old values when updating a object like user, meeting, account or webinar ', + name: 'zoom.old_values', + type: 'flattened', + }, + 'zoom.settings': { + category: 'zoom', + description: + 'The current active settings related to a object like user, meeting, account or webinar ', + name: 'zoom.settings', + type: 'flattened', + }, + 'aws-cloudwatch.log_group': { + category: 'aws-cloudwatch', description: 'The name of the log group to which this event belongs.', - name: 'awscloudwatch.log_group', + name: 'aws-cloudwatch.log_group', type: 'keyword', }, - 'awscloudwatch.log_stream': { - category: 'awscloudwatch', + 'aws-cloudwatch.log_stream': { + category: 'aws-cloudwatch', description: 'The name of the log stream to which this event belongs.', - name: 'awscloudwatch.log_stream', + name: 'aws-cloudwatch.log_stream', type: 'keyword', }, - 'awscloudwatch.ingestion_time': { - category: 'awscloudwatch', + 'aws-cloudwatch.ingestion_time': { + category: 'aws-cloudwatch', description: 'The time the event was ingested in AWS CloudWatch.', - name: 'awscloudwatch.ingestion_time', + name: 'aws-cloudwatch.ingestion_time', + type: 'keyword', + }, + bucket_name: { + category: 'base', + description: 'Name of the S3 bucket that this log retrieved from. ', + name: 'bucket_name', + type: 'keyword', + }, + object_key: { + category: 'base', + description: 'Name of the S3 object that this log retrieved from. ', + name: 'object_key', type: 'keyword', }, 'netflow.type': { @@ -30934,18 +37091,6 @@ export const fieldsBeat: BeatFields = { name: 'netflow.vpn_identifier', type: 'short', }, - bucket_name: { - category: 'base', - description: 'Name of the S3 bucket that this log retrieved from. ', - name: 'bucket_name', - type: 'keyword', - }, - object_key: { - category: 'base', - description: 'Name of the S3 object that this log retrieved from. ', - name: 'object_key', - type: 'keyword', - }, 'cef.version': { category: 'cef', description: 'Version of the CEF specification used by the message. ', @@ -33954,377 +40099,451 @@ export const fieldsBeat: BeatFields = { 'If the Redis command has resulted in an error, this field contains the error message returned by the Redis server. ', name: 'redis.error', }, - 'thrift.params': { - category: 'thrift', - description: - 'The RPC method call parameters in a human readable format. If the IDL files are available, the parameters use names whenever possible. Otherwise, the IDs from the message are used. ', - name: 'thrift.params', + 'sip.code': { + category: 'sip', + description: 'Response status code.', + name: 'sip.code', + type: 'keyword', }, - 'thrift.service': { - category: 'thrift', - description: 'The name of the Thrift-RPC service as defined in the IDL files. ', - name: 'thrift.service', + 'sip.method': { + category: 'sip', + description: 'Request method.', + name: 'sip.method', + type: 'keyword', }, - 'thrift.return_value': { - category: 'thrift', - description: - 'The value returned by the Thrift-RPC call. This is encoded in a human readable format. ', - name: 'thrift.return_value', + 'sip.status': { + category: 'sip', + description: 'Response status phrase.', + name: 'sip.status', + type: 'keyword', }, - 'thrift.exceptions': { - category: 'thrift', - description: - 'If the call resulted in exceptions, this field contains the exceptions in a human readable format. ', - name: 'thrift.exceptions', + 'sip.type': { + category: 'sip', + description: 'Either request or response.', + name: 'sip.type', + type: 'keyword', }, - 'tls.client.x509.version': { - category: 'tls', - description: 'Version of x509 format.', - example: 3, - name: 'tls.client.x509.version', + 'sip.version': { + category: 'sip', + description: 'SIP protocol version.', + name: 'sip.version', type: 'keyword', }, - 'tls.client.x509.version_number': { - category: 'tls', - description: 'Version of x509 format.', - example: 3, - name: 'tls.client.x509.version_number', + 'sip.uri.original': { + category: 'sip', + description: 'The original URI.', + name: 'sip.uri.original', type: 'keyword', }, - 'tls.client.x509.serial_number': { - category: 'tls', - description: - 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters. ', - example: '55FBB9C7DEBF09809D12CCAA', - name: 'tls.client.x509.serial_number', + 'sip.uri.scheme': { + category: 'sip', + description: 'The URI scheme.', + name: 'sip.uri.scheme', type: 'keyword', }, - 'tls.client.x509.issuer.distinguished_name': { - category: 'tls', - description: 'Distinguished name (DN) of issuing certificate authority.', - example: 'C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA', - name: 'tls.client.x509.issuer.distinguished_name', + 'sip.uri.username': { + category: 'sip', + description: 'The URI user name.', + name: 'sip.uri.username', type: 'keyword', }, - 'tls.client.x509.issuer.common_name': { - category: 'tls', - description: 'List of common name (CN) of issuing certificate authority.', - example: 'DigiCert SHA2 High Assurance Server CA', - name: 'tls.client.x509.issuer.common_name', + 'sip.uri.host': { + category: 'sip', + description: 'The URI host.', + name: 'sip.uri.host', type: 'keyword', }, - 'tls.client.x509.issuer.organizational_unit': { - category: 'tls', - description: 'List of organizational units (OU) of issuing certificate authority.', - example: 'www.digicert.com', - name: 'tls.client.x509.issuer.organizational_unit', + 'sip.uri.port': { + category: 'sip', + description: 'The URI port.', + name: 'sip.uri.port', type: 'keyword', }, - 'tls.client.x509.issuer.organization': { - category: 'tls', - description: 'List of organizations (O) of issuing certificate authority.', - example: 'DigiCert Inc', - name: 'tls.client.x509.issuer.organization', + 'sip.accept': { + category: 'sip', + description: 'Accept header value.', + name: 'sip.accept', type: 'keyword', }, - 'tls.client.x509.issuer.locality': { - category: 'tls', - description: 'List of locality names (L)', - example: 'Mountain View', - name: 'tls.client.x509.issuer.locality', + 'sip.allow': { + category: 'sip', + description: 'Allowed methods.', + name: 'sip.allow', type: 'keyword', }, - 'tls.client.x509.issuer.province': { - category: 'tls', - description: 'Province or region within country.', - name: 'tls.client.x509.issuer.province', + 'sip.call_id': { + category: 'sip', + description: 'Call ID.', + name: 'sip.call_id', type: 'keyword', }, - 'tls.client.x509.issuer.state_or_province': { - category: 'tls', - description: 'List of state or province names (ST, S, or P)', - example: 'California', - name: 'tls.client.x509.issuer.state_or_province', + 'sip.content_length': { + category: 'sip', + name: 'sip.content_length', + type: 'long', + }, + 'sip.content_type': { + category: 'sip', + name: 'sip.content_type', type: 'keyword', }, - 'tls.client.x509.issuer.country': { - category: 'tls', - description: 'List of country (C) codes', - example: 'US', - name: 'tls.client.x509.issuer.country', + 'sip.max_forwards': { + category: 'sip', + name: 'sip.max_forwards', + type: 'long', + }, + 'sip.supported': { + category: 'sip', + description: 'Supported methods.', + name: 'sip.supported', type: 'keyword', }, - 'tls.client.x509.signature_algorithm': { - category: 'tls', - description: - 'Identifier for certificate signature algorithm. Recommend using names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353).', - example: 'SHA256-RSA', - name: 'tls.client.x509.signature_algorithm', + 'sip.user_agent.original': { + category: 'sip', + name: 'sip.user_agent.original', type: 'keyword', }, - 'tls.client.x509.not_before': { - category: 'tls', - description: 'Time at which the certificate is first considered valid.', - example: '"2019-08-16T01:40:25.000Z"', - name: 'tls.client.x509.not_before', - type: 'date', + 'sip.private.uri.original': { + category: 'sip', + description: 'Private original URI.', + name: 'sip.private.uri.original', + type: 'keyword', }, - 'tls.client.x509.not_after': { - category: 'tls', - description: 'Time at which the certificate is no longer considered valid.', - example: '"2020-07-16T03:15:39.000Z"', - name: 'tls.client.x509.not_after', - type: 'date', + 'sip.private.uri.scheme': { + category: 'sip', + description: 'Private URI scheme.', + name: 'sip.private.uri.scheme', + type: 'keyword', }, - 'tls.client.x509.subject.distinguished_name': { - category: 'tls', - description: 'Distinguished name (DN) of the certificate subject entity.', - example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', - name: 'tls.client.x509.subject.distinguished_name', + 'sip.private.uri.username': { + category: 'sip', + description: 'Private URI user name.', + name: 'sip.private.uri.username', type: 'keyword', }, - 'tls.client.x509.subject.common_name': { - category: 'tls', - description: 'List of common names (CN) of subject.', - example: 'r2.shared.global.fastly.net', - name: 'tls.client.x509.subject.common_name', + 'sip.private.uri.host': { + category: 'sip', + description: 'Private URI host.', + name: 'sip.private.uri.host', type: 'keyword', }, - 'tls.client.x509.subject.organizational_unit': { - category: 'tls', - description: 'List of organizational units (OU) of subject.', - name: 'tls.client.x509.subject.organizational_unit', + 'sip.private.uri.port': { + category: 'sip', + description: 'Private URI port.', + name: 'sip.private.uri.port', type: 'keyword', }, - 'tls.client.x509.subject.organization': { - category: 'tls', - description: 'List of organizations (O) of subject.', - example: 'Fastly, Inc.', - name: 'tls.client.x509.subject.organization', + 'sip.cseq.code': { + category: 'sip', + description: 'Sequence code.', + name: 'sip.cseq.code', type: 'keyword', }, - 'tls.client.x509.subject.locality': { - category: 'tls', - description: 'List of locality names (L)', - example: 'San Francisco', - name: 'tls.client.x509.subject.locality', + 'sip.cseq.method': { + category: 'sip', + description: 'Sequence method.', + name: 'sip.cseq.method', type: 'keyword', }, - 'tls.client.x509.subject.province': { - category: 'tls', - description: 'Province or region within country.', - name: 'tls.client.x509.subject.province', + 'sip.via.original': { + category: 'sip', + description: 'The original Via value.', + name: 'sip.via.original', type: 'keyword', }, - 'tls.client.x509.subject.state_or_province': { - category: 'tls', - description: 'List of state or province names (ST, S, or P)', - example: 'California', - name: 'tls.client.x509.subject.state_or_province', + 'sip.to.display_info': { + category: 'sip', + description: 'To display info', + name: 'sip.to.display_info', type: 'keyword', }, - 'tls.client.x509.subject.country': { - category: 'tls', - description: 'List of country (C) code', - example: 'US', - name: 'tls.client.x509.subject.country', + 'sip.to.uri.original': { + category: 'sip', + description: 'To original URI', + name: 'sip.to.uri.original', type: 'keyword', }, - 'tls.client.x509.public_key_algorithm': { - category: 'tls', - description: 'Algorithm used to generate the public key.', - example: 'RSA', - name: 'tls.client.x509.public_key_algorithm', + 'sip.to.uri.scheme': { + category: 'sip', + description: 'To URI scheme', + name: 'sip.to.uri.scheme', type: 'keyword', }, - 'tls.client.x509.public_key_size': { - category: 'tls', - description: 'The size of the public key space in bits.', - example: 2048, - name: 'tls.client.x509.public_key_size', - type: 'long', + 'sip.to.uri.username': { + category: 'sip', + description: 'To URI user name', + name: 'sip.to.uri.username', + type: 'keyword', }, - 'tls.client.x509.alternative_names': { - category: 'tls', - description: - 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', - example: '*.elastic.co', - name: 'tls.client.x509.alternative_names', + 'sip.to.uri.host': { + category: 'sip', + description: 'To URI host', + name: 'sip.to.uri.host', type: 'keyword', }, - 'tls.server.x509.version': { - category: 'tls', - description: 'Version of x509 format.', - example: 3, - name: 'tls.server.x509.version', + 'sip.to.uri.port': { + category: 'sip', + description: 'To URI port', + name: 'sip.to.uri.port', type: 'keyword', }, - 'tls.server.x509.version_number': { - category: 'tls', - description: 'Version of x509 format.', - example: 3, - name: 'tls.server.x509.version_number', + 'sip.to.tag': { + category: 'sip', + description: 'To tag', + name: 'sip.to.tag', type: 'keyword', }, - 'tls.server.x509.serial_number': { - category: 'tls', - description: - 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters. ', - example: '55FBB9C7DEBF09809D12CCAA', - name: 'tls.server.x509.serial_number', + 'sip.from.display_info': { + category: 'sip', + description: 'From display info', + name: 'sip.from.display_info', type: 'keyword', }, - 'tls.server.x509.issuer.distinguished_name': { - category: 'tls', - description: 'Distinguished name (DN) of issuing certificate authority.', - example: 'C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA', - name: 'tls.server.x509.issuer.distinguished_name', + 'sip.from.uri.original': { + category: 'sip', + description: 'From original URI', + name: 'sip.from.uri.original', type: 'keyword', }, - 'tls.server.x509.issuer.common_name': { - category: 'tls', - description: 'List of common name (CN) of issuing certificate authority.', - example: 'DigiCert SHA2 High Assurance Server CA', - name: 'tls.server.x509.issuer.common_name', + 'sip.from.uri.scheme': { + category: 'sip', + description: 'From URI scheme', + name: 'sip.from.uri.scheme', type: 'keyword', }, - 'tls.server.x509.issuer.organizational_unit': { - category: 'tls', - description: 'List of organizational units (OU) of issuing certificate authority.', - example: 'www.digicert.com', - name: 'tls.server.x509.issuer.organizational_unit', + 'sip.from.uri.username': { + category: 'sip', + description: 'From URI user name', + name: 'sip.from.uri.username', type: 'keyword', }, - 'tls.server.x509.issuer.organization': { - category: 'tls', - description: 'List of organizations (O) of issuing certificate authority.', - example: 'DigiCert Inc', - name: 'tls.server.x509.issuer.organization', + 'sip.from.uri.host': { + category: 'sip', + description: 'From URI host', + name: 'sip.from.uri.host', type: 'keyword', }, - 'tls.server.x509.issuer.locality': { - category: 'tls', - description: 'List of locality names (L)', - example: 'Mountain View', - name: 'tls.server.x509.issuer.locality', + 'sip.from.uri.port': { + category: 'sip', + description: 'From URI port', + name: 'sip.from.uri.port', type: 'keyword', }, - 'tls.server.x509.issuer.province': { - category: 'tls', - description: 'Province or region within country.', - name: 'tls.server.x509.issuer.province', + 'sip.from.tag': { + category: 'sip', + description: 'From tag', + name: 'sip.from.tag', type: 'keyword', }, - 'tls.server.x509.issuer.state_or_province': { - category: 'tls', - description: 'List of state or province names (ST, S, or P)', - example: 'California', - name: 'tls.server.x509.issuer.state_or_province', + 'sip.contact.display_info': { + category: 'sip', + description: 'Contact display info', + name: 'sip.contact.display_info', type: 'keyword', }, - 'tls.server.x509.issuer.country': { - category: 'tls', - description: 'List of country (C) codes', - example: 'US', - name: 'tls.server.x509.issuer.country', + 'sip.contact.uri.original': { + category: 'sip', + description: 'Contact original URI', + name: 'sip.contact.uri.original', type: 'keyword', }, - 'tls.server.x509.signature_algorithm': { - category: 'tls', - description: - 'Identifier for certificate signature algorithm. Recommend using names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353).', - example: 'SHA256-RSA', - name: 'tls.server.x509.signature_algorithm', + 'sip.contact.uri.scheme': { + category: 'sip', + description: 'Contat URI scheme', + name: 'sip.contact.uri.scheme', type: 'keyword', }, - 'tls.server.x509.not_before': { - category: 'tls', - description: 'Time at which the certificate is first considered valid.', - example: '"2019-08-16T01:40:25.000Z"', - name: 'tls.server.x509.not_before', - type: 'date', + 'sip.contact.uri.username': { + category: 'sip', + description: 'Contact URI user name', + name: 'sip.contact.uri.username', + type: 'keyword', }, - 'tls.server.x509.not_after': { - category: 'tls', - description: 'Time at which the certificate is no longer considered valid.', - example: '"2020-07-16T03:15:39.000Z"', - name: 'tls.server.x509.not_after', - type: 'date', + 'sip.contact.uri.host': { + category: 'sip', + description: 'Contact URI host', + name: 'sip.contact.uri.host', + type: 'keyword', }, - 'tls.server.x509.subject.distinguished_name': { - category: 'tls', - description: 'Distinguished name (DN) of the certificate subject entity.', - example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', - name: 'tls.server.x509.subject.distinguished_name', + 'sip.contact.uri.port': { + category: 'sip', + description: 'Contact URI port', + name: 'sip.contact.uri.port', type: 'keyword', }, - 'tls.server.x509.subject.common_name': { - category: 'tls', - description: 'List of common names (CN) of subject.', - example: 'r2.shared.global.fastly.net', - name: 'tls.server.x509.subject.common_name', + 'sip.contact.transport': { + category: 'sip', + description: 'Contact transport', + name: 'sip.contact.transport', type: 'keyword', }, - 'tls.server.x509.subject.organizational_unit': { - category: 'tls', - description: 'List of organizational units (OU) of subject.', - name: 'tls.server.x509.subject.organizational_unit', + 'sip.contact.line': { + category: 'sip', + description: 'Contact line', + name: 'sip.contact.line', type: 'keyword', }, - 'tls.server.x509.subject.organization': { - category: 'tls', - description: 'List of organizations (O) of subject.', - example: 'Fastly, Inc.', - name: 'tls.server.x509.subject.organization', + 'sip.contact.expires': { + category: 'sip', + description: 'Contact expires', + name: 'sip.contact.expires', type: 'keyword', }, - 'tls.server.x509.subject.locality': { - category: 'tls', - description: 'List of locality names (L)', - example: 'San Francisco', - name: 'tls.server.x509.subject.locality', + 'sip.contact.q': { + category: 'sip', + description: 'Contact Q', + name: 'sip.contact.q', type: 'keyword', }, - 'tls.server.x509.subject.province': { + 'sip.auth.scheme': { + category: 'sip', + description: 'Auth scheme', + name: 'sip.auth.scheme', + type: 'keyword', + }, + 'sip.auth.realm': { + category: 'sip', + description: 'Auth realm', + name: 'sip.auth.realm', + type: 'keyword', + }, + 'sip.auth.uri.original': { + category: 'sip', + description: 'Auth original URI', + name: 'sip.auth.uri.original', + type: 'keyword', + }, + 'sip.auth.uri.scheme': { + category: 'sip', + description: 'Auth URI scheme', + name: 'sip.auth.uri.scheme', + type: 'keyword', + }, + 'sip.auth.uri.host': { + category: 'sip', + description: 'Auth URI host', + name: 'sip.auth.uri.host', + type: 'keyword', + }, + 'sip.auth.uri.port': { + category: 'sip', + description: 'Auth URI port', + name: 'sip.auth.uri.port', + type: 'keyword', + }, + 'sip.sdp.version': { + category: 'sip', + description: 'SDP version', + name: 'sip.sdp.version', + type: 'keyword', + }, + 'sip.sdp.owner.username': { + category: 'sip', + description: 'SDP owner user name', + name: 'sip.sdp.owner.username', + type: 'keyword', + }, + 'sip.sdp.owner.session_id': { + category: 'sip', + description: 'SDP owner session ID', + name: 'sip.sdp.owner.session_id', + type: 'keyword', + }, + 'sip.sdp.owner.version': { + category: 'sip', + description: 'SDP owner version', + name: 'sip.sdp.owner.version', + type: 'keyword', + }, + 'sip.sdp.owner.ip': { + category: 'sip', + description: 'SDP owner IP', + name: 'sip.sdp.owner.ip', + type: 'ip', + }, + 'sip.sdp.session.name': { + category: 'sip', + description: 'SDP session name', + name: 'sip.sdp.session.name', + type: 'keyword', + }, + 'sip.sdp.connection.info': { + category: 'sip', + description: 'SDP connection info', + name: 'sip.sdp.connection.info', + type: 'keyword', + }, + 'sip.sdp.connection.address': { + category: 'sip', + description: 'SDP connection address', + name: 'sip.sdp.connection.address', + type: 'keyword', + }, + 'sip.sdp.body.original': { + category: 'sip', + description: 'SDP original body', + name: 'sip.sdp.body.original', + type: 'keyword', + }, + 'thrift.params': { + category: 'thrift', + description: + 'The RPC method call parameters in a human readable format. If the IDL files are available, the parameters use names whenever possible. Otherwise, the IDs from the message are used. ', + name: 'thrift.params', + }, + 'thrift.service': { + category: 'thrift', + description: 'The name of the Thrift-RPC service as defined in the IDL files. ', + name: 'thrift.service', + }, + 'thrift.return_value': { + category: 'thrift', + description: + 'The value returned by the Thrift-RPC call. This is encoded in a human readable format. ', + name: 'thrift.return_value', + }, + 'thrift.exceptions': { + category: 'thrift', + description: + 'If the call resulted in exceptions, this field contains the exceptions in a human readable format. ', + name: 'thrift.exceptions', + }, + 'tls.client.x509.version': { category: 'tls', - description: 'Province or region within country.', - name: 'tls.server.x509.subject.province', + description: 'Version of x509 format.', + example: 3, + name: 'tls.client.x509.version', type: 'keyword', }, - 'tls.server.x509.subject.state_or_province': { + 'tls.client.x509.issuer.province': { category: 'tls', - description: 'List of state or province names (ST, S, or P)', - example: 'California', - name: 'tls.server.x509.subject.state_or_province', + description: 'Province or region within country.', + name: 'tls.client.x509.issuer.province', type: 'keyword', }, - 'tls.server.x509.subject.country': { + 'tls.client.x509.subject.province': { category: 'tls', - description: 'List of country (C) code', - example: 'US', - name: 'tls.server.x509.subject.country', + description: 'Province or region within country.', + name: 'tls.client.x509.subject.province', type: 'keyword', }, - 'tls.server.x509.public_key_algorithm': { + 'tls.server.x509.version': { category: 'tls', - description: 'Algorithm used to generate the public key.', - example: 'RSA', - name: 'tls.server.x509.public_key_algorithm', + description: 'Version of x509 format.', + example: 3, + name: 'tls.server.x509.version', type: 'keyword', }, - 'tls.server.x509.public_key_size': { + 'tls.server.x509.issuer.province': { category: 'tls', - description: 'The size of the public key space in bits.', - example: 2048, - name: 'tls.server.x509.public_key_size', - type: 'long', + description: 'Province or region within country.', + name: 'tls.server.x509.issuer.province', + type: 'keyword', }, - 'tls.server.x509.alternative_names': { + 'tls.server.x509.subject.province': { category: 'tls', - description: - 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', - example: '*.elastic.co', - name: 'tls.server.x509.alternative_names', + description: 'Province or region within country.', + name: 'tls.server.x509.subject.province', type: 'keyword', }, 'tls.detailed.version': { @@ -35117,7 +41336,7 @@ export const fieldsBeat: BeatFields = { 'winlog.api': { category: 'winlog', description: - 'The event log API type used to read the record. The possible values are "wineventlog" for the Windows Event Log API or "eventlogging" for the Event Logging API. The Event Logging API was designed for Windows Server 2003 or Windows 2000 operating systems. In Windows Vista, the event logging infrastructure was redesigned. On Windows Vista or later operating systems, the Windows Event Log API is used. Winlogbeat automatically detects which API to use for reading event logs. ', + 'The event log API type used to read the record. The possible values are "wineventlog" for the Windows Event Log API or "wineventlog-experimental" for its experimental implementation. ', name: 'winlog.api', }, 'winlog.activity_id': { diff --git a/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts b/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts index e607936be2741..9b24a70792030 100644 --- a/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts +++ b/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts @@ -9,6 +9,15 @@ import { DEFAULT_SPACE_ID } from '../constants'; const spaceContextRegex = /^\/s\/([a-z0-9_\-]+)/; +/** + * Extracts the space id from the given path. + * + * @param requestBasePath The base path of the current request. + * @param serverBasePath The server's base path. + * @returns the space id. + * + * @private + */ export function getSpaceIdFromPath( requestBasePath?: string | null, serverBasePath?: string | null @@ -40,6 +49,15 @@ export function getSpaceIdFromPath( }; } +/** + * Given a server base path, space id, and requested resource, this will construct a space-aware path + * that includes a URL identifier with the space id. + * + * @param basePath the server's base path. + * @param spaceId the space id. + * @param requestedPath the requested path (e.g. `/app/dashboard`). + * @returns the space-aware version of the requested path, inclusive of the server's base path. + */ export function addSpaceIdToPath( basePath: string = '/', spaceId: string = '', diff --git a/x-pack/plugins/spaces/common/types.ts b/x-pack/plugins/spaces/common/types.ts index 46455c1f601d4..866d29bf64d5b 100644 --- a/x-pack/plugins/spaces/common/types.ts +++ b/x-pack/plugins/spaces/common/types.ts @@ -7,17 +7,46 @@ import type { Space } from 'src/plugins/spaces_oss/common'; +/** + * Controls how spaces are retrieved. + */ export interface GetAllSpacesOptions { + /** + * An optional purpose describing how the set of spaces will be used. + * The default purpose (`any`) will retrieve all spaces the user is authorized to see, + * whereas a more specific purpose will retrieve all spaces the user is authorized to perform a specific action within. + * + * @see GetAllSpacesPurpose + */ purpose?: GetAllSpacesPurpose; + + /** + * Set to true to return a set of flags indicating which purposes the user is authorized for. + * + * @see GetAllSpacesPurpose + */ includeAuthorizedPurposes?: boolean; } +/** + * The set of purposes to retrieve spaces: + * - `any`: retrieves all spaces the user is authorized to see. + * - `copySavedObjectsIntoSpace`: retrieves all spaces the user is authorized to copy saved objects into. + * - `findSavedObjects`: retrieves all spaces the user is authorized to search within. + * - `shareSavedObjectsIntoSpace`: retrieves all spaces the user is authorized to share saved objects into. + */ export type GetAllSpacesPurpose = | 'any' | 'copySavedObjectsIntoSpace' | 'findSavedObjects' | 'shareSavedObjectsIntoSpace'; +/** + * Response format when querying for spaces. + */ export interface GetSpaceResult extends Space { + /** + * A set of flags indicating which purposes the user is authorized for. + */ authorizedPurposes?: Record; } diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index 062a534c91c84..8892671551896 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -36,7 +36,14 @@ export interface PluginsStart { management?: ManagementStart; } +/** + * Setup contract for the Spaces plugin. + */ export type SpacesPluginSetup = ReturnType; + +/** + * Start contract for the Spaces plugin. + */ export type SpacesPluginStart = ReturnType; export class SpacesPlugin implements Plugin { diff --git a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts index e37d7eed322e1..682a61c6f23a5 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts +++ b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts @@ -19,7 +19,7 @@ const FALLBACK_CODE_POINT = 97; * If a color is present on the Space itself, then that is used. * Otherwise, a color is provided from EUI's Visualization Colors based on the space name. * - * @param {Space} space + * @param {Space} space the space. */ export function getSpaceColor(space: Partial = {}) { const { color, name = '' } = space; @@ -38,7 +38,7 @@ export function getSpaceColor(space: Partial = {}) { * If initials are present on the Space itself, then that is used. * Otherwise, the initials are calculated based off the words in the space name, with a max length of 2 characters. * - * @param {Space} space + * @param {Space} space the space. */ export function getSpaceInitials(space: Partial = {}) { const { initials, name = '' } = space; @@ -59,7 +59,7 @@ export function getSpaceInitials(space: Partial = {}) { /** * Determines the avatar image for the provided space. * - * @param {Space} space + * @param {Space} space the space. */ export function getSpaceImageUrl(space: Partial = {}) { const { imageUrl } = space; diff --git a/x-pack/plugins/spaces/server/config.test.ts b/x-pack/plugins/spaces/server/config.test.ts index 1ce1be0698b1c..1e60c1b635320 100644 --- a/x-pack/plugins/spaces/server/config.test.ts +++ b/x-pack/plugins/spaces/server/config.test.ts @@ -37,7 +37,7 @@ describe('spaces config', () => { expect(messages).toMatchInlineSnapshot(` Array [ - "Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)", + "Disabling the Spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)", ] `); expect(migrated).toEqual(originalConfig); diff --git a/x-pack/plugins/spaces/server/config.ts b/x-pack/plugins/spaces/server/config.ts index ed541fda6c292..7f8b1a29befb3 100644 --- a/x-pack/plugins/spaces/server/config.ts +++ b/x-pack/plugins/spaces/server/config.ts @@ -27,7 +27,7 @@ export function createConfig$(context: PluginInitializerContext) { const disabledDeprecation: ConfigDeprecation = (config, fromPath, addDeprecation) => { if (config.xpack?.spaces?.enabled === false) { addDeprecation({ - message: `Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)`, + message: `Disabling the Spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)`, }); } return config; diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index 4218d8518720c..c779e92a3ae9b 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -21,7 +21,7 @@ export { addSpaceIdToPath } from '../common'; export { SpacesPluginSetup, SpacesPluginStart } from './plugin'; export { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; -export { ISpacesClient } from './spaces_client'; +export { ISpacesClient, SpacesClientRepositoryFactory, SpacesClientWrapper } from './spaces_client'; export { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from '../common'; diff --git a/x-pack/plugins/spaces/server/plugin.test.ts b/x-pack/plugins/spaces/server/plugin.test.ts index 94236b6e7f589..c5732fc4045bb 100644 --- a/x-pack/plugins/spaces/server/plugin.test.ts +++ b/x-pack/plugins/spaces/server/plugin.test.ts @@ -14,7 +14,7 @@ import { licensingMock } from '../../licensing/server/mocks'; import type { PluginsStart } from './plugin'; import { SpacesPlugin } from './plugin'; -describe('Spaces Plugin', () => { +describe('Spaces plugin', () => { describe('#setup', () => { it('can setup with all optional plugins disabled, exposing the expected contract', () => { const initializerContext = coreMock.createPluginInitializerContext({}); diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index aaa19ee74cb74..a7400fb71c1db 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -51,15 +51,41 @@ export interface PluginsStart { features: FeaturesPluginStart; } +/** + * Setup contract for the Spaces plugin. + */ export interface SpacesPluginSetup { + /** + * Service for interacting with spaces. + * + * @deprecated Please use the `spacesService` available on this plugin's start contract. + * @removeBy 7.16 + */ spacesService: SpacesServiceSetup; + + /** + * Registries exposed for the security plugin to transparently provide authorization and audit logging. + * @private + */ spacesClient: { + /** + * Sets the client repository factory. + * @private + */ setClientRepositoryFactory: (factory: SpacesClientRepositoryFactory) => void; + /** + * Registers a client wrapper. + * @private + */ registerClientWrapper: (wrapper: SpacesClientWrapper) => void; }; } +/** + * Start contract for the Spaces plugin. + */ export interface SpacesPluginStart { + /** Service for interacting with spaces. */ spacesService: SpacesServiceStart; } diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index c4f5e9edaf494..02aa4d0b976c0 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -8,7 +8,6 @@ import Boom from '@hapi/boom'; import { omit } from 'lodash'; -import type { PublicMethodsOf } from '@kbn/utility-types'; import type { ISavedObjectsRepository, SavedObject } from 'src/core/server'; import type { Space } from 'src/plugins/spaces_oss/common'; @@ -24,9 +23,46 @@ const SUPPORTED_GET_SPACE_PURPOSES: GetAllSpacesPurpose[] = [ ]; const DEFAULT_PURPOSE = 'any'; -export type ISpacesClient = PublicMethodsOf; +/** + * Client interface for interacting with spaces. + */ +export interface ISpacesClient { + /** + * Retrieve all available spaces. + * @param options controls which spaces are retrieved. + */ + getAll(options?: GetAllSpacesOptions): Promise; + + /** + * Retrieve a space by its id. + * @param id the space id. + */ + get(id: string): Promise; + + /** + * Creates a space. + * @param space the space to create. + */ + create(space: Space): Promise; + + /** + * Updates a space. + * @param id the id of the space to update. + * @param space the updated space. + */ + update(id: string, space: Space): Promise; + + /** + * Deletes a space, and all saved objects belonging to that space. + * @param id the id of the space to delete. + */ + delete(id: string): Promise; +} -export class SpacesClient { +/** + * Client for interacting with spaces. + */ +export class SpacesClient implements ISpacesClient { constructor( private readonly debugLogger: (message: string) => void, private readonly config: ConfigType, diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts index 4db27bac845f1..6580a2d57f040 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts @@ -18,11 +18,19 @@ import type { ConfigType } from '../config'; import type { ISpacesClient } from './spaces_client'; import { SpacesClient } from './spaces_client'; +/** + * For consumption by the security plugin only. + * @private + */ export type SpacesClientWrapper = ( request: KibanaRequest, baseClient: ISpacesClient ) => ISpacesClient; +/** + * For consumption by the security plugin only. + * @private + */ export type SpacesClientRepositoryFactory = ( request: KibanaRequest, savedObjectsStart: SavedObjectsServiceStart diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts index c76646fa8a2b4..e951ed38072d7 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts @@ -11,67 +11,77 @@ import type { Space } from 'src/plugins/spaces_oss/common'; import { getSpaceIdFromPath } from '../../common'; import { DEFAULT_SPACE_ID } from '../../common/constants'; import { namespaceToSpaceId, spaceIdToNamespace } from '../lib/utils/namespace'; -import type { SpacesClientServiceStart } from '../spaces_client'; +import type { ISpacesClient, SpacesClientServiceStart } from '../spaces_client'; +/** + * The Spaces service setup contract. + */ export interface SpacesServiceSetup { /** * Retrieves the space id associated with the provided request. - * @param request + * @param request the request. * * @deprecated Use `getSpaceId` from the `SpacesServiceStart` contract instead. + * @removeBy 7.16 */ getSpaceId(request: KibanaRequest): string; /** * Converts the provided space id into the corresponding Saved Objects `namespace` id. - * @param spaceId + * @param spaceId the space id to convert. * * @deprecated use `spaceIdToNamespace` from the `SpacesServiceStart` contract instead. + * @removeBy 7.16 */ spaceIdToNamespace(spaceId: string): string | undefined; /** * Converts the provided namespace into the corresponding space id. - * @param namespace + * @param namespace the namespace to convert. * * @deprecated use `namespaceToSpaceId` from the `SpacesServiceStart` contract instead. + * @removeBy 7.16 */ namespaceToSpaceId(namespace: string | undefined): string; } +/** + * The Spaces service start contract. + */ export interface SpacesServiceStart { /** * Creates a scoped instance of the SpacesClient. + * @param request the request. */ - createSpacesClient: SpacesClientServiceStart['createSpacesClient']; + createSpacesClient: (request: KibanaRequest) => ISpacesClient; /** * Retrieves the space id associated with the provided request. - * @param request + * @param request the request. */ getSpaceId(request: KibanaRequest): string; /** * Indicates if the provided request is executing within the context of the `default` space. - * @param request + * @param request the request. */ isInDefaultSpace(request: KibanaRequest): boolean; /** * Retrieves the Space associated with the provided request. - * @param request + * @param request the request. */ getActiveSpace(request: KibanaRequest): Promise; /** * Converts the provided space id into the corresponding Saved Objects `namespace` id. - * @param spaceId + * @param spaceId the space id to convert. */ spaceIdToNamespace(spaceId: string): string | undefined; /** * Converts the provided namespace into the corresponding space id. - * @param namespace + * @param namespace the namespace to convert. */ namespaceToSpaceId(namespace: string | undefined): string; } @@ -85,6 +95,9 @@ interface SpacesServiceStartDeps { spacesClientService: SpacesClientServiceStart; } +/** + * Service for interacting with spaces. + */ export class SpacesService { public setup({ basePath }: SpacesServiceSetupDeps): SpacesServiceSetup { return { @@ -96,7 +109,7 @@ export class SpacesService { }; } - public start({ basePath, spacesClientService }: SpacesServiceStartDeps) { + public start({ basePath, spacesClientService }: SpacesServiceStartDeps): SpacesServiceStart { return { getSpaceId: (request: KibanaRequest) => { return this.getSpaceId(request, basePath); diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index ef001edd429fd..a43171fc3b464 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -338,13 +338,13 @@ export function getSpacesUsageCollector( available: { type: 'boolean', _meta: { - description: 'Indicates if the spaces feature is available in this installation.', + description: 'Indicates if the Spaces feature is available in this installation.', }, }, enabled: { type: 'boolean', _meta: { - description: 'Indicates if the spaces feature is enabled in this installation.', + description: 'Indicates if the Spaces feature is enabled in this installation.', }, }, count: { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/__snapshots__/geo_index_pattern_select.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/__snapshots__/geo_index_pattern_select.test.tsx.snap new file mode 100644 index 0000000000000..84ba07b3c015b --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/__snapshots__/geo_index_pattern_select.test.tsx.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render with error when index pattern does not have geo_point field 1`] = ` + + + + + +`; + +exports[`should render without error after mounting 1`] = ` + + + + + +`; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.test.tsx new file mode 100644 index 0000000000000..f587e8ed35e17 --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.test.tsx @@ -0,0 +1,73 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import { GeoIndexPatternSelect } from './geo_index_pattern_select'; +import { IndexPatternsContract } from 'src/plugins/data/public'; +import { HttpSetup } from 'kibana/public'; + +class MockIndexPatternSelectComponent extends React.Component { + render() { + return 'MockIndexPatternSelectComponent'; + } +} + +function makeMockIndexPattern(id: string, fields: unknown) { + return { + id, + fields, + }; +} + +const mockIndexPatternService: IndexPatternsContract = ({ + get(id: string) { + if (id === 'foobar_with_geopoint') { + return makeMockIndexPattern(id, [{ type: 'geo_point' }]); + } else if (id === 'foobar_without_geopoint') { + return makeMockIndexPattern(id, [{ type: 'string' }]); + } + }, +} as unknown) as IndexPatternsContract; + +test('should render without error after mounting', async () => { + const component = shallow( + {}} + value={'foobar_with_geopoint'} + includedGeoTypes={['geo_point']} + indexPatternService={mockIndexPatternService} + IndexPatternSelectComponent={MockIndexPatternSelectComponent} + /> + ); + + // Ensure all promises resolve + await new Promise((resolve) => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + expect(component).toMatchSnapshot(); +}); + +test('should render with error when index pattern does not have geo_point field', async () => { + const component = shallow( + {}} + value={'foobar_without_geopoint'} + includedGeoTypes={['geo_point']} + indexPatternService={mockIndexPatternService} + IndexPatternSelectComponent={MockIndexPatternSelectComponent} + /> + ); + + // Ensure all promises resolve + await new Promise((resolve) => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.tsx index 9bb29b65e7454..f024499122ce5 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/util_components/geo_index_pattern_select.tsx @@ -41,9 +41,12 @@ export class GeoIndexPatternSelect extends Component { componentDidMount() { this._isMounted = true; + if (this.props.value) { + this._loadIndexPattern(this.props.value); + } } - _onIndexPatternSelect = async (indexPatternId: string) => { + _loadIndexPattern = async (indexPatternId: string) => { if (!indexPatternId || indexPatternId.length === 0 || !this.props.indexPatternService) { return; } @@ -55,14 +58,22 @@ export class GeoIndexPatternSelect extends Component { return; } - // method may be called again before 'get' returns - // ignore response when fetched index pattern does not match active index pattern - if (this._isMounted && indexPattern.id === indexPatternId) { - this.setState({ - doesIndexPatternHaveGeoField: indexPattern.fields.some((field) => { - return this.props.includedGeoTypes.includes(field.type); - }), - }); + if (!this._isMounted || indexPattern.id !== indexPatternId) { + return; + } + + this.setState({ + doesIndexPatternHaveGeoField: indexPattern.fields.some((field) => { + return this.props.includedGeoTypes.includes(field.type); + }), + }); + + return indexPattern; + }; + + _onIndexPatternSelect = async (indexPatternId: string) => { + const indexPattern = await this._loadIndexPattern(indexPatternId); + if (indexPattern) { this.props.onChange(indexPattern); } }; @@ -123,9 +134,14 @@ export class GeoIndexPatternSelect extends Component { const isIndexPatternInvalid = !!this.props.value && !this.state.doesIndexPatternHaveGeoField; const error = isIndexPatternInvalid ? i18n.translate('xpack.stackAlerts.geoContainment.noGeoFieldInIndexPattern.message', { - defaultMessage: 'Index pattern does not contain any geospatial fields', + defaultMessage: + 'Index pattern does not contain any allowed geospatial fields. Must have one of type {geoFields}.', + values: { + geoFields: this.props.includedGeoTypes.join(', '), + }, }) : ''; + return ( <> {this._renderNoIndexPatternWarning()} diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts index 6015881270405..e3d379f2869b9 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts @@ -120,6 +120,7 @@ export interface GeoContainmentParams extends AlertTypeParams { export interface GeoContainmentState extends AlertTypeState { shapesFilters: Record; shapesIdsNamesMap: Record; + prevLocationMap: Record; } export interface GeoContainmentInstanceState extends AlertInstanceState { location: number[]; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts index 15a6564395c16..754af920b009e 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts @@ -16,6 +16,7 @@ import { GeoContainmentInstanceState, GeoContainmentAlertType, GeoContainmentInstanceContext, + GeoContainmentState, } from './alert_type'; export type LatestEntityLocation = GeoContainmentInstanceState; @@ -141,7 +142,7 @@ export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType[ params, alertId, state, - }) { + }): Promise { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( @@ -176,8 +177,7 @@ export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType[ } const currLocationMap: Map = transformResults( - // @ts-expect-error body doesn't exist on currentIntervalResults - currentIntervalResults?.body, + currentIntervalResults, params.dateField, params.geoField ); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response.json b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response.json index 70edbd09aa5a1..b5751e527df4d 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response.json +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response.json @@ -1,167 +1,246 @@ { - "took" : 2760, - "timed_out" : false, - "_shards" : { - "total" : 1, - "successful" : 1, - "skipped" : 0, - "failed" : 0 - }, - "hits" : { - "total" : { - "value" : 10000, - "relation" : "gte" + "body": { + "took":194, + "timed_out":false, + "_shards":{ + "total":1, + "successful":1, + "skipped":0, + "failed":0 }, - "max_score" : 0.0, - "hits" : [] - }, - "aggregations" : { - "shapes" : { - "meta" : { }, - "buckets" : { - "0DrJu3QB6yyY-xQxv6Ip" : { - "doc_count" : 1047, - "entitySplit" : { - "doc_count_error_upper_bound" : 0, - "sum_other_doc_count" : 957, - "buckets" : [ - { - "key" : "936", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "N-ng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "@timestamp" : [ - "2020-09-28T18:01:41.190Z" - ], - "location" : [ - "40.62806099653244, -82.8814151789993" - ], - "entity_id" : [ - "936" + "hits":{ + "total":{ + "value":18, + "relation":"eq" + }, + "max_score":null, + "hits":[ + + ] + }, + "aggregations":{ + "shapes":{ + "meta":{ + + }, + "buckets":{ + "k1ATGXkBsFLYN2Tj6AAk":{ + "doc_count":0, + "entitySplit":{ + "doc_count_error_upper_bound":0, + "sum_other_doc_count":0, + "buckets":[ + + ] + } + }, + "kFATGXkBsFLYN2Tj6AAk":{ + "doc_count":2, + "entitySplit":{ + "doc_count_error_upper_bound":0, + "sum_other_doc_count":0, + "buckets":[ + { + "key":"0", + "doc_count":1, + "entityHits":{ + "hits":{ + "total":{ + "value":1, + "relation":"eq" + }, + "max_score":null, + "hits":[ + { + "_index":"tracks", + "_id":"ZVBoGXkBsFLYN2Tj1wmV", + "_score":null, + "fields":{ + "@timestamp":[ + "2021-04-28T16:56:11.923Z" + ], + "location":[ + "40.751759740523994, -73.99018926545978" + ], + "entity_id":[ + "0" + ] + }, + "sort":[ + 1619628971923 ] - }, - "sort" : [ - 1601316101190 - ] - } - ] + } + ] + } } - } - }, - { - "key" : "AAL2019", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "iOng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "@timestamp" : [ - "2020-09-28T18:01:41.191Z" - ], - "location" : [ - "39.006176185794175, -82.22068064846098" - ], - "entity_id" : [ - "AAL2019" + }, + { + "key":"1", + "doc_count":1, + "entityHits":{ + "hits":{ + "total":{ + "value":1, + "relation":"eq" + }, + "max_score":null, + "hits":[ + { + "_index":"tracks", + "_id":"ZlBoGXkBsFLYN2Tj1wmV", + "_score":null, + "fields":{ + "@timestamp":[ + "2021-04-28T16:56:11.923Z" + ], + "location":[ + "40.75449890457094, -73.99561604484916" + ], + "entity_id":[ + "1" + ] + }, + "sort":[ + 1619628971923 ] - }, - "sort" : [ - 1601316101191 - ] - } - ] + } + ] + } } } - }, - { - "key" : "AAL2323", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "n-ng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "@timestamp" : [ - "2020-09-28T18:01:41.191Z" - ], - "location" : [ - "41.6677269525826, -84.71324851736426" - ], - "entity_id" : [ - "AAL2323" + ] + } + }, + "kVATGXkBsFLYN2Tj6AAk":{ + "doc_count":0, + "entitySplit":{ + "doc_count_error_upper_bound":0, + "sum_other_doc_count":0, + "buckets":[ + + ] + } + }, + "lVATGXkBsFLYN2Tj6AAk":{ + "doc_count":0, + "entitySplit":{ + "doc_count_error_upper_bound":0, + "sum_other_doc_count":0, + "buckets":[ + + ] + } + }, + "other":{ + "doc_count":15, + "entitySplit":{ + "doc_count_error_upper_bound":0, + "sum_other_doc_count":0, + "buckets":[ + { + "key":"2", + "doc_count":6, + "entityHits":{ + "hits":{ + "total":{ + "value":6, + "relation":"eq" + }, + "max_score":null, + "hits":[ + { + "_index":"tracks", + "_id":"Z1BoGXkBsFLYN2Tj1wmV", + "_score":null, + "fields":{ + "@timestamp":[ + "2021-04-28T16:56:11.923Z" + ], + "location":[ + "40.7667087810114, -73.98662586696446" + ], + "entity_id":[ + "2" + ] + }, + "sort":[ + 1619628971923 ] - }, - "sort" : [ - 1601316101191 - ] - } - ] + } + ] + } } - } - }, - { - "key" : "ABD5250", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "GOng1XQB6yyY-xQxnGWM", - "_score" : null, - "fields" : { - "@timestamp" : [ - "2020-09-28T18:01:41.192Z" - ], - "location" : [ - "39.07997465226799, 6.073727197945118" - ], - "entity_id" : [ - "ABD5250" + }, + { + "key":"1", + "doc_count":5, + "entityHits":{ + "hits":{ + "total":{ + "value":5, + "relation":"eq" + }, + "max_score":null, + "hits":[ + { + "_index":"tracks", + "_id":"Y1BoGXkBsFLYN2TjsAlp", + "_score":null, + "fields":{ + "@timestamp":[ + "2021-04-28T16:56:01.896Z" + ], + "location":[ + "40.755913141183555, -73.99459345266223" + ], + "entity_id":[ + "1" + ] + }, + "sort":[ + 1619628961896 + ] + } + ] + } + } + }, + { + "key":"0", + "doc_count":4, + "entityHits":{ + "hits":{ + "total":{ + "value":4, + "relation":"eq" + }, + "max_score":null, + "hits":[ + { + "_index":"tracks", + "_id":"YlBoGXkBsFLYN2TjsAlp", + "_score":null, + "fields":{ + "@timestamp":[ + "2021-04-28T16:56:01.896Z" + ], + "location":[ + "40.7506317878142, -73.98968475870788" + ], + "entity_id":[ + "0" + ] + }, + "sort":[ + 1619628961896 ] - }, - "sort" : [ - 1601316101192 - ] - } - ] + } + ] + } } } - } - ] + ] + } } } } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_shapes.json b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_shapes.json new file mode 100644 index 0000000000000..dfe5a8b7f6ec1 --- /dev/null +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_shapes.json @@ -0,0 +1,428 @@ +{ + "body": { + "took":1, + "timed_out":false, + "_shards":{ + "total":1, + "successful":1, + "skipped":0, + "failed":0 + }, + "hits":{ + "total":{ + "value":11, + "relation":"eq" + }, + "max_score":1, + "hits":[ + { + "_index":"manhattan_boundaries", + "_id":"waFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.96772861480713, + 40.76060200607076 + ], + [ + -73.96805047988892, + 40.7601631739201 + ], + [ + -73.96732091903687, + 40.7598706175435 + ], + [ + -73.96693468093872, + 40.760471982031845 + ], + [ + -73.96759986877441, + 40.76078078870895 + ], + [ + -73.96772861480713, + 40.76060200607076 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"wqFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.97641897201538, + 40.75618104606283 + ], + [ + -73.97865056991577, + 40.75371038152863 + ], + [ + -73.97770643234252, + 40.75323899431278 + ], + [ + -73.9788007736206, + 40.75187357799962 + ], + [ + -73.97671937942503, + 40.751060816881505 + ], + [ + -73.97500276565552, + 40.75377540019266 + ], + [ + -73.97639751434325, + 40.75460438258571 + ], + [ + -73.9755392074585, + 40.755985996937774 + ], + [ + -73.97641897201538, + 40.75618104606283 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"w6FXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.98592472076415, + 40.75957805987928 + ], + [ + -73.98695468902588, + 40.75566091379097 + ], + [ + -73.98573160171509, + 40.75553088008716 + ], + [ + -73.98465871810913, + 40.75946428710659 + ], + [ + -73.98592472076415, + 40.75957805987928 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"xKFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.9894437789917, + 40.75161349552273 + ], + [ + -73.98914337158203, + 40.75206863918968 + ], + [ + -73.99575233459473, + 40.75486445336327 + ], + [ + -73.99819850921631, + 40.75148345390278 + ], + [ + -73.9914608001709, + 40.74881754464601 + ], + [ + -73.9894437789917, + 40.75161349552273 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"xaFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.96914482116699, + 40.75874913950493 + ], + [ + -73.96946668624878, + 40.758229027325804 + ], + [ + -73.96856546401978, + 40.75793646243674 + ], + [ + -73.96824359893799, + 40.75845657690492 + ], + [ + -73.96914482116699, + 40.75874913950493 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"xqFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.96953105926514, + 40.7581640130173 + ], + [ + -73.9699387550354, + 40.75749761268889 + ], + [ + -73.96923065185547, + 40.75728631362887 + ], + [ + -73.96862983703613, + 40.757920208794026 + ], + [ + -73.96953105926514, + 40.7581640130173 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"x6FXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.97045373916626, + 40.75679869785023 + ], + [ + -73.97079706192015, + 40.75629482445485 + ], + [ + -73.96998167037964, + 40.756051013376364 + ], + [ + -73.96961688995361, + 40.756554888619675 + ], + [ + -73.97045373916626, + 40.75679869785023 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"yKFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.98412227630615, + 40.75479943576424 + ], + [ + -73.98498058319092, + 40.75351532515499 + ], + [ + -73.98191213607788, + 40.75219867966512 + ], + [ + -73.9808177947998, + 40.75340154200611 + ], + [ + -73.98412227630615, + 40.75479943576424 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"yaFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.99725437164307, + 40.74498104863726 + ], + [ + -74.00386333465576, + 40.736136757139285 + ], + [ + -73.99703979492188, + 40.73334015558748 + ], + [ + -73.9897871017456, + 40.74153451605774 + ], + [ + -73.99725437164307, + 40.74498104863726 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"yqFXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.98830652236938, + 40.75075196505171 + ], + [ + -73.9885640144348, + 40.74759834321152 + ], + [ + -73.98761987686157, + 40.747582087041366 + ], + [ + -73.98751258850098, + 40.74816730666263 + ], + [ + -73.98807048797607, + 40.74826484276548 + ], + [ + -73.9875340461731, + 40.75075196505171 + ], + [ + -73.98830652236938, + 40.75075196505171 + ] + ] + ] + } + } + }, + { + "_index":"manhattan_boundaries", + "_id":"y6FXH3kBi9P-_6qn8c8A", + "_score":1, + "_source":{ + "coordinates":{ + "type":"Polygon", + "coordinates":[ + [ + [ + -73.9824914932251, + 40.7467692734681 + ], + [ + -73.98356437683105, + 40.7452411570555 + ], + [ + -73.9813756942749, + 40.74446082874893 + ], + [ + -73.98030281066895, + 40.745696344339564 + ], + [ + -73.9824914932251, + 40.7467692734681 + ] + ] + ] + } + } + } + ] + } + } +} + diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_with_nesting.json b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_with_nesting.json index a4b7b6872b341..9baf58465c38e 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_with_nesting.json +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/es_sample_response_with_nesting.json @@ -1,167 +1,169 @@ { - "took" : 2760, - "timed_out" : false, - "_shards" : { - "total" : 1, - "successful" : 1, - "skipped" : 0, - "failed" : 0 - }, - "hits" : { - "total" : { - "value" : 10000, - "relation" : "gte" + "body": { + "took" : 2760, + "timed_out" : false, + "_shards" : { + "total" : 1, + "successful" : 1, + "skipped" : 0, + "failed" : 0 }, - "max_score" : 0.0, - "hits" : [] - }, - "aggregations" : { - "shapes" : { - "meta" : { }, - "buckets" : { - "0DrJu3QB6yyY-xQxv6Ip" : { - "doc_count" : 1047, - "entitySplit" : { - "doc_count_error_upper_bound" : 0, - "sum_other_doc_count" : 957, - "buckets" : [ - { - "key" : "936", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "N-ng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "time_data.@timestamp" : [ - "2020-09-28T18:01:41.190Z" - ], - "geo.coords.location" : [ - "40.62806099653244, -82.8814151789993" - ], - "entity_id" : [ - "936" + "hits" : { + "total" : { + "value" : 10000, + "relation" : "gte" + }, + "max_score" : 0.0, + "hits" : [] + }, + "aggregations" : { + "shapes" : { + "meta" : { }, + "buckets" : { + "0DrJu3QB6yyY-xQxv6Ip" : { + "doc_count" : 1047, + "entitySplit" : { + "doc_count_error_upper_bound" : 0, + "sum_other_doc_count" : 957, + "buckets" : [ + { + "key" : "936", + "doc_count" : 9, + "entityHits" : { + "hits" : { + "total" : { + "value" : 9, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "flight_tracks", + "_id" : "N-ng1XQB6yyY-xQxnGSM", + "_score" : null, + "fields" : { + "time_data.@timestamp" : [ + "2020-09-28T18:01:41.190Z" + ], + "geo.coords.location" : [ + "40.62806099653244, -82.8814151789993" + ], + "entity_id" : [ + "936" + ] + }, + "sort" : [ + 1601316101190 ] - }, - "sort" : [ - 1601316101190 - ] - } - ] + } + ] + } } - } - }, - { - "key" : "AAL2019", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "iOng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "time_data.@timestamp" : [ - "2020-09-28T18:01:41.191Z" - ], - "geo.coords.location" : [ - "39.006176185794175, -82.22068064846098" - ], - "entity_id" : [ - "AAL2019" + }, + { + "key" : "AAL2019", + "doc_count" : 9, + "entityHits" : { + "hits" : { + "total" : { + "value" : 9, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "flight_tracks", + "_id" : "iOng1XQB6yyY-xQxnGSM", + "_score" : null, + "fields" : { + "time_data.@timestamp" : [ + "2020-09-28T18:01:41.191Z" + ], + "geo.coords.location" : [ + "39.006176185794175, -82.22068064846098" + ], + "entity_id" : [ + "AAL2019" + ] + }, + "sort" : [ + 1601316101191 ] - }, - "sort" : [ - 1601316101191 - ] - } - ] + } + ] + } } - } - }, - { - "key" : "AAL2323", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "n-ng1XQB6yyY-xQxnGSM", - "_score" : null, - "fields" : { - "time_data.@timestamp" : [ - "2020-09-28T18:01:41.191Z" - ], - "geo.coords.location" : [ - "41.6677269525826, -84.71324851736426" - ], - "entity_id" : [ - "AAL2323" + }, + { + "key" : "AAL2323", + "doc_count" : 9, + "entityHits" : { + "hits" : { + "total" : { + "value" : 9, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "flight_tracks", + "_id" : "n-ng1XQB6yyY-xQxnGSM", + "_score" : null, + "fields" : { + "time_data.@timestamp" : [ + "2020-09-28T18:01:41.191Z" + ], + "geo.coords.location" : [ + "41.6677269525826, -84.71324851736426" + ], + "entity_id" : [ + "AAL2323" + ] + }, + "sort" : [ + 1601316101191 ] - }, - "sort" : [ - 1601316101191 - ] - } - ] + } + ] + } } - } - }, - { - "key" : "ABD5250", - "doc_count" : 9, - "entityHits" : { - "hits" : { - "total" : { - "value" : 9, - "relation" : "eq" - }, - "max_score" : null, - "hits" : [ - { - "_index" : "flight_tracks", - "_id" : "GOng1XQB6yyY-xQxnGWM", - "_score" : null, - "fields" : { - "time_data.@timestamp" : [ - "2020-09-28T18:01:41.192Z" - ], - "geo.coords.location" : [ - "39.07997465226799, 6.073727197945118" - ], - "entity_id" : [ - "ABD5250" + }, + { + "key" : "ABD5250", + "doc_count" : 9, + "entityHits" : { + "hits" : { + "total" : { + "value" : 9, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "flight_tracks", + "_id" : "GOng1XQB6yyY-xQxnGWM", + "_score" : null, + "fields" : { + "time_data.@timestamp" : [ + "2020-09-28T18:01:41.192Z" + ], + "geo.coords.location" : [ + "39.07997465226799, 6.073727197945118" + ], + "entity_id" : [ + "ABD5250" + ] + }, + "sort" : [ + 1601316101192 ] - }, - "sort" : [ - 1601316101192 - ] - } - ] + } + ] + } } } - } - ] + ] + } } } } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts index 429331916ea7d..df2e9df4ba189 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts @@ -6,67 +6,101 @@ */ import _ from 'lodash'; -import sampleJsonResponse from './es_sample_response.json'; -import sampleJsonResponseWithNesting from './es_sample_response_with_nesting.json'; -import { getActiveEntriesAndGenerateAlerts, transformResults } from '../geo_containment'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { AlertServicesMock, alertsMock } from '../../../../../alerting/server/mocks'; +import sampleAggsJsonResponse from './es_sample_response.json'; +import sampleShapesJsonResponse from './es_sample_response_shapes.json'; +import sampleAggsJsonResponseWithNesting from './es_sample_response_with_nesting.json'; +import { + getActiveEntriesAndGenerateAlerts, + transformResults, + getGeoContainmentExecutor, +} from '../geo_containment'; import { OTHER_CATEGORY } from '../es_query_builder'; -import { alertsMock } from '../../../../../alerting/server/mocks'; -import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type'; +import { + GeoContainmentInstanceContext, + GeoContainmentInstanceState, + GeoContainmentParams, +} from '../alert_type'; import { SearchResponse } from 'elasticsearch'; +const alertInstanceFactory = (contextKeys: unknown[], testAlertActionArr: unknown[]) => ( + instanceId: string +) => { + const alertInstance = alertsMock.createAlertInstanceFactory< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(); + alertInstance.scheduleActions.mockImplementation( + (actionGroupId: string, context?: GeoContainmentInstanceContext) => { + // Check subset of alert for comparison to expected results + // @ts-ignore + const contextSubset = _.pickBy(context, (v, k) => contextKeys.includes(k)); + testAlertActionArr.push({ + actionGroupId, + instanceId, + context: contextSubset, + }); + } + ); + return alertInstance; +}; + describe('geo_containment', () => { describe('transformResults', () => { const dateField = '@timestamp'; const geoField = 'location'; it('should correctly transform expected results', async () => { const transformedResults = transformResults( - (sampleJsonResponse as unknown) as SearchResponse, + // @ts-ignore + (sampleAggsJsonResponse.body as unknown) as SearchResponse, dateField, geoField ); expect(transformedResults).toEqual( new Map([ [ - '936', + '0', [ { - dateInShape: '2020-09-28T18:01:41.190Z', - docId: 'N-ng1XQB6yyY-xQxnGSM', - location: [-82.8814151789993, 40.62806099653244], - shapeLocationId: '0DrJu3QB6yyY-xQxv6Ip', + dateInShape: '2021-04-28T16:56:11.923Z', + docId: 'ZVBoGXkBsFLYN2Tj1wmV', + location: [-73.99018926545978, 40.751759740523994], + shapeLocationId: 'kFATGXkBsFLYN2Tj6AAk', }, - ], - ], - [ - 'AAL2019', - [ { - dateInShape: '2020-09-28T18:01:41.191Z', - docId: 'iOng1XQB6yyY-xQxnGSM', - location: [-82.22068064846098, 39.006176185794175], - shapeLocationId: '0DrJu3QB6yyY-xQxv6Ip', + dateInShape: '2021-04-28T16:56:01.896Z', + docId: 'YlBoGXkBsFLYN2TjsAlp', + location: [-73.98968475870788, 40.7506317878142], + shapeLocationId: 'other', }, ], ], [ - 'AAL2323', + '1', [ { - dateInShape: '2020-09-28T18:01:41.191Z', - docId: 'n-ng1XQB6yyY-xQxnGSM', - location: [-84.71324851736426, 41.6677269525826], - shapeLocationId: '0DrJu3QB6yyY-xQxv6Ip', + dateInShape: '2021-04-28T16:56:11.923Z', + docId: 'ZlBoGXkBsFLYN2Tj1wmV', + location: [-73.99561604484916, 40.75449890457094], + shapeLocationId: 'kFATGXkBsFLYN2Tj6AAk', + }, + { + dateInShape: '2021-04-28T16:56:01.896Z', + docId: 'Y1BoGXkBsFLYN2TjsAlp', + location: [-73.99459345266223, 40.755913141183555], + shapeLocationId: 'other', }, ], ], [ - 'ABD5250', + '2', [ { - dateInShape: '2020-09-28T18:01:41.192Z', - docId: 'GOng1XQB6yyY-xQxnGWM', - location: [6.073727197945118, 39.07997465226799], - shapeLocationId: '0DrJu3QB6yyY-xQxv6Ip', + dateInShape: '2021-04-28T16:56:11.923Z', + docId: 'Z1BoGXkBsFLYN2Tj1wmV', + location: [-73.98662586696446, 40.7667087810114], + shapeLocationId: 'other', }, ], ], @@ -78,7 +112,8 @@ describe('geo_containment', () => { const nestedGeoField = 'geo.coords.location'; it('should correctly transform expected results if fields are nested', async () => { const transformedResults = transformResults( - (sampleJsonResponseWithNesting as unknown) as SearchResponse, + // @ts-ignore + (sampleAggsJsonResponseWithNesting.body as unknown) as SearchResponse, nestedDateField, nestedGeoField ); @@ -181,7 +216,7 @@ describe('geo_containment', () => { ], ]); - const expectedContext = [ + const expectedAlertResults = [ { actionGroupId: 'Tracked entity contained', context: { @@ -213,28 +248,9 @@ describe('geo_containment', () => { instanceId: 'c-789', }, ]; + const contextKeys = Object.keys(expectedAlertResults[0].context); const emptyShapesIdsNamesMap = {}; - const alertInstanceFactory = (instanceId: string) => { - const alertInstance = alertsMock.createAlertInstanceFactory< - GeoContainmentInstanceState, - GeoContainmentInstanceContext - >(); - alertInstance.scheduleActions.mockImplementation( - (actionGroupId: string, context?: GeoContainmentInstanceContext) => { - const contextKeys = Object.keys(expectedContext[0].context); - const contextSubset = _.pickBy(context, (v, k) => contextKeys.includes(k)); - testAlertActionArr.push({ - actionGroupId, - instanceId, - context: contextSubset, - }); - return alertInstance; - } - ); - return alertInstance; - }; - const currentDateTime = new Date(); it('should use currently active entities if no older entity entries', () => { @@ -242,13 +258,14 @@ describe('geo_containment', () => { const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( emptyPrevLocationMap, currLocationMap, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); expect(allActiveEntriesMap).toEqual(currLocationMap); - expect(testAlertActionArr).toMatchObject(expectedContext); + expect(testAlertActionArr).toMatchObject(expectedAlertResults); }); + it('should overwrite older identical entity entries', () => { const prevLocationMapWithIdenticalEntityEntry = new Map([ [ @@ -266,13 +283,14 @@ describe('geo_containment', () => { const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( prevLocationMapWithIdenticalEntityEntry, currLocationMap, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); expect(allActiveEntriesMap).toEqual(currLocationMap); - expect(testAlertActionArr).toMatchObject(expectedContext); + expect(testAlertActionArr).toMatchObject(expectedAlertResults); }); + it('should preserve older non-identical entity entries', () => { const prevLocationMapWithNonIdenticalEntityEntry = new Map([ [ @@ -287,7 +305,7 @@ describe('geo_containment', () => { ], ], ]); - const expectedContextPlusD = [ + const expectedAlertResultsPlusD = [ { actionGroupId: 'Tracked entity contained', context: { @@ -298,19 +316,19 @@ describe('geo_containment', () => { }, instanceId: 'd-999', }, - ...expectedContext, + ...expectedAlertResults, ]; const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( prevLocationMapWithNonIdenticalEntityEntry, currLocationMap, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); expect(allActiveEntriesMap).not.toEqual(currLocationMap); expect(allActiveEntriesMap.has('d')).toBeTruthy(); - expect(testAlertActionArr).toMatchObject(expectedContextPlusD); + expect(testAlertActionArr).toMatchObject(expectedAlertResultsPlusD); }); it('should remove "other" entries and schedule the expected number of actions', () => { @@ -327,12 +345,12 @@ describe('geo_containment', () => { const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( emptyPrevLocationMap, currLocationMapWithOther, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); expect(allActiveEntriesMap).toEqual(currLocationMap); - expect(testAlertActionArr).toMatchObject(expectedContext); + expect(testAlertActionArr).toMatchObject(expectedAlertResults); }); it('should generate multiple alerts per entity if found in multiple shapes in interval', () => { @@ -360,7 +378,7 @@ describe('geo_containment', () => { getActiveEntriesAndGenerateAlerts( emptyPrevLocationMap, currLocationMapWithThreeMore, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); @@ -397,7 +415,7 @@ describe('geo_containment', () => { const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( emptyPrevLocationMap, currLocationMapWithOther, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); @@ -429,7 +447,7 @@ describe('geo_containment', () => { const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( emptyPrevLocationMap, currLocationMapWithOther, - alertInstanceFactory, + alertInstanceFactory(contextKeys, testAlertActionArr), emptyShapesIdsNamesMap, currentDateTime ); @@ -445,4 +463,171 @@ describe('geo_containment', () => { ); }); }); + + describe('getGeoContainmentExecutor', () => { + // Params needed for all tests + const expectedAlertResults = [ + { + actionGroupId: 'Tracked entity contained', + context: { + containingBoundaryId: 'kFATGXkBsFLYN2Tj6AAk', + entityDocumentId: 'ZVBoGXkBsFLYN2Tj1wmV', + entityId: '0', + entityLocation: 'POINT (-73.99018926545978 40.751759740523994)', + }, + instanceId: '0-kFATGXkBsFLYN2Tj6AAk', + }, + { + actionGroupId: 'Tracked entity contained', + context: { + containingBoundaryId: 'kFATGXkBsFLYN2Tj6AAk', + entityDocumentId: 'ZlBoGXkBsFLYN2Tj1wmV', + entityId: '1', + entityLocation: 'POINT (-73.99561604484916 40.75449890457094)', + }, + instanceId: '1-kFATGXkBsFLYN2Tj6AAk', + }, + ]; + const testAlertActionArr: unknown[] = []; + const mockLogger = loggingSystemMock.createLogger(); + const previousStartedAt = new Date('2021-04-27T16:56:11.923Z'); + const startedAt = new Date('2021-04-29T16:56:11.923Z'); + const geoContainmentParams: GeoContainmentParams = { + index: 'testIndex', + indexId: 'testIndexId', + geoField: 'location', + entity: 'testEntity', + dateField: '@timestamp', + boundaryType: 'testBoundaryType', + boundaryIndexTitle: 'testBoundaryIndexTitle', + boundaryIndexId: 'testBoundaryIndexId', + boundaryGeoField: 'testBoundaryGeoField', + }; + const alertId = 'testAlertId'; + const geoContainmentState = { + shapesFilters: { + testShape: 'thisIsAShape', + }, + shapesIdsNamesMap: {}, + prevLocationMap: {}, + }; + + // Boundary test mocks + const boundaryCall = jest.fn(); + const esAggCall = jest.fn(); + const contextKeys = Object.keys(expectedAlertResults[0].context); + const alertServicesWithSearchMock: AlertServicesMock = { + ...alertsMock.createAlertServices(), + // @ts-ignore + alertInstanceFactory: alertInstanceFactory(contextKeys, testAlertActionArr), + scopedClusterClient: { + asCurrentUser: { + // @ts-ignore + search: jest.fn(({ index }: { index: string }) => { + if (index === geoContainmentParams.boundaryIndexTitle) { + boundaryCall(); + return sampleShapesJsonResponse; + } else { + esAggCall(); + return sampleAggsJsonResponse; + } + }), + }, + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + testAlertActionArr.length = 0; + }); + + it('should query for shapes if state does not contain shapes', async () => { + const executor = await getGeoContainmentExecutor(mockLogger); + const executionResult = await executor({ + previousStartedAt, + startedAt, + // @ts-ignore + services: alertServicesWithSearchMock, + params: geoContainmentParams, + alertId, + // @ts-ignore + state: {}, + }); + if (executionResult && executionResult.shapesFilters) { + expect(boundaryCall.mock.calls.length).toBe(1); + expect(esAggCall.mock.calls.length).toBe(1); + } + expect(testAlertActionArr).toMatchObject(expectedAlertResults); + }); + + it('should not query for shapes if state contains shapes', async () => { + const executor = await getGeoContainmentExecutor(mockLogger); + const executionResult = await executor({ + previousStartedAt, + startedAt, + // @ts-ignore + services: alertServicesWithSearchMock, + params: geoContainmentParams, + alertId, + state: geoContainmentState, + }); + if (executionResult && executionResult.shapesFilters) { + expect(boundaryCall.mock.calls.length).toBe(0); + expect(esAggCall.mock.calls.length).toBe(1); + } + expect(testAlertActionArr).toMatchObject(expectedAlertResults); + }); + + it('should carry through shapes filters in state to next call unmodified', async () => { + const executor = await getGeoContainmentExecutor(mockLogger); + const executionResult = await executor({ + previousStartedAt, + startedAt, + // @ts-ignore + services: alertServicesWithSearchMock, + params: geoContainmentParams, + alertId, + state: geoContainmentState, + }); + if (executionResult && executionResult.shapesFilters) { + expect(executionResult.shapesFilters).toEqual(geoContainmentState.shapesFilters); + } + expect(testAlertActionArr).toMatchObject(expectedAlertResults); + }); + + it('should return previous locations map', async () => { + const expectedPrevLocationMap = { + '0': [ + { + dateInShape: '2021-04-28T16:56:11.923Z', + docId: 'ZVBoGXkBsFLYN2Tj1wmV', + location: [-73.99018926545978, 40.751759740523994], + shapeLocationId: 'kFATGXkBsFLYN2Tj6AAk', + }, + ], + '1': [ + { + dateInShape: '2021-04-28T16:56:11.923Z', + docId: 'ZlBoGXkBsFLYN2Tj1wmV', + location: [-73.99561604484916, 40.75449890457094], + shapeLocationId: 'kFATGXkBsFLYN2Tj6AAk', + }, + ], + }; + const executor = await getGeoContainmentExecutor(mockLogger); + const executionResult = await executor({ + previousStartedAt, + startedAt, + // @ts-ignore + services: alertServicesWithSearchMock, + params: geoContainmentParams, + alertId, + state: geoContainmentState, + }); + if (executionResult && executionResult.prevLocationMap) { + expect(executionResult.prevLocationMap).toEqual(expectedPrevLocationMap); + } + expect(testAlertActionArr).toMatchObject(expectedAlertResults); + }); + }); }); diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index d2fbbf147efd5..9c42ce424b448 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -5218,13 +5218,13 @@ "available": { "type": "boolean", "_meta": { - "description": "Indicates if the spaces feature is available in this installation." + "description": "Indicates if the Spaces feature is available in this installation." } }, "enabled": { "type": "boolean", "_meta": { - "description": "Indicates if the spaces feature is enabled in this installation." + "description": "Indicates if the Spaces feature is enabled in this installation." } }, "count": { @@ -5405,13 +5405,22 @@ "monitor_name_stats": { "properties": { "avg_length": { - "type": "float" + "type": "float", + "_meta": { + "description": "This field represents the average length of monitor names" + } }, "max_length": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the max length of monitor names" + } }, "min_length": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the min length of monitor names" + } } } }, @@ -5419,21 +5428,36 @@ "type": "long" }, "no_of_unique_monitors": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the number of unique configured monitors" + } }, "no_of_unique_observer_locations": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the number of unique monitor observer locations" + } }, "observer_location_name_stats": { "properties": { "avg_length": { - "type": "float" + "type": "float", + "_meta": { + "description": "This field represents the average length of monitor observer location names" + } }, "max_length": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the max length of monitor observer location names" + } }, "min_length": { - "type": "long" + "type": "long", + "_meta": { + "description": "This field represents the min length of monitor observer location names" + } } } }, @@ -5442,6 +5466,43 @@ }, "settings_page": { "type": "long" + }, + "fleet_monitor_name_stats": { + "properties": { + "avg_length": { + "type": "float", + "_meta": { + "description": "This field represents the average length of fleet managed monitor names" + } + }, + "max_length": { + "type": "long", + "_meta": { + "description": "This field represents the max length of fleet managed monitor names" + } + }, + "min_length": { + "type": "long", + "_meta": { + "description": "This field represents the min length of fleet managed monitor names" + } + } + } + }, + "fleet_monitor_frequency": { + "type": "array", + "items": { + "type": "long", + "_meta": { + "description": "This field represents the average the monitor frequency of fleet managed monitors" + } + } + }, + "fleet_no_of_unique_monitors": { + "type": "long", + "_meta": { + "description": "This field represents the number of unique configured fleet managed monitors" + } } } } diff --git a/x-pack/plugins/transform/common/utils/field_utils.test.ts b/x-pack/plugins/transform/common/utils/field_utils.test.ts new file mode 100644 index 0000000000000..bb38b3888bd75 --- /dev/null +++ b/x-pack/plugins/transform/common/utils/field_utils.test.ts @@ -0,0 +1,59 @@ +/* + * 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 { hasKeywordDuplicate, isKeywordDuplicate, removeKeywordPostfix } from './field_utils'; + +const allFields = new Set([ + 'field1', + 'field2', + 'field2.keyword', + 'field3.keyword', + 'field3.keyword.keyword', + 'field4.keyword.b', + 'field4.keyword.b.keyword', +]); + +describe('field_utils: hasKeywordDuplicate()', () => { + it('returns true when a corresponding keyword field is available', () => { + expect(hasKeywordDuplicate('field2', allFields)).toBe(true); + expect(hasKeywordDuplicate('field3.keyword', allFields)).toBe(true); + expect(hasKeywordDuplicate('field4.keyword.b', allFields)).toBe(true); + }); + it('returns false when a corresponding keyword field is not available', () => { + expect(hasKeywordDuplicate('field1', allFields)).toBe(false); + expect(hasKeywordDuplicate('field2.keyword', allFields)).toBe(false); + expect(hasKeywordDuplicate('field3.keyword.keyword', allFields)).toBe(false); + expect(hasKeywordDuplicate('field4.keyword.b.keyword', allFields)).toBe(false); + }); +}); + +describe('field_utils: isKeywordDuplicate()', () => { + it('returns true when a corresponding field without keyword postfix is available', () => { + expect(isKeywordDuplicate('field2.keyword', allFields)).toBe(true); + expect(isKeywordDuplicate('field3.keyword.keyword', allFields)).toBe(true); + expect(isKeywordDuplicate('field4.keyword.b.keyword', allFields)).toBe(true); + }); + it('returns false when a corresponding field without keyword postfix is not available', () => { + expect(isKeywordDuplicate('field1', allFields)).toBe(false); + expect(isKeywordDuplicate('field2', allFields)).toBe(false); + expect(isKeywordDuplicate('field3.keyword', allFields)).toBe(false); + expect(isKeywordDuplicate('field4.keyword.b', allFields)).toBe(false); + }); +}); + +describe('field_utils: removeKeywordPostfix()', () => { + it('removes the keyword postfix', () => { + expect(removeKeywordPostfix('field2.keyword')).toBe('field2'); + expect(removeKeywordPostfix('field3.keyword.keyword')).toBe('field3.keyword'); + expect(removeKeywordPostfix('field4.keyword.b.keyword')).toBe('field4.keyword.b'); + }); + it("returns the field name as is when there's no keyword postfix", () => { + expect(removeKeywordPostfix('field1')).toBe('field1'); + expect(removeKeywordPostfix('field2')).toBe('field2'); + expect(removeKeywordPostfix('field4.keyword.b')).toBe('field4.keyword.b'); + }); +}); diff --git a/x-pack/plugins/transform/common/utils/field_utils.ts b/x-pack/plugins/transform/common/utils/field_utils.ts new file mode 100644 index 0000000000000..cde26f3e657b9 --- /dev/null +++ b/x-pack/plugins/transform/common/utils/field_utils.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const KEYWORD_POSTFIX = '.keyword'; + +// checks if fieldName has a `fieldName.keyword` equivalent in the set of all field names. +export const hasKeywordDuplicate = (fieldName: string, fieldNamesSet: Set): boolean => + fieldNamesSet.has(`${fieldName}${KEYWORD_POSTFIX}`); + +// checks if a fieldName ends with `.keyword` and has a field name equivalent without the postfix in the set of all field names. +export const isKeywordDuplicate = (fieldName: string, fieldNamesSet: Set): boolean => + fieldName.endsWith(KEYWORD_POSTFIX) && fieldNamesSet.has(removeKeywordPostfix(fieldName)); + +// removes the `.keyword` postfix form a field name if applicable +export const removeKeywordPostfix = (fieldName: string): string => + fieldName.replace(new RegExp(`${KEYWORD_POSTFIX}$`), ''); diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index 2dabc6ba1595d..bd1ecd79f4d12 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -14,6 +14,11 @@ import { isEsSearchResponse, isFieldHistogramsResponseSchema, } from '../../../common/api_schemas/type_guards'; +import { + hasKeywordDuplicate, + isKeywordDuplicate, + removeKeywordPostfix, +} from '../../../common/utils/field_utils'; import type { EsSorting, UseIndexDataReturnType } from '../../shared_imports'; import { getErrorMessage } from '../../../common/utils/errors'; @@ -209,14 +214,25 @@ export const useIndexData = ( }; const fetchColumnChartsData = async function () { + const allIndexPatternFieldNames = new Set(indexPattern.fields.map((f) => f.name)); const columnChartsData = await api.getHistogramsForFields( indexPattern.title, columns .filter((cT) => dataGrid.visibleColumns.includes(cT.id)) - .map((cT) => ({ - fieldName: cT.id, - type: getFieldType(cT.schema), - })), + .map((cT) => { + // If a column field name has a corresponding keyword field, + // fetch the keyword field instead to be able to do aggregations. + const fieldName = cT.id; + return hasKeywordDuplicate(fieldName, allIndexPatternFieldNames) + ? { + fieldName: `${fieldName}.keyword`, + type: getFieldType(undefined), + } + : { + fieldName, + type: getFieldType(cT.schema), + }; + }), isDefaultQuery(query) ? matchAllQuery : query, combinedRuntimeMappings ); @@ -226,7 +242,15 @@ export const useIndexData = ( return; } - setColumnCharts(columnChartsData); + setColumnCharts( + // revert field names with `.keyword` used to do aggregations to their original column name + columnChartsData.map((d) => ({ + ...d, + ...(isKeywordDuplicate(d.id, allIndexPatternFieldNames) + ? { id: removeKeywordPostfix(d.id) } + : {}), + })) + ); }; useEffect(() => { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts index 957439810adc7..300626e0570ae 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts @@ -13,6 +13,8 @@ import { } from '../../../../../../../../../../src/plugins/data/public'; import { getNestedProperty } from '../../../../../../../common/utils/object_utils'; +import { removeKeywordPostfix } from '../../../../../../../common/utils/field_utils'; + import { isRuntimeMappings } from '../../../../../../../common/shared_imports'; import { @@ -93,41 +95,44 @@ export function getPivotDropdownOptions( const combinedFields = [...indexPatternFields, ...runtimeFields].sort(sortByLabel); combinedFields.forEach((field) => { + const rawFieldName = field.name; + const displayFieldName = removeKeywordPostfix(rawFieldName); + // Group by const availableGroupByAggs: [] = getNestedProperty(pivotGroupByFieldSupport, field.type); if (availableGroupByAggs !== undefined) { availableGroupByAggs.forEach((groupByAgg) => { // Aggregation name for the group-by is the plain field name. Illegal characters will be removed. - const aggName = field.name.replace(illegalEsAggNameChars, '').trim(); + const aggName = displayFieldName.replace(illegalEsAggNameChars, '').trim(); // Option name in the dropdown for the group-by is in the form of `sum(fieldname)`. - const dropDownName = `${groupByAgg}(${field.name})`; + const dropDownName = `${groupByAgg}(${displayFieldName})`; const groupByOption: DropDownLabel = { label: dropDownName }; groupByOptions.push(groupByOption); groupByOptionsData[dropDownName] = getDefaultGroupByConfig( aggName, dropDownName, - field.name, + rawFieldName, groupByAgg ); }); } // Aggregations - const aggOption: DropDownOption = { label: field.name, options: [] }; + const aggOption: DropDownOption = { label: displayFieldName, options: [] }; const availableAggs: [] = getNestedProperty(pivotAggsFieldSupport, field.type); if (availableAggs !== undefined) { availableAggs.forEach((agg) => { // Aggregation name is formatted like `fieldname.sum`. Illegal characters will be removed. - const aggName = `${field.name.replace(illegalEsAggNameChars, '').trim()}.${agg}`; + const aggName = `${displayFieldName.replace(illegalEsAggNameChars, '').trim()}.${agg}`; // Option name in the dropdown for the aggregation is in the form of `sum(fieldname)`. - const dropDownName = `${agg}(${field.name})`; + const dropDownName = `${agg}(${displayFieldName})`; aggOption.options.push({ label: dropDownName }); aggOptionsData[dropDownName] = getDefaultAggregationConfig( aggName, dropDownName, - field.name, + rawFieldName, agg ); }); diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts index 1dd136cb484fa..80f80d6f1d71b 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms.ts @@ -61,6 +61,7 @@ import { registerTransformsAuditMessagesRoutes } from './transforms_audit_messag import { registerTransformNodesRoutes } from './transforms_nodes'; import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; import { isLatestTransform } from '../../../common/types/transform'; +import { isKeywordDuplicate } from '../../../common/utils/field_utils'; enum TRANSFORM_ACTIONS { STOP = 'stop', @@ -562,9 +563,7 @@ const previewTransformHandler: RequestHandler< ).reduce((acc, [fieldName, fieldCaps]) => { const fieldDefinition = Object.values(fieldCaps)[0]; const isMetaField = fieldDefinition.type.startsWith('_') || fieldName === '_doc_count'; - const isKeywordDuplicate = - fieldName.endsWith('.keyword') && fieldNamesSet.has(fieldName.split('.keyword')[0]); - if (isMetaField || isKeywordDuplicate) { + if (isMetaField || isKeywordDuplicate(fieldName, fieldNamesSet)) { return acc; } acc[fieldName] = { ...fieldDefinition }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 46074a0dbed13..2ca4dc49e89cb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7614,16 +7614,16 @@ "xpack.enterpriseSearch.overview.productCard.launchButton": "{productName}の起動", "xpack.enterpriseSearch.overview.productCard.setupButton": "{productName} のセットアップ", "xpack.enterpriseSearch.overview.setupCta.description": "Elastic App Search および Workplace Search を使用して、アプリまたは社内組織に検索を追加できます。検索が簡単になるとどのような利点があるのかについては、動画をご覧ください。", - "xpack.enterpriseSearch.overview.setupCta.title": "あらゆる規模のチームに対応するエンタープライズ級の機能", "xpack.enterpriseSearch.overview.setupHeading": "セットアップする製品を選択し、開始してください。", "xpack.enterpriseSearch.overview.subheading": "開始する製品を選択します。", "xpack.enterpriseSearch.productName": "エンタープライズサーチ", + "xpack.enterpriseSearch.productSelectorCalloutTitle": "あらゆる規模のチームに対応するエンタープライズ級の機能", "xpack.enterpriseSearch.readOnlyMode.warning": "エンタープライズ サーチは読み取り専用モードです。作成、編集、削除などの変更を実行できません。", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.addField": "フィールドの追加", + "xpack.enterpriseSearch.schema.addFieldModal.addFieldButtonLabel": "フィールドの追加", + "xpack.enterpriseSearch.schema.addFieldModal.description": "追加すると、フィールドはスキーマから削除されます。", + "xpack.enterpriseSearch.schema.addFieldModal.title": "新しいフィールドを追加", "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.correct": "フィールド名には、小文字、数字、アンダースコアのみを使用できます。", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.corrected": "フィールド名が変更されます", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.description": "追加すると、フィールドはスキーマから削除されます。", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.title": "新しいフィールドを追加", + "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.corrected": "フィールド名が変更されます {correctedName}", "xpack.enterpriseSearch.schema.errorsCallout.buttonLabel": "エラーを表示", "xpack.enterpriseSearch.schema.errorsTable.control.review": "見直し", "xpack.enterpriseSearch.schema.errorsTable.heading.error": "エラー", @@ -9914,8 +9914,6 @@ "xpack.indexLifecycleMgmt.appTitle": "インデックスライフサイクルポリシー", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "ポリシーの編集", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "インデックスライフサイクル管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody": "ロールに基づく割り当てを使用するには、1 つ以上のノードを、コールド、ウォーム、またはホットティアに割り当てます。使用可能なノードがない場合、ポリシーは割り当てを完了できません。", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "コールドティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。安価なハードウェアのコールドフェーズにデータを格納します。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "インデックスを凍結", "xpack.indexLifecycleMgmt.common.dataTier.title": "データ割り当て", @@ -9928,7 +9926,6 @@ "xpack.indexLifecycleMgmt.editPolicy.cancelButton": "キャンセル", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body": "Elastic Cloud デプロイを編集し、色ティアを設定します。", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "コールドティアを作成", - "xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "コールドフェーズを有効にする", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "データをコールドティアに移動します。これは、検索パフォーマンスよりもコスト削減を優先するように最適化されています。通常、コールドフェーズではデータが読み取り専用です。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "コールドフェーズ", @@ -9946,7 +9943,6 @@ "xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "新しいスナップショットリポジドリを作成", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "データティアオプション", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "ノード属性を選択", - "xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "コールド", "xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel": "ホット", "xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel": "ウォーム", "xpack.indexLifecycleMgmt.editPolicy.daysOptionLabel": "日", @@ -10011,9 +10007,7 @@ "xpack.indexLifecycleMgmt.editPolicy.minutesOptionLabel": "分", "xpack.indexLifecycleMgmt.editPolicy.nanoSecondsOptionLabel": "ナノ秒", "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "ノード属性を使用して、シャード割り当てを制御します。{learnMoreLink}。", - "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "割り当て構成を修正しない", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "ノードデータを読み込めません", - "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel": "カスタムノード属性が構成されていません", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesReloadButton": "再試行", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsLoadingFailedTitle": "ノード属性詳細を読み込めません", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsReloadButton": "再試行", @@ -10059,7 +10053,6 @@ "xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage": "ポリシー名の頭にアンダーラインを使用することはできず、カンマやスペースを含めることもできません。", "xpack.indexLifecycleMgmt.editPolicy.viewNodeDetailsButton": "選択した属性のノードを表示", "xpack.indexLifecycleMgmt.editPolicy.waitForSnapshot.snapshotPolicyFieldLabel": "ポリシー名 (任意) ", - "xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "ウォームフェーズを有効にする", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "ノードの再起動後にインデックスを復元する優先順位を設定します。優先順位の高いインデックスは優先順位の低いインデックスよりも先に復元されます。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescription": "データをウォームティアに移動します。これは、インデックスパフォーマンスよりも検索パフォーマンスを優先するように最適化されています。ウォームフェーズでは、データの追加または更新頻度は高くありません。", @@ -10203,12 +10196,6 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "ホットフェーズ", "xpack.indexLifecycleMgmt.timeline.title": "ポリシー概要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "ウォームフェーズ", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "ロールに基づく割り当てを使用するには、1つ以上のノードを、ウォームまたはホットティアに割り当てます。使用可能なノードがない場合、ポリシーは割り当てを完了できません。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "ウォームティアに割り当てられているノードがありません", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "このポリシーはコールドフェーズのデータを{tier}ティアノードに移動します。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title": "コールドティアに割り当てられているノードがありません", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "このポリシーはウォームフェーズのデータを{tier}ティアノードに移動します。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "ウォームティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。", "xpack.infra.alerting.alertDropdownTitle": "アラート", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし (グループなし) ", @@ -18763,12 +18750,6 @@ "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewHelpText": "クエリ結果をプレビューするデータのタイムフレームを選択します", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewLabel": "クイッククエリプレビュー", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewSubtitleLoading": "...loading", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageBody.essenceDescription": "アラートを表示する権限のみが付与されています。アラート状態を更新 (アラートを開く、アラートを閉じる) 必要がある場合は、Kibana管理者に連絡してください。", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageBody.messageDetail": "{essence} 関連ドキュメント:{docs}", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageTitle": "アラート状態を変更することはできません", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageBody.essenceDescription": "現在、検出エンジンルールを作成/編集するための必要な権限がありません。サポートについては、管理者にお問い合わせください。", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageBody.messageDetail": "{essence} 関連ドキュメント:{docs}", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageTitle": "ルールアクセス権が必要です", "xpack.securitySolution.detectionEngine.rule.editRule.errorMsgDescription": "{countError, plural, one {このタブ} other {これらのタブ}}に無効な入力があります:{tabHasError}", "xpack.securitySolution.detectionEngine.ruleDescription.mlJobStartedDescription": "開始", "xpack.securitySolution.detectionEngine.ruleDescription.mlJobStoppedDescription": "停止", @@ -21585,7 +21566,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "メールに送信", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel": "電子メールアカウントの構成", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel": "ユーザー名とパスワードは暗号化されます。これらのフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.rememberValuesLabel": "これらの値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "サーバーからメールを送信します。", "xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText": "送信元は有効なメールアドレスではありません。", "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText": "パスワードが必要です。", @@ -21634,7 +21614,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "親問題", "xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "プロジェクトキー", "xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel": "認証資格情報は暗号化されます。これらのフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.rememberValuesLabel": "これらの値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField": "API トークンまたはパスワードが必要です", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField": "URL が必要です。", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField": "説明が必要です。", @@ -21669,7 +21648,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "トリガー", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "グループ (任意) ", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel": "このキーは暗号化されています。このフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.rememberValueLabel": "この値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "PagerDuty アカウントを構成します", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "統合キー", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "PagerDuty でイベントを送信します。", @@ -21749,7 +21727,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText": "Web フック URL は https:// から始める必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "メッセージ", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel": "この URL は暗号化されています。このフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.rememberValueLabel": "この値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "Slack チャネルにメッセージを送信します。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "Slack Web フック URL を作成", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Web フック URL", @@ -21760,7 +21737,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText": "Web フック URL は https:// から始める必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "メッセージ", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel": "この URL は暗号化されています。このフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.rememberValueLabel": "この値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "メッセージを Microsoft Teams チャネルに送信します。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "Microsoft Teams Web フック URL を作成", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel": "Web フック URL", @@ -21779,7 +21755,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "メソド", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "パスワード", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel": "ユーザー名とパスワードは暗号化されます。これらのフィールドの値を再入力してください。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.rememberValuesLabel": "これらの値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "Web サービスにリクエストを送信してください。", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "ユーザー名", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 213e5415ceedd..215cdded5da46 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7684,16 +7684,16 @@ "xpack.enterpriseSearch.overview.productCard.launchButton": "推出 {productName}", "xpack.enterpriseSearch.overview.productCard.setupButton": "设置 {productName}", "xpack.enterpriseSearch.overview.setupCta.description": "通过 Elastic App Search 和 Workplace Search,将搜索添加到您的应用或内部组织中。观看视频,了解方便易用的搜索功能可以帮您做些什么。", - "xpack.enterpriseSearch.overview.setupCta.title": "适用于大型和小型团队的企业级功能", "xpack.enterpriseSearch.overview.setupHeading": "选择产品进行设置并开始使用。", "xpack.enterpriseSearch.overview.subheading": "选择产品开始使用。", "xpack.enterpriseSearch.productName": "企业搜索", + "xpack.enterpriseSearch.productSelectorCalloutTitle": "适用于大型和小型团队的企业级功能", "xpack.enterpriseSearch.readOnlyMode.warning": "企业搜索处于只读模式。您将无法执行更改,例如创建、编辑或删除。", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.addField": "添加字段", + "xpack.enterpriseSearch.schema.addFieldModal.addFieldButtonLabel": "添加字段", + "xpack.enterpriseSearch.schema.addFieldModal.description": "字段添加后,将无法从架构中删除。", + "xpack.enterpriseSearch.schema.addFieldModal.title": "添加新字段", "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.correct": "字段名称只能包含小写字母、数字和下划线", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.corrected": "该字段将被命名", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.description": "字段添加后,将无法从架构中删除。", - "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.title": "添加新字段", + "xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.corrected": "该字段将被命名 {correctedName}", "xpack.enterpriseSearch.schema.errorsCallout.buttonLabel": "查看错误", "xpack.enterpriseSearch.schema.errorsTable.control.review": "复查", "xpack.enterpriseSearch.schema.errorsTable.heading.error": "错误", @@ -10045,8 +10045,6 @@ "xpack.indexLifecycleMgmt.appTitle": "索引生命周期策略", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "编辑策略", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "索引生命周期管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody": "至少将一个节点分配到冷层、温层或热层,以使用基于角色的分配。如果没有可用节点,则策略无法完成分配。", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到冷层的节点", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。将处于冷阶段的数据存储在成本较低的硬件上。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "冻结索引", "xpack.indexLifecycleMgmt.common.dataTier.title": "数据分配", @@ -10059,7 +10057,6 @@ "xpack.indexLifecycleMgmt.editPolicy.cancelButton": "取消", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body": "编辑您的 Elastic Cloud 部署以设置冷层。", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "创建冷层", - "xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "激活冷阶段", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "将数据移到经过优化后节省了成本但牺牲了搜索性能的冷层。数据在冷阶段通常为只读。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "冷阶段", @@ -10077,7 +10074,6 @@ "xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "创建新的快照库", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "数据层选项", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "选择节点属性", - "xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "冷", "xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel": "热", "xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel": "温", "xpack.indexLifecycleMgmt.editPolicy.daysOptionLabel": "天", @@ -10142,9 +10138,7 @@ "xpack.indexLifecycleMgmt.editPolicy.minutesOptionLabel": "分钟", "xpack.indexLifecycleMgmt.editPolicy.nanoSecondsOptionLabel": "纳秒", "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "使用节点属性控制分片分配。{learnMoreLink}。", - "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "切勿修改分配配置", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "无法加载节点数据", - "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel": "未配置定制节点属性", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesReloadButton": "重试", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsLoadingFailedTitle": "无法加载节点属性详情", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsReloadButton": "重试", @@ -10190,7 +10184,6 @@ "xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage": "策略名称不能以下划线开头,且不能包含逗号或空格。", "xpack.indexLifecycleMgmt.editPolicy.viewNodeDetailsButton": "查看具有选定属性的节点", "xpack.indexLifecycleMgmt.editPolicy.waitForSnapshot.snapshotPolicyFieldLabel": "策略名称 (可选) ", - "xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "激活温阶段", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "设置在节点重新启动后恢复索引的优先级。较高优先级的索引会在较低优先级的索引之前恢复。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescription": "将数据移到优化了搜索性能但牺牲了索引性能的温层。在温层不经常添加或更新数据。", @@ -10338,12 +10331,6 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "热阶段", "xpack.indexLifecycleMgmt.timeline.title": "策略摘要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "温阶段", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "至少将一个节点分配到温层或冷层,以使用基于角色的分配。如果没有可用节点,则策略无法完成分配。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到温层的节点", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "此策略会改为将冷阶段的数据移到{tier}层节点。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title": "没有分配到冷层的节点", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "此策略会改为将温阶段的数据移到{tier}层节点。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "没有分配到温层的节点", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。", "xpack.infra.alerting.alertDropdownTitle": "告警", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容 (未分组) ", @@ -19031,12 +19018,6 @@ "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewHelpText": "选择数据的时间范围以预览查询结果", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewLabel": "快速查询预览", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewSubtitleLoading": "...正在加载", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageBody.essenceDescription": "您仅有权查看告警。如果您需要更新告警状态 (打开或关闭告警) ,请联系您的 Kibana 管理员。", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageBody.messageDetail": "{essence} 相关文档:{docs}", - "xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageTitle": "您无法更改告警状态", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageBody.essenceDescription": "您当前缺少所需的权限,无法创建/编辑检测引擎规则。有关进一步帮助,请联系您的管理员。", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageBody.messageDetail": "{essence} 相关文档:{docs}", - "xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageTitle": "需要规则权限", "xpack.securitySolution.detectionEngine.rule.editRule.errorMsgDescription": "您在{countError, plural, other {以下选项卡}}中的输入无效:{tabHasError}", "xpack.securitySolution.detectionEngine.ruleDescription.mlJobStartedDescription": "已启动", "xpack.securitySolution.detectionEngine.ruleDescription.mlJobStoppedDescription": "已停止", @@ -21933,7 +21914,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "发送到电子邮件", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.configureAccountsHelpLabel": "配置电子邮件帐户", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel": "用户名和密码已加密。请为这些字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.rememberValuesLabel": "请记住这些值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "从您的服务器发送电子邮件。", "xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText": "发送者电子邮件地址无效。", "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText": "“密码”必填。", @@ -21982,7 +21962,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "父问题", "xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "项目键", "xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel": "验证凭据已加密。请为这些字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.jira.rememberValuesLabel": "请记住这些值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField": "“API 令牌”或“密码”必填", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField": "“URL”必填。", "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField": "“描述”必填。", @@ -22017,7 +21996,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "触发", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "组 (可选) ", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel": "此密钥已加密。请为此字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.rememberValueLabel": "请记住此值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "配置 PagerDuty 帐户", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "集成密钥", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "在 PagerDuty 中发送事件。", @@ -22097,7 +22075,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText": "Webhook URL 必须以 https:// 开头。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "消息", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel": "此 URL 已加密。请为此字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.rememberValueLabel": "请记住此值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "向 Slack 频道或用户发送消息。", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "创建 Slack webhook URL", "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Webhook URL", @@ -22108,7 +22085,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText": "Webhook URL 必须以 https:// 开头。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "消息", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel": "此 URL 已加密。请为此字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.rememberValueLabel": "请记住此值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "向 Microsoft Teams 频道发送消息。", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "创建 Microsoft Teams Webhook URL", "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel": "Webhook URL", @@ -22127,7 +22103,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "方法", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "密码", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel": "用户名和密码已加密。请为这些字段重新输入值。", - "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.rememberValuesLabel": "请记住这些值。每次编辑连接器时都必须重新输入。", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "将请求发送到 Web 服务。", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL", "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "用户名", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx index 61bac7de965b4..917e62b58c3f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx @@ -96,6 +96,27 @@ describe('EmailActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0); }); + test('should display a message for missing secrets after import', () => { + const actionConnector = { + actionTypeId: '.email', + config: { + hasAuth: true, + }, + isMissingSecrets: true, + secrets: {}, + } as EmailActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); + test('should display a message when editing an authenticated email connector explaining why username and password must be re-entered', () => { const actionConnector = { secrets: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx index df6822c85340a..55c7932b37d4e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { Fragment, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { EuiFieldText, EuiFlexItem, @@ -14,10 +14,8 @@ import { EuiFieldPassword, EuiSwitch, EuiFormRow, - EuiText, EuiTitle, EuiSpacer, - EuiCallOut, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -25,6 +23,7 @@ import { EuiLink } from '@elastic/eui'; import { ActionConnectorFieldsProps } from '../../../../types'; import { EmailActionConnector } from '../types'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; export const EmailActionConnectorFields: React.FunctionComponent< ActionConnectorFieldsProps @@ -40,7 +39,7 @@ export const EmailActionConnectorFields: React.FunctionComponent< }, []); return ( - + <> {hasAuth ? ( <> - {getEncryptedFieldNotifyLabel(!action.id)} + {getEncryptedFieldNotifyLabel( + !action.id, + 2, + action.isMissingSecrets ?? false, + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel', + { + defaultMessage: + 'Username and password are encrypted. Please reenter values for these fields.', + } + ) + )} ) : null} - + ); }; @@ -281,40 +291,5 @@ function nullableString(str: string | null | undefined) { return str; } -function getEncryptedFieldNotifyLabel(isCreate: boolean) { - if (isCreate) { - return ( - - - - - - - - ); - } - return ( - - - - - - ); -} - // eslint-disable-next-line import/no-default-export export { EmailActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx index 3e6de4a00996b..b89f71b0fc354 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx @@ -110,6 +110,26 @@ describe('JiraActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0); }); + test('should display a message when secrets is missing', () => { + const actionConnector = { + actionTypeId: '.jira', + isPreconfigured: false, + isMissingSecrets: true, + secrets: {}, + config: {}, + } as JiraActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); + test('should display a message on edit to re-enter credentials', () => { const actionConnector = { secrets: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx index 5e3b67754a0eb..f2753310d73ae 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx @@ -8,14 +8,12 @@ import React, { useCallback } from 'react'; import { - EuiCallOut, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldPassword, EuiSpacer, - EuiText, EuiTitle, } from '@elastic/eui'; @@ -23,6 +21,7 @@ import { ActionConnectorFieldsProps } from '../../../../types'; import * as i18n from './translations'; import { JiraActionConnector } from './types'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const JiraConnectorFields: React.FC> = ({ action, @@ -121,7 +120,14 @@ const JiraConnectorFields: React.FC - {getEncryptedFieldNotifyLabel(!action.id)} + + {getEncryptedFieldNotifyLabel( + !action.id, + 2, + action.isMissingSecrets ?? false, + i18n.JIRA_REENTER_VALUES_LABEL + )} + @@ -174,23 +180,5 @@ const JiraConnectorFields: React.FC - {i18n.JIRA_REMEMBER_VALUES_LABEL} - - ); - } - return ( - - ); -} - // eslint-disable-next-line import/no-default-export export { JiraConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts index 76dbe96f44f71..4577e55260d9d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts @@ -70,14 +70,6 @@ export const JIRA_AUTHENTICATION_LABEL = i18n.translate( } ); -export const JIRA_REMEMBER_VALUES_LABEL = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.jira.rememberValuesLabel', - { - defaultMessage: - 'Remember these values. You must reenter them each time you edit the connector.', - } -); - export const JIRA_REENTER_VALUES_LABEL = i18n.translate( 'xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel', { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx index 1205b9bf8288d..86347de528a01 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx @@ -91,4 +91,29 @@ describe('PagerDutyActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0); expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0); }); + + test('should display a message for missing secrets after import', () => { + const actionConnector = { + secrets: { + routingKey: 'test', + }, + id: 'test', + actionTypeId: '.pagerduty', + isMissingSecrets: true, + name: 'pagerduty', + config: { + apiUrl: 'http:\\test', + }, + } as PagerDutyActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx index 60935684527e5..8c9f809b97447 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx @@ -6,12 +6,13 @@ */ import React, { Fragment } from 'react'; -import { EuiCallOut, EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ActionConnectorFieldsProps } from '../../../../types'; import { PagerDutyActionConnector } from '.././types'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const PagerDutyActionConnectorFields: React.FunctionComponent< ActionConnectorFieldsProps @@ -68,7 +69,15 @@ const PagerDutyActionConnectorFields: React.FunctionComponent< )} > - {getEncryptedFieldNotifyLabel(!action.id)} + {getEncryptedFieldNotifyLabel( + !action.id, + 1, + action.isMissingSecrets ?? false, + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel', + { defaultMessage: 'This key is encrypted. Please reenter a value for this field.' } + ) + )} 0 && routingKey !== undefined} @@ -91,37 +100,5 @@ const PagerDutyActionConnectorFields: React.FunctionComponent< ); }; -function getEncryptedFieldNotifyLabel(isCreate: boolean) { - if (isCreate) { - return ( - - - - - - - - ); - } - return ( - - - - - - ); -} - // eslint-disable-next-line import/no-default-export export { PagerDutyActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx index 8d262a94e7342..b7b68b9485d8a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx @@ -111,6 +111,26 @@ describe('ResilientActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0); }); + test('should display a message for missing secrets after import', () => { + const actionConnector = { + actionTypeId: '.resilient', + isPreconfigured: false, + config: {}, + secrets: {}, + isMissingSecrets: true, + } as ResilientActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); + test('should display a message on edit to re-enter credentials', () => { const actionConnector = { secrets: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx index 2f88834556443..6996062899c39 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx @@ -8,20 +8,19 @@ import React, { useCallback } from 'react'; import { - EuiCallOut, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldPassword, EuiSpacer, - EuiText, EuiTitle, } from '@elastic/eui'; import { ActionConnectorFieldsProps } from '../../../../types'; import * as i18n from './translations'; import { ResilientActionConnector } from './types'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const ResilientConnectorFields: React.FC> = ({ action, @@ -115,7 +114,14 @@ const ResilientConnectorFields: React.FC - {getEncryptedFieldNotifyLabel(!action.id)} + + {getEncryptedFieldNotifyLabel( + !action.id, + 2, + action.isMissingSecrets ?? false, + i18n.REENTER_VALUES_LABEL + )} + @@ -176,23 +182,5 @@ const ResilientConnectorFields: React.FC - {i18n.REMEMBER_VALUES_LABEL} - - ); - } - return ( - - ); -} - // eslint-disable-next-line import/no-default-export export { ResilientConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx index 0132da9093e77..330844b93b6b5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx @@ -97,6 +97,26 @@ describe('ServiceNowActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0); }); + test('should display a message for missing secrets after import', () => { + const actionConnector = { + actionTypeId: '.servicenow', + isPreconfigured: false, + isMissingSecrets: true, + config: {}, + secrets: {}, + } as ServiceNowActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); + test('should display a message on edit to re-enter credentials', () => { const actionConnector = { secrets: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index 4e0db7f2054c6..e7b2c4bac5914 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -8,7 +8,6 @@ import React, { useCallback } from 'react'; import { - EuiCallOut, EuiFieldText, EuiFlexGroup, EuiFlexItem, @@ -16,7 +15,6 @@ import { EuiFieldPassword, EuiSpacer, EuiLink, - EuiText, EuiTitle, } from '@elastic/eui'; @@ -26,6 +24,7 @@ import { ActionConnectorFieldsProps } from '../../../../types'; import * as i18n from './translations'; import { ServiceNowActionConnector } from './types'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const ServiceNowConnectorFields: React.FC< ActionConnectorFieldsProps @@ -96,7 +95,14 @@ const ServiceNowConnectorFields: React.FC< - {getEncryptedFieldNotifyLabel(!action.id)} + + {getEncryptedFieldNotifyLabel( + !action.id, + 2, + action.isMissingSecrets ?? false, + i18n.REENTER_VALUES_LABEL + )} + @@ -157,23 +163,5 @@ const ServiceNowConnectorFields: React.FC< ); }; -function getEncryptedFieldNotifyLabel(isCreate: boolean) { - if (isCreate) { - return ( - - {i18n.REMEMBER_VALUES_LABEL} - - ); - } - return ( - - ); -} - // eslint-disable-next-line import/no-default-export export { ServiceNowConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx index 82bd43d823026..547346054011b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx @@ -62,6 +62,25 @@ describe('SlackActionFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0); }); + test('should display a message for missing secrets after import', () => { + const actionConnector = { + actionTypeId: '.email', + isMissingSecrets: true, + config: {}, + secrets: {}, + } as SlackActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); + test('should display a message on edit to re-enter credentials', () => { const actionConnector = { secrets: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx index fb0275b92bad8..677eb8d7d05f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx @@ -6,12 +6,13 @@ */ import React, { Fragment } from 'react'; -import { EuiCallOut, EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ActionConnectorFieldsProps } from '../../../../types'; import { SlackActionConnector } from '../types'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const SlackActionFields: React.FunctionComponent< ActionConnectorFieldsProps @@ -42,7 +43,15 @@ const SlackActionFields: React.FunctionComponent< )} > - {getEncryptedFieldNotifyLabel(!action.id)} + {getEncryptedFieldNotifyLabel( + !action.id, + 1, + action.isMissingSecrets ?? false, + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel', + { defaultMessage: 'This URL is encrypted. Please reenter a value for this field.' } + ) + )} 0 && webhookUrl !== undefined} @@ -65,37 +74,5 @@ const SlackActionFields: React.FunctionComponent< ); }; -function getEncryptedFieldNotifyLabel(isCreate: boolean) { - if (isCreate) { - return ( - - - - - - - - ); - } - return ( - - - - - - ); -} - // eslint-disable-next-line import/no-default-export export { SlackActionFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx index 1da22823845de..11c747125595d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx @@ -84,4 +84,27 @@ describe('TeamsActionFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0); expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0); }); + + test('should display a message for missing secrets after import', () => { + const actionConnector = { + secrets: { + webhookUrl: 'http:\\test', + }, + id: 'test', + actionTypeId: '.teams', + isMissingSecrets: true, + name: 'teams', + config: {}, + } as TeamsActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx index 3797d784131d7..454b938692225 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx @@ -5,13 +5,14 @@ * 2.0. */ -import React, { Fragment } from 'react'; -import { EuiCallOut, EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ActionConnectorFieldsProps } from '../../../../types'; import { TeamsActionConnector } from '../types'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const TeamsActionFields: React.FunctionComponent< ActionConnectorFieldsProps @@ -20,7 +21,7 @@ const TeamsActionFields: React.FunctionComponent< const { docLinks } = useKibana().services; return ( - + <> - - {getEncryptedFieldNotifyLabel(!action.id)} + <> + {getEncryptedFieldNotifyLabel( + !action.id, + 1, + action.isMissingSecrets ?? false, + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel', + { defaultMessage: 'This URL is encrypted. Please reenter a value for this field.' } + ) + )} 0 && webhookUrl !== undefined} @@ -59,43 +68,11 @@ const TeamsActionFields: React.FunctionComponent< } }} /> - + - + ); }; -function getEncryptedFieldNotifyLabel(isCreate: boolean) { - if (isCreate) { - return ( - - - - - - - - ); - } - return ( - - - - - - ); -} - // eslint-disable-next-line import/no-default-export export { TeamsActionFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx index 1cd4388c8d7a3..c041b4e3e1e42 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx @@ -97,4 +97,34 @@ describe('WebhookActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0); expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0); }); + + test('should display a message for missing secrets after import', () => { + const actionConnector = { + secrets: { + user: 'user', + password: 'pass', + }, + id: 'test', + actionTypeId: '.webhook', + isPreconfigured: false, + isMissingSecrets: true, + name: 'webhook', + config: { + method: 'PUT', + url: 'http:\\test', + headers: { 'content-type': 'text' }, + hasAuth: true, + }, + } as WebhookActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx index 94020a574be2d..9a93d29cfcb15 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx @@ -9,7 +9,6 @@ import React, { Fragment, useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiCallOut, EuiFieldPassword, EuiFieldText, EuiFormRow, @@ -21,7 +20,6 @@ import { EuiDescriptionList, EuiDescriptionListDescription, EuiDescriptionListTitle, - EuiText, EuiTitle, EuiSwitch, EuiButtonEmpty, @@ -29,6 +27,7 @@ import { import { i18n } from '@kbn/i18n'; import { ActionConnectorFieldsProps } from '../../../../types'; import { WebhookActionConnector } from '../types'; +import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label'; const HTTP_VERBS = ['post', 'put']; @@ -309,7 +308,18 @@ const WebhookActionConnectorFields: React.FunctionComponent< {hasAuth ? ( <> - {getEncryptedFieldNotifyLabel(!action.id)} + {getEncryptedFieldNotifyLabel( + !action.id, + 2, + action.isMissingSecrets ?? false, + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel', + { + defaultMessage: + 'Username and password are encrypted. Please reenter values for these fields.', + } + ) + )} - - - - - - - ); - } - return ( - - - - - - ); -} - // eslint-disable-next-line import/no-default-export export { WebhookActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx new file mode 100644 index 0000000000000..d627f75080f5b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx @@ -0,0 +1,70 @@ +/* + * 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 { getEncryptedFieldNotifyLabel } from './get_encrypted_field_notify_label'; + +describe('getEncryptedFieldNotifyLabel', () => { + test('renders proper notify label when isCreate equals true', () => { + const jsxObject = getEncryptedFieldNotifyLabel(true, 2, false, 'test'); + + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage' + ).length + ).toBeGreaterThan(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage' + ).length + ).toBe(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage' + ).length + ).toBe(0); + }); + + test('renders proper notify label when secrets is missing', () => { + const jsxObject = getEncryptedFieldNotifyLabel(false, 2, true, 'test'); + + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage' + ).length + ).toBe(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage' + ).length + ).toBeGreaterThan(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage' + ).length + ).toBe(0); + }); + + test('renders proper notify label when isCreate false (edit mode) and isMissingSecrets false', () => { + const jsxObject = getEncryptedFieldNotifyLabel(false, 2, false, 'test'); + + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage' + ).length + ).toBe(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage' + ).length + ).toBe(0); + expect( + jsxObject.props.children.filter( + (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage' + ).length + ).toBeGreaterThan(0); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx new file mode 100644 index 0000000000000..51168e591f619 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx @@ -0,0 +1,68 @@ +/* + * 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 { EuiSpacer, EuiCallOut, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const getEncryptedFieldNotifyLabel = ( + isCreate: boolean, + encryptedFieldsLength: number, + isMissingSecrets: boolean, + reEnterDefaultMessage: string +) => { + if (isMissingSecrets) { + return ( + <> + + + + + ); + } + if (isCreate) { + return ( + <> + + + + + + + ); + } + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index cf424ea1e7317..7011ec016c089 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -23,11 +23,13 @@ const transformConnector: RewriteRequestCase< connector_type_id: actionTypeId, is_preconfigured: isPreconfigured, referenced_by_count: referencedByCount, + is_missing_secrets: isMissingSecrets, ...res }) => ({ actionTypeId, isPreconfigured, referencedByCount, + isMissingSecrets, ...res, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts index e6e74f3f3c059..7a63f6a19f583 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts @@ -14,7 +14,7 @@ import type { } from '../../../types'; const rewriteBodyRequest: RewriteResponseCase< - Omit + Omit > = ({ actionTypeId, isPreconfigured, ...res }) => ({ ...res, connector_type_id: actionTypeId, @@ -23,10 +23,16 @@ const rewriteBodyRequest: RewriteResponseCase< const rewriteBodyRes: RewriteRequestCase< ActionConnectorProps, Record> -> = ({ connector_type_id: actionTypeId, is_preconfigured: isPreconfigured, ...res }) => ({ +> = ({ + connector_type_id: actionTypeId, + is_preconfigured: isPreconfigured, + is_missing_secrets: isMissingSecrets, + ...res +}) => ({ ...res, actionTypeId, isPreconfigured, + isMissingSecrets, }); export async function createActionConnector({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts index 1bc0cefc2723b..f2319ace29d68 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts @@ -15,10 +15,16 @@ import type { const rewriteBodyRes: RewriteRequestCase< ActionConnectorProps, Record> -> = ({ connector_type_id: actionTypeId, is_preconfigured: isPreconfigured, ...res }) => ({ +> = ({ + connector_type_id: actionTypeId, + is_preconfigured: isPreconfigured, + is_missing_secrets: isMissingSecrets, + ...res +}) => ({ ...res, actionTypeId, isPreconfigured, + isMissingSecrets, }); export async function updateActionConnector({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 26a101cedf955..174407e7edec5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -124,6 +124,72 @@ describe('action_form', () => { actionParamsFields: mockedActionParamsFields, }; + const allActions = [ + { + secrets: {}, + isMissingSecrets: false, + id: 'test', + actionTypeId: actionType.id, + name: 'Test connector', + config: {}, + isPreconfigured: false, + }, + { + secrets: {}, + isMissingSecrets: false, + id: 'test2', + actionTypeId: actionType.id, + name: 'Test connector 2', + config: {}, + isPreconfigured: true, + }, + { + secrets: {}, + isMissingSecrets: false, + id: 'test3', + actionTypeId: preconfiguredOnly.id, + name: 'Preconfigured Only', + config: {}, + isPreconfigured: true, + }, + { + secrets: {}, + isMissingSecrets: false, + id: 'test4', + actionTypeId: preconfiguredOnly.id, + name: 'Regular connector', + config: {}, + isPreconfigured: false, + }, + { + secrets: {}, + isMissingSecrets: false, + id: '.servicenow', + actionTypeId: '.servicenow', + name: 'Non consumer connector', + config: {}, + isPreconfigured: false, + }, + { + secrets: {}, + isMissingSecrets: false, + id: '.jira', + actionTypeId: disabledByActionType.id, + name: 'Connector with disabled action group', + config: {}, + isPreconfigured: false, + }, + { + secrets: null, + isMissingSecrets: true, + id: '.jira', + actionTypeId: actionType.id, + name: 'Connector with disabled action group', + config: {}, + isPreconfigured: false, + }, + ]; + const useKibanaMock = useKibana as jest.Mocked; describe('action_form in alert', () => { @@ -131,56 +197,7 @@ describe('action_form', () => { const actionTypeRegistry = actionTypeRegistryMock.create(); const { loadAllActions } = jest.requireMock('../../lib/action_connector_api'); - loadAllActions.mockResolvedValueOnce([ - { - secrets: {}, - id: 'test', - actionTypeId: actionType.id, - name: 'Test connector', - config: {}, - isPreconfigured: false, - }, - { - secrets: {}, - id: 'test2', - actionTypeId: actionType.id, - name: 'Test connector 2', - config: {}, - isPreconfigured: true, - }, - { - secrets: {}, - id: 'test3', - actionTypeId: preconfiguredOnly.id, - name: 'Preconfigured Only', - config: {}, - isPreconfigured: true, - }, - { - secrets: {}, - id: 'test4', - actionTypeId: preconfiguredOnly.id, - name: 'Regular connector', - config: {}, - isPreconfigured: false, - }, - { - secrets: {}, - id: '.servicenow', - actionTypeId: '.servicenow', - name: 'Non consumer connector', - config: {}, - isPreconfigured: false, - }, - { - secrets: {}, - id: '.jira', - actionTypeId: disabledByActionType.id, - name: 'Connector with disabled action group', - config: {}, - isPreconfigured: false, - }, - ]); + loadAllActions.mockResolvedValueOnce(allActions); const mocks = coreMock.createSetup(); const [ { @@ -467,6 +484,14 @@ describe('action_form', () => { ); actionOption.first().simulate('click'); const combobox = wrapper.find(`[data-test-subj="selectActionConnector-${actionType.id}"]`); + const numConnectors = allActions.filter((action) => action.actionTypeId === actionType.id) + .length; + const numConnectorsWithMissingSecrets = allActions.filter( + (action) => action.actionTypeId === actionType.id && action.isMissingSecrets + ).length; + expect((combobox.first().props() as any).options.length).toEqual( + numConnectors - numConnectorsWithMissingSecrets + ); expect((combobox.first().props() as any).options).toMatchInlineSnapshot(` Array [ Object { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 02e96b5fd05c5..55ebbbc6f3edd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -141,7 +141,7 @@ export const ActionForm = ({ try { setIsLoadingConnectors(true); const loadedConnectors = await loadConnectors({ http }); - setConnectors(loadedConnectors); + setConnectors(loadedConnectors.filter((connector) => !connector.isMissingSecrets)); } catch (e) { toasts.addDanger({ title: i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 21a90fb1ae17f..6c08b0b0b1ac5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -255,6 +255,9 @@ export const ConnectorEditFlyout = ({ if (closeAfterSave) { closeFlyout(); } + if (connector.isMissingSecrets) { + connector.isMissingSecrets = false; + } if (reloadConnectors) { reloadConnectors(); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index cc516fa5a32b9..9102e73690cac 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -118,6 +118,7 @@ describe('actions_connectors_list component with items', () => { id: '3', actionTypeId: 'test2', description: 'My preconfigured test 2', + isMissingSecrets: true, referencedByCount: 1, isPreconfigured: true, config: {}, @@ -215,6 +216,16 @@ describe('actions_connectors_list component with items', () => { ).toBeDisabled(); }); + it('renders fix button when connector secrets is missing', async () => { + await setup(); + expect( + wrapper.find('button[data-test-subj="deleteConnector"]').last().getDOMNode() + ).not.toBeDisabled(); + expect( + wrapper.find('button[data-test-subj="fixConnectorButton"]').last().getDOMNode() + ).not.toBeDisabled(); + }); + it('supports pagination', async () => { await setup( times(15, (index) => ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index d80041312d790..a322460cde444 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiInMemoryTable, EuiSpacer, @@ -19,6 +19,7 @@ import { EuiButtonIcon, EuiEmptyPrompt, Criteria, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { omit } from 'lodash'; @@ -63,9 +64,9 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { const [editConnectorProps, setEditConnectorProps] = useState<{ initialConnector?: ActionConnector; tab?: EditConectorTabs; + isFix?: boolean; }>({}); const [connectorsToDelete, setConnectorsToDelete] = useState([]); - useEffect(() => { loadActions(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -139,8 +140,12 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { } } - async function editItem(actionConnector: ActionConnector, tab: EditConectorTabs) { - setEditConnectorProps({ initialConnector: actionConnector, tab }); + async function editItem( + actionConnector: ActionConnector, + tab: EditConectorTabs, + isFix?: boolean + ) { + setEditConnectorProps({ initialConnector: actionConnector, tab, isFix: isFix ?? false }); } const actionsTableColumns = [ @@ -161,27 +166,41 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { ); const link = ( - editItem(item, EditConectorTabs.Configuration)} - key={item.id} - disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} - > - {value} - + <> + editItem(item, EditConectorTabs.Configuration)} + key={item.id} + disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} + > + {value} + + {item.isMissingSecrets ? ( + + ) : null} + ); return checkEnabledResult.isEnabled ? ( link ) : ( - + <> {link} - + ); }, }, @@ -207,11 +226,39 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { item={item} onDelete={() => setConnectorsToDelete([item.id])} /> - editItem(item, EditConectorTabs.Test)} - /> + {item.isMissingSecrets ? ( + <> + {actionTypesIndex && actionTypesIndex[item.actionTypeId]?.enabled ? ( + + + editItem(item, EditConectorTabs.Configuration, true)} + > + {i18n.translate( + 'xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.fixButtonLabel', + { + defaultMessage: 'Fix', + } + )} + + + + ) : null} + + ) : ( + editItem(item, EditConectorTabs.Test)} + /> + )} ); }, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 1fd031cda6d96..6db5634be2221 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -141,6 +141,7 @@ export interface ActionConnectorProps { referencedByCount?: number; config: Config; isPreconfigured: boolean; + isMissingSecrets?: boolean; } export type PreConfiguredActionConnector = Omit< diff --git a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.tsx b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.tsx index 934e0b07f9d38..6cdb01a992034 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.tsx @@ -14,7 +14,6 @@ import { CustomizeTimeRangeModal } from './customize_time_range_modal'; import { OpenModal, CommonlyUsedRange } from './types'; export const CUSTOM_TIME_RANGE = 'CUSTOM_TIME_RANGE'; -const SEARCH_EMBEDDABLE_TYPE = 'search'; export interface TimeRangeInput extends EmbeddableInput { timeRange: TimeRange; @@ -79,15 +78,7 @@ export class CustomTimeRangeAction implements Action { const isMarkdown = isVisualizeEmbeddable(embeddable) && (embeddable as VisualizeEmbeddable).getOutput().visTypeName === 'markdown'; - return Boolean( - embeddable && - hasTimeRange(embeddable) && - // Saved searches don't listen to the time range from the container that is passed down to them so it - // won't work without a fix. For now, just leave them out. - embeddable.type !== SEARCH_EMBEDDABLE_TYPE && - !isInputControl && - !isMarkdown - ); + return Boolean(embeddable && hasTimeRange(embeddable) && !isInputControl && !isMarkdown); } public async execute({ embeddable }: TimeRangeActionContext) { diff --git a/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts b/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts index 33d7177cf15f9..fd7cd2c90e952 100644 --- a/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts +++ b/x-pack/plugins/upgrade_assistant/tests_client_integration/overview.test.ts @@ -49,7 +49,7 @@ describe('Overview page', () => { domainId: 'xpack.spaces', level: 'critical', message: - 'Disabling the spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)', + 'Disabling the Spaces plugin (xpack.spaces.enabled) will not be supported in the next major version (8.0)', }, ]; diff --git a/x-pack/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts index f59f1939b5989..91e017292d00f 100644 --- a/x-pack/plugins/uptime/public/state/api/utils.ts +++ b/x-pack/plugins/uptime/public/state/api/utils.ts @@ -16,8 +16,9 @@ function isObject(value: unknown) { return value != null && (type === 'object' || type === 'function'); } -// TODO: Copied from https://github.com/elastic/kibana/blob/master/x-pack/plugins/security_solution/common/format_errors.ts -// We should figure out a better way to share this +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + */ export const formatErrors = (errors: t.Errors): string[] => { return errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/__snapshots__/kibana_telemetry_adapter.test.ts.snap b/x-pack/plugins/uptime/server/lib/adapters/telemetry/__snapshots__/kibana_telemetry_adapter.test.ts.snap index 8c55d5da54ac7..9148887ae4f83 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/__snapshots__/kibana_telemetry_adapter.test.ts.snap +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/__snapshots__/kibana_telemetry_adapter.test.ts.snap @@ -14,6 +14,13 @@ Object { "dateRangeStart": Array [ "now-15", ], + "fleet_monitor_frequency": Array [], + "fleet_monitor_name_stats": Object { + "avg_length": 0, + "max_length": 0, + "min_length": 0, + }, + "fleet_no_of_unique_monitors": 0, "monitor_frequency": Array [], "monitor_name_stats": Object { "avg_length": 0, @@ -49,6 +56,13 @@ Object { "dateRangeStart": Array [ "now-15", ], + "fleet_monitor_frequency": Array [], + "fleet_monitor_name_stats": Object { + "avg_length": 0, + "max_length": 0, + "min_length": 0, + }, + "fleet_no_of_unique_monitors": 0, "monitor_frequency": Array [], "monitor_name_stats": Object { "avg_length": 0, diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.test.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.test.ts index 06a14b30bdae0..70385698ca6e0 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.test.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.test.ts @@ -27,7 +27,9 @@ describe('KibanaTelemetryAdapter', () => { }, }; getSavedObjectsClient = () => { - return {}; + return { + get: () => {}, + }; }; collectorFetchContext = createCollectorFetchContextMock(); }); diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts index e540909b505c7..631bcf1b245db 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts @@ -52,20 +52,104 @@ export class KibanaTelemetryAdapter { dateRangeStart: { type: 'array', items: { type: 'date' } }, monitor_frequency: { type: 'array', items: { type: 'long' } }, monitor_name_stats: { - avg_length: { type: 'float' }, - max_length: { type: 'long' }, - min_length: { type: 'long' }, + avg_length: { + type: 'float', + _meta: { + description: 'This field represents the average length of monitor names', + }, + }, + max_length: { + type: 'long', + _meta: { + description: 'This field represents the max length of monitor names', + }, + }, + min_length: { + type: 'long', + _meta: { + description: 'This field represents the min length of monitor names', + }, + }, }, monitor_page: { type: 'long' }, - no_of_unique_monitors: { type: 'long' }, - no_of_unique_observer_locations: { type: 'long' }, + no_of_unique_monitors: { + type: 'long', + _meta: { + description: 'This field represents the number of unique configured monitors', + }, + }, + no_of_unique_observer_locations: { + type: 'long', + _meta: { + description: + 'This field represents the number of unique monitor observer locations', + }, + }, observer_location_name_stats: { - avg_length: { type: 'float' }, - max_length: { type: 'long' }, - min_length: { type: 'long' }, + avg_length: { + type: 'float', + _meta: { + description: + 'This field represents the average length of monitor observer location names', + }, + }, + max_length: { + type: 'long', + _meta: { + description: + 'This field represents the max length of monitor observer location names', + }, + }, + min_length: { + type: 'long', + _meta: { + description: + 'This field represents the min length of monitor observer location names', + }, + }, }, overview_page: { type: 'long' }, settings_page: { type: 'long' }, + fleet_monitor_name_stats: { + avg_length: { + type: 'float', + _meta: { + description: + 'This field represents the average length of fleet managed monitor names', + }, + }, + max_length: { + type: 'long', + _meta: { + description: + 'This field represents the max length of fleet managed monitor names', + }, + }, + min_length: { + type: 'long', + _meta: { + description: + 'This field represents the min length of fleet managed monitor names', + }, + }, + }, + fleet_monitor_frequency: { + type: 'array', + items: { + type: 'long', + _meta: { + description: + 'This field represents the average the monitor frequency of fleet managed monitors', + }, + }, + }, + fleet_no_of_unique_monitors: { + type: 'long', + _meta: { + description: + 'This field represents the number of unique configured fleet managed monitors', + }, + }, }, }, }, @@ -74,6 +158,7 @@ export class KibanaTelemetryAdapter { if (savedObjectsClient) { const uptimeEsClient = createUptimeESClient({ esClient, savedObjectsClient }); await this.countNoOfUniqueMonitorAndLocations(uptimeEsClient, savedObjectsClient); + await this.countNoOfUniqueFleetManagedMonitors(uptimeEsClient); } const report = this.getReport(); return { last_24_hours: { hits: { ...report } } }; @@ -218,6 +303,79 @@ export class KibanaTelemetryAdapter { return bucket; } + public static async countNoOfUniqueFleetManagedMonitors(callCluster: UptimeESClient) { + const params = { + index: 'synthetics-*', + body: { + query: { + bool: { + must: [ + { + range: { + '@timestamp': { + gte: 'now-1d/d', + lt: 'now', + }, + }, + }, + { + term: { + 'monitor.fleet_managed': true, + }, + }, + ], + }, + }, + size: 0, + aggs: { + unique_monitors: { + cardinality: { + field: 'monitor.id', + }, + }, + monitor_name: { + string_stats: { + field: 'monitor.name', + }, + }, + monitors: { + terms: { + field: 'monitor.id', + size: 1000, + }, + aggs: { + docs: { + top_hits: { + size: 1, + _source: ['monitor.timespan'], + }, + }, + }, + }, + }, + }, + }; + + const { body: result } = await callCluster.search(params); + + const numberOfUniqueMonitors: number = result?.aggregations?.unique_monitors?.value ?? 0; + const monitorNameStats = result?.aggregations?.monitor_name; + const uniqueMonitors: any = result?.aggregations?.monitors.buckets; + + const bucketId = this.getBucketToIncrement(); + const bucket = this.collector[bucketId]; + + bucket.fleet_no_of_unique_monitors = numberOfUniqueMonitors; + bucket.fleet_monitor_name_stats = { + min_length: monitorNameStats?.min_length ?? 0, + max_length: monitorNameStats?.max_length ?? 0, + avg_length: +(monitorNameStats?.avg_length?.toFixed(2) ?? 0), + }; + + bucket.fleet_monitor_frequency = this.getMonitorsFrequency(uniqueMonitors); + return bucket; + } + private static getMonitorsFrequency(uniqueMonitors = []) { const frequencies: number[] = []; uniqueMonitors @@ -285,6 +443,14 @@ export class KibanaTelemetryAdapter { dateRangeEnd: [], autoRefreshEnabled: false, autorefreshInterval: [], + + fleet_no_of_unique_monitors: 0, + fleet_monitor_frequency: [], + fleet_monitor_name_stats: { + min_length: 0, + max_length: 0, + avg_length: 0, + }, }; } return bucketId; diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts index 632544a5c2d3d..eceee3505dd7e 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts @@ -40,4 +40,8 @@ export interface UptimeTelemetry { dateRangeEnd: string[]; autorefreshInterval: number[]; autoRefreshEnabled: boolean; + + fleet_no_of_unique_monitors: number; + fleet_monitor_frequency: number[]; + fleet_monitor_name_stats: Stats; } diff --git a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts index ef1e0a07c6392..088cf494efbf7 100644 --- a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts +++ b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts @@ -34,6 +34,7 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ uptimeEsClient, savedObjectsClient ); + await KibanaTelemetryAdapter.countNoOfUniqueFleetManagedMonitors(uptimeEsClient); return KibanaTelemetryAdapter.countPageView(pageView as PageViewParams); }, }); diff --git a/x-pack/tasks/build.ts b/x-pack/tasks/build.ts index 1b52b073b3cef..96ec1c22687d1 100644 --- a/x-pack/tasks/build.ts +++ b/x-pack/tasks/build.ts @@ -76,6 +76,7 @@ async function copySourceAndBabelify() { '**/node_modules/**', '**/public/**/*.{js,ts,tsx,json}', '**/{__tests__,__mocks__,__snapshots__}/**', + 'plugins/*/target/**', 'plugins/canvas/shareable_runtime/test/**', 'plugins/telemetry_collection_xpack/schema/**', // Skip telemetry schemas ], diff --git a/x-pack/test/accessibility/apps/transform.ts b/x-pack/test/accessibility/apps/transform.ts index 4c0cae4174d36..38cd8d98e8c32 100644 --- a/x-pack/test/accessibility/apps/transform.ts +++ b/x-pack/test/accessibility/apps/transform.ts @@ -45,8 +45,8 @@ export default function ({ getService }: FtrProviderContext) { const pivotGroupByEntries = [ { - identifier: 'terms(category.keyword)', - label: 'category.keyword', + identifier: 'terms(category)', + label: 'category', }, { identifier: 'date_histogram(order_date)', diff --git a/x-pack/test/api_integration/apis/file_upload/index.ts b/x-pack/test/api_integration/apis/file_upload/index.ts index cf0159997fa11..30fd1ae819598 100644 --- a/x-pack/test/api_integration/apis/file_upload/index.ts +++ b/x-pack/test/api_integration/apis/file_upload/index.ts @@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('File upload', function () { loadTestFile(require.resolve('./has_import_permission')); + loadTestFile(require.resolve('./index_exists')); }); } diff --git a/x-pack/test/api_integration/apis/file_upload/index_exists.ts b/x-pack/test/api_integration/apis/file_upload/index_exists.ts new file mode 100644 index 0000000000000..4e014105ab7a8 --- /dev/null +++ b/x-pack/test/api_integration/apis/file_upload/index_exists.ts @@ -0,0 +1,48 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + + describe('POST /internal/file_upload/index_exists', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + }); + + after(async () => { + await esArchiver.unload('logstash_functional'); + }); + + it('should return true when index exists', async () => { + const resp = await supertest + .post(`/internal/file_upload/index_exists`) + .set('kbn-xsrf', 'kibana') + .send({ + index: 'logstash-2015.09.22', + }) + .expect(200); + + expect(resp.body.exists).to.be(true); + }); + + it('should return false when index does not exists', async () => { + const resp = await supertest + .post(`/internal/file_upload/index_exists`) + .set('kbn-xsrf', 'kibana') + .send({ + index: 'myNewIndex', + }) + .expect(200); + + expect(resp.body.exists).to.be(false); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/security/index_fields.ts b/x-pack/test/api_integration/apis/security/index_fields.ts index 6918ec3415dac..442740c7666df 100644 --- a/x-pack/test/api_integration/apis/security/index_fields.ts +++ b/x-pack/test/api_integration/apis/security/index_fields.ts @@ -71,6 +71,14 @@ export default function ({ getService }: FtrProviderContext) { expect(actualFields).to.eql(expectedFields); }); + + it('should return an empty result for indices that do not exist', async () => { + await supertest + .get('/internal/security/fields/this-index-name-definitely-does-not-exist-*') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200, []); + }); }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts index c8b1434b6ffd0..829ae07eccf20 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts @@ -18,6 +18,7 @@ interface CheckProps { mogrify?: (doc: any) => any; refresh?: boolean; tls?: boolean | TlsProps; + isFleetManaged?: boolean; } const getRandomMonitorId = () => { @@ -31,6 +32,7 @@ export const makeCheck = async ({ mogrify = (d) => d, refresh = true, tls = false, + isFleetManaged = false, }: CheckProps): Promise<{ monitorId: string; docs: any }> => { const cgFields = { monitor: { @@ -52,7 +54,15 @@ export const makeCheck = async ({ if (i === numIps - 1) { pingFields.summary = summary; } - const doc = await makePing(es, monitorId, pingFields, mogrify, false, tls as any); + const doc = await makePing( + es, + monitorId, + pingFields, + mogrify, + false, + tls as any, + isFleetManaged + ); docs.push(doc); // @ts-ignore summary[doc.monitor.status]++; @@ -73,7 +83,8 @@ export const makeChecks = async ( every: number = 10000, // number of millis between checks fields: { [key: string]: any } = {}, mogrify: (doc: any) => any = (d) => d, - refresh: boolean = true + refresh: boolean = true, + isFleetManaged: boolean = false ) => { const checks = []; const oldestTime = new Date().getTime() - numChecks * every; @@ -90,7 +101,15 @@ export const makeChecks = async ( }, }, }); - const { docs } = await makeCheck({ es, monitorId, numIps, fields, mogrify, refresh: false }); + const { docs } = await makeCheck({ + es, + monitorId, + numIps, + fields, + mogrify, + refresh: false, + isFleetManaged, + }); checks.push(docs); } @@ -110,7 +129,8 @@ export const makeChecksWithStatus = async ( fields: { [key: string]: any } = {}, status: 'up' | 'down', mogrify: (doc: any) => any = (d) => d, - refresh: boolean = true + refresh: boolean = true, + isFleetManaged: boolean = false ) => { const oppositeStatus = status === 'up' ? 'down' : 'up'; @@ -130,7 +150,8 @@ export const makeChecksWithStatus = async ( return mogrify(d); }, - refresh + refresh, + isFleetManaged ); }; diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts index f6ce4246f0a23..c45f68e4ae0ca 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts @@ -9,7 +9,8 @@ import uuid from 'uuid'; import { merge } from 'lodash'; import { makeTls, TlsProps } from './make_tls'; -const INDEX_NAME = 'heartbeat-8-generated-test'; +const DEFAULT_INDEX_NAME = 'heartbeat-8-generated-test'; +const DATA_STREAM_INDEX_NAME = 'synthetics-http-default'; export const makePing = async ( es: any, @@ -17,7 +18,8 @@ export const makePing = async ( fields: { [key: string]: any }, mogrify: (doc: any) => any, refresh: boolean = true, - tls: boolean | TlsProps = false + tls: boolean | TlsProps = false, + isFleetManaged: boolean | undefined = false ) => { const timestamp = new Date(); const baseDoc: any = { @@ -115,7 +117,7 @@ export const makePing = async ( const doc = mogrify(merge(baseDoc, fields)); await es.index({ - index: INDEX_NAME, + index: isFleetManaged ? DATA_STREAM_INDEX_NAME : DEFAULT_INDEX_NAME, refresh, body: doc, }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index a46aa653b6f2b..9f90d3739797b 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -38,14 +38,21 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { }); describe('with generated data', () => { - beforeEach('load heartbeat data', async () => await esArchiver.loadIfNeeded('uptime/blank')); - after('unload', async () => await esArchiver.unload('uptime/blank')); + beforeEach('load heartbeat data', async () => { + await esArchiver.loadIfNeeded('uptime/blank'); + await esArchiver.loadIfNeeded('uptime/blank_data_stream'); + }); + after('unload', async () => { + await esArchiver.unload('uptime/blank'); + await esArchiver.unload('uptime/blank_data_stream'); + }); loadTestFile(require.resolve('./certs')); loadTestFile(require.resolve('./dynamic_settings')); loadTestFile(require.resolve('./snapshot')); loadTestFile(require.resolve('./monitor_states_generated')); loadTestFile(require.resolve('./telemetry_collectors')); + loadTestFile(require.resolve('./telemetry_collectors_fleet')); }); describe('with real-world data', () => { diff --git a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts index e528ab6719fe7..f0f7a520d098a 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts @@ -14,7 +14,7 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const es = getService('legacyEs'); - describe('telemetry collectors', () => { + describe('telemetry collectors heartbeat', () => { before('generating data', async () => { await getService('esArchiver').load('uptime/blank'); @@ -82,7 +82,9 @@ export default function ({ getService }: FtrProviderContext) { await es.indices.refresh(); }); - after('unload heartbeat index', () => getService('esArchiver').unload('uptime/blank')); + after('unload heartbeat index', () => { + getService('esArchiver').unload('uptime/blank'); + }); beforeEach(async () => { await es.indices.refresh(); @@ -116,6 +118,13 @@ export default function ({ getService }: FtrProviderContext) { dateRangeEnd: ['now/d'], autoRefreshEnabled: true, autorefreshInterval: [100], + fleet_monitor_frequency: [], + fleet_monitor_name_stats: { + avg_length: 0, + max_length: 0, + min_length: 0, + }, + fleet_no_of_unique_monitors: 0, }); }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors_fleet.ts b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors_fleet.ts new file mode 100644 index 0000000000000..8c462f1db431b --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors_fleet.ts @@ -0,0 +1,191 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { makeChecksWithStatus } from './helper/make_checks'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + const client = getService('es'); + + describe('telemetry collectors fleet', () => { + before('generating data', async () => { + await getService('esArchiver').load('uptime/blank_data_stream'); + + const observer = { + geo: { + name: 'US-East', + location: '40.7128, -74.0060', + }, + }; + + const observer2 = { + geo: { + name: 'US', + location: '40.7128, -74.0060', + }, + }; + + await makeChecksWithStatus( + es, + 'upMonitorId', + 1, + 1, + 60 * 1000, + { + observer: {}, + monitor: { + name: 'Elastic', + fleet_managed: true, + }, + }, + 'up', + undefined, + undefined, + true + ); + + await makeChecksWithStatus( + es, + 'downMonitorId', + 1, + 1, + 120 * 1000, + { + observer, + monitor: { + name: 'Long Name with 22 Char', + fleet_managed: true, + }, + }, + 'down', + undefined, + undefined, + true + ); + + await makeChecksWithStatus( + es, + 'noGeoNameMonitor', + 1, + 1, + 60 * 1000, + { + observer: {}, + monitor: { + fleet_managed: true, + }, + }, + 'down', + undefined, + undefined, + true + ); + await makeChecksWithStatus( + es, + 'downMonitorId', + 1, + 1, + 1, + { + observer, + monitor: { + name: 'Elastic', + fleet_managed: true, + }, + }, + 'down', + undefined, + undefined, + true + ); + + await makeChecksWithStatus( + es, + 'mixMonitorId', + 1, + 1, + 1, + { observer: observer2, monitor: { fleet_managed: true } }, + 'down', + undefined, + undefined, + true + ); + await es.indices.refresh(); + }); + + after('unload heartbeat index', () => { + getService('esArchiver').unload('uptime/blank_data_stream'); + /** + * Data streams aren't included in the javascript elasticsearch client in kibana yet so we + * need to do raw requests here. Delete a data stream is slightly different than that of a regular index which + * is why we're using _data_stream here. + */ + client.transport.request({ + method: 'DELETE', + path: `_data_stream/synthetics-http-default`, + }); + }); + + beforeEach(async () => { + await es.indices.refresh(); + }); + + it('should receive expected results for fleet managed monitors after calling monitor logging', async () => { + // call monitor page + const { body: result } = await supertest + .post(API_URLS.LOG_PAGE_VIEW) + .set('kbn-xsrf', 'true') + .send({ + page: 'Monitor', + autorefreshInterval: 100, + dateStart: 'now/d', + dateEnd: 'now/d', + autoRefreshEnabled: true, + refreshTelemetryHistory: true, + }) + .expect(200); + + expect(result).to.eql({ + overview_page: 0, + monitor_page: 1, + no_of_unique_monitors: 4, + settings_page: 0, + monitor_frequency: [120, 0.001, 60, 60], + monitor_name_stats: { min_length: 7, max_length: 22, avg_length: 12 }, + no_of_unique_observer_locations: 3, + observer_location_name_stats: { min_length: 2, max_length: 7, avg_length: 4.8 }, + dateRangeStart: ['now/d'], + dateRangeEnd: ['now/d'], + autoRefreshEnabled: true, + autorefreshInterval: [100], + fleet_monitor_frequency: [120, 0.001, 60, 60], + fleet_monitor_name_stats: { min_length: 7, max_length: 22, avg_length: 12 }, + fleet_no_of_unique_monitors: 4, + }); + }); + + it('should receive 200 status after overview logging', async () => { + // call overview page + await supertest + .post(API_URLS.LOG_PAGE_VIEW) + .set('kbn-xsrf', 'true') + .send({ + page: 'Overview', + autorefreshInterval: 60, + dateStart: 'now/d', + dateEnd: 'now-30', + autoRefreshEnabled: true, + }) + .expect(200); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts index 8d0b87782ff7c..f7b8b34d88c01 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { get, merge, omit } from 'lodash'; +import { merge, omit } from 'lodash'; import { format } from 'url'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; @@ -30,7 +30,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertestAsApmWriteUser'); const es = getService('es'); - const MAX_POLLS = 5; + const MAX_POLLS = 10; const BULK_INDEX_DELAY = 1000; const INDEXING_DELAY = 5000; @@ -108,11 +108,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { } registry.when('Rule registry with write enabled', { config: 'rules', archives: [] }, () => { - it('bootstraps the apm alert indices', async () => { + it('does not bootstrap indices on plugin startup', async () => { const { body } = await es.indices.get({ index: ALERTS_INDEX_TARGET, expand_wildcards: 'open', - allow_no_indices: false, + allow_no_indices: true, }); const indices = Object.entries(body).map(([indexName, index]) => { @@ -122,23 +122,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }; }); - const indexNames = indices.map((index) => index.indexName); - - const apmIndex = indices[0]; - - // make sure it only creates one index - expect(indices.length).to.be(1); - - const apmIndexName = apmIndex.indexName; - - expect(apmIndexName.split('-').includes('observability')).to.be(true); - expect(apmIndexName.split('-').includes('apm')).to.be(true); - - expect(indexNames[0].startsWith('.kibana-alerts-observability-apm')).to.be(true); - - expect(get(apmIndex, 'index.mappings.properties.service.properties.environment.type')).to.be( - 'keyword' - ); + expect(indices.length).to.be(0); }); describe('when creating a rule', () => { @@ -282,6 +266,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { '@timestamp': 'desc', }, }, + allow_no_indices: true, }); expect(beforeDataResponse.body.hits.hits.length).to.be(0); @@ -335,12 +320,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { sort: { '@timestamp': 'desc', }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], }, }); expect(afterViolatingDataResponse.body.hits.hits.length).to.be(1); - const alertEvent = afterViolatingDataResponse.body.hits.hits[0]._source as Record< + const alertEvent = afterViolatingDataResponse.body.hits.hits[0].fields as Record< string, any >; @@ -354,23 +341,56 @@ export default function ApiTest({ getService }: FtrProviderContext) { const toCompare = omit(alertEvent, exclude); - expect(toCompare).to.eql({ - 'event.action': 'open', - 'event.kind': 'state', - 'kibana.rac.alert.duration.us': 0, - 'kibana.rac.alert.id': 'apm.transaction_error_rate_opbeans-go_request', - 'kibana.rac.alert.status': 'open', - 'kibana.rac.producer': 'apm', - 'kibana.observability.evaluation.threshold': 30, - 'kibana.observability.evaluation.value': 50, - 'processor.event': 'transaction', - 'rule.category': 'Transaction error rate threshold', - 'rule.id': 'apm.transaction_error_rate', - 'rule.name': 'Transaction error rate threshold | opbeans-go', - 'service.name': 'opbeans-go', - tags: ['apm', 'service.name:opbeans-go'], - 'transaction.type': 'request', - }); + expectSnapshot(toCompare).toMatchInline(` + Object { + "event.action": Array [ + "open", + ], + "event.kind": Array [ + "state", + ], + "kibana.rac.alert.duration.us": Array [ + 0, + ], + "kibana.rac.alert.evaluation.threshold": Array [ + 30, + ], + "kibana.rac.alert.evaluation.value": Array [ + 50, + ], + "kibana.rac.alert.id": Array [ + "apm.transaction_error_rate_opbeans-go_request", + ], + "kibana.rac.alert.producer": Array [ + "apm", + ], + "kibana.rac.alert.status": Array [ + "open", + ], + "processor.event": Array [ + "transaction", + ], + "rule.category": Array [ + "Transaction error rate threshold", + ], + "rule.id": Array [ + "apm.transaction_error_rate", + ], + "rule.name": Array [ + "Transaction error rate threshold | opbeans-go", + ], + "service.name": Array [ + "opbeans-go", + ], + "tags": Array [ + "apm", + "service.name:opbeans-go", + ], + "transaction.type": Array [ + "request", + ], + } + `); const now = new Date().getTime(); @@ -390,7 +410,56 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(topAlerts.length).to.be.greaterThan(0); - expect(omit(topAlerts[0], exclude)).to.eql(toCompare); + expectSnapshot(omit(topAlerts[0], exclude)).toMatchInline(` + Object { + "event.action": Array [ + "open", + ], + "event.kind": Array [ + "state", + ], + "kibana.rac.alert.duration.us": Array [ + 0, + ], + "kibana.rac.alert.evaluation.threshold": Array [ + 30, + ], + "kibana.rac.alert.evaluation.value": Array [ + 50, + ], + "kibana.rac.alert.id": Array [ + "apm.transaction_error_rate_opbeans-go_request", + ], + "kibana.rac.alert.producer": Array [ + "apm", + ], + "kibana.rac.alert.status": Array [ + "open", + ], + "processor.event": Array [ + "transaction", + ], + "rule.category": Array [ + "Transaction error rate threshold", + ], + "rule.id": Array [ + "apm.transaction_error_rate", + ], + "rule.name": Array [ + "Transaction error rate threshold | opbeans-go", + ], + "service.name": Array [ + "opbeans-go", + ], + "tags": Array [ + "apm", + "service.name:opbeans-go", + ], + "transaction.type": Array [ + "request", + ], + } + `); await es.bulk({ index: APM_TRANSACTION_INDEX_NAME, @@ -423,43 +492,76 @@ export default function ApiTest({ getService }: FtrProviderContext) { sort: { '@timestamp': 'desc', }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], }, }); expect(afterRecoveryResponse.body.hits.hits.length).to.be(1); - const recoveredAlertEvent = afterRecoveryResponse.body.hits.hits[0]._source as Record< + const recoveredAlertEvent = afterRecoveryResponse.body.hits.hits[0].fields as Record< string, any >; - expect(recoveredAlertEvent['kibana.rac.alert.status']).to.eql('closed'); - expect(recoveredAlertEvent['kibana.rac.alert.duration.us']).to.be.greaterThan(0); - expect(new Date(recoveredAlertEvent['kibana.rac.alert.end']).getTime()).to.be.greaterThan( - 0 - ); - + expect(recoveredAlertEvent['kibana.rac.alert.status']?.[0]).to.eql('closed'); + expect(recoveredAlertEvent['kibana.rac.alert.duration.us']?.[0]).to.be.greaterThan(0); expect( + new Date(recoveredAlertEvent['kibana.rac.alert.end']?.[0]).getTime() + ).to.be.greaterThan(0); + + expectSnapshot( omit( recoveredAlertEvent, exclude.concat(['kibana.rac.alert.duration.us', 'kibana.rac.alert.end']) ) - ).to.eql({ - 'event.action': 'close', - 'event.kind': 'state', - 'kibana.rac.alert.id': 'apm.transaction_error_rate_opbeans-go_request', - 'kibana.rac.alert.status': 'closed', - 'kibana.rac.producer': 'apm', - 'kibana.observability.evaluation.threshold': 30, - 'kibana.observability.evaluation.value': 50, - 'processor.event': 'transaction', - 'rule.category': 'Transaction error rate threshold', - 'rule.id': 'apm.transaction_error_rate', - 'rule.name': 'Transaction error rate threshold | opbeans-go', - 'service.name': 'opbeans-go', - tags: ['apm', 'service.name:opbeans-go'], - 'transaction.type': 'request', - }); + ).toMatchInline(` + Object { + "event.action": Array [ + "close", + ], + "event.kind": Array [ + "state", + ], + "kibana.rac.alert.evaluation.threshold": Array [ + 30, + ], + "kibana.rac.alert.evaluation.value": Array [ + 50, + ], + "kibana.rac.alert.id": Array [ + "apm.transaction_error_rate_opbeans-go_request", + ], + "kibana.rac.alert.producer": Array [ + "apm", + ], + "kibana.rac.alert.status": Array [ + "closed", + ], + "processor.event": Array [ + "transaction", + ], + "rule.category": Array [ + "Transaction error rate threshold", + ], + "rule.id": Array [ + "apm.transaction_error_rate", + ], + "rule.name": Array [ + "Transaction error rate threshold | opbeans-go", + ], + "service.name": Array [ + "opbeans-go", + ], + "tags": Array [ + "apm", + "service.name:opbeans-go", + ], + "transaction.type": Array [ + "request", + ], + } + `); const { body: topAlertsAfterRecovery, @@ -480,7 +582,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(topAlertsAfterRecovery.length).to.be(1); - expect(topAlertsAfterRecovery[0]['kibana.rac.alert.status']).to.be('closed'); + expect(topAlertsAfterRecovery[0]['kibana.rac.alert.status']?.[0]).to.be('closed'); }); }); }); diff --git a/x-pack/test/case_api_integration/basic/tests/cases/alerts/get_cases.ts b/x-pack/test/case_api_integration/basic/tests/cases/alerts/get_cases.ts new file mode 100644 index 0000000000000..140fb80949a24 --- /dev/null +++ b/x-pack/test/case_api_integration/basic/tests/cases/alerts/get_cases.ts @@ -0,0 +1,112 @@ +/* + * 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 { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { postCaseReq, postCommentAlertReq } from '../../../../common/lib/mock'; +import { deleteAllCaseItems } from '../../../../common/lib/utils'; +import { CaseResponse } from '../../../../../../plugins/cases/common'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const es = getService('es'); + + describe('get_cases using alertID', () => { + const createCase = async () => { + const { body } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(postCaseReq) + .expect(200); + return body; + }; + + const createComment = async (caseID: string) => { + await supertest + .post(`${CASES_URL}/${caseID}/comments`) + .set('kbn-xsrf', 'true') + .send(postCommentAlertReq) + .expect(200); + }; + + afterEach(async () => { + await deleteAllCaseItems(es); + }); + + it('should return all cases with the same alert ID attached to them', async () => { + const [case1, case2, case3] = await Promise.all([createCase(), createCase(), createCase()]); + + await Promise.all([ + createComment(case1.id), + createComment(case2.id), + createComment(case3.id), + ]); + + const { body: caseIDsWithAlert } = await supertest + .get(`${CASES_URL}/alerts/test-id`) + .expect(200); + + expect(caseIDsWithAlert.length).to.eql(3); + expect(caseIDsWithAlert).to.contain(case1.id); + expect(caseIDsWithAlert).to.contain(case2.id); + expect(caseIDsWithAlert).to.contain(case3.id); + }); + + it('should return all cases with the same alert ID when more than 100 cases', async () => { + // if there are more than 100 responses, the implementation sets the aggregation size to the + // specific value + const numCases = 102; + const createCasePromises: Array> = []; + for (let i = 0; i < numCases; i++) { + createCasePromises.push(createCase()); + } + + const cases = await Promise.all(createCasePromises); + + const commentPromises: Array> = []; + for (const caseInfo of cases) { + commentPromises.push(createComment(caseInfo.id)); + } + + await Promise.all(commentPromises); + + const { body: caseIDsWithAlert } = await supertest + .get(`${CASES_URL}/alerts/test-id`) + .expect(200); + + expect(caseIDsWithAlert.length).to.eql(numCases); + + for (const caseInfo of cases) { + expect(caseIDsWithAlert).to.contain(caseInfo.id); + } + }); + + it('should return no cases when the alert ID is not found', async () => { + const [case1, case2, case3] = await Promise.all([createCase(), createCase(), createCase()]); + + await Promise.all([ + createComment(case1.id), + createComment(case2.id), + createComment(case3.id), + ]); + + const { body: caseIDsWithAlert } = await supertest + .get(`${CASES_URL}/alerts/test-id100`) + .expect(200); + + expect(caseIDsWithAlert.length).to.eql(0); + }); + + it('should return a 302 when passing an empty alertID', async () => { + // kibana returns a 302 instead of a 400 when a url param is missing + await supertest.get(`${CASES_URL}/alerts/`).expect(302); + }); + }); +}; diff --git a/x-pack/test/case_api_integration/basic/tests/index.ts b/x-pack/test/case_api_integration/basic/tests/index.ts index 837e6503084a7..24e6b12138895 100644 --- a/x-pack/test/case_api_integration/basic/tests/index.ts +++ b/x-pack/test/case_api_integration/basic/tests/index.ts @@ -13,6 +13,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { // Fastest ciGroup for the moment. this.tags('ciGroup5'); + loadTestFile(require.resolve('./cases/alerts/get_cases')); loadTestFile(require.resolve('./cases/comments/delete_comment')); loadTestFile(require.resolve('./cases/comments/find_comments')); loadTestFile(require.resolve('./cases/comments/get_comment')); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts new file mode 100644 index 0000000000000..9e7fb0ea7c84b --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -0,0 +1,256 @@ +/* + * 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 { MachineLearningCreateSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createRule, + createRuleWithExceptionEntries, + createSignalsIndex, + deleteAllAlerts, + deleteSignalsIndex, + getOpenSignals, +} from '../../utils'; +import { + createListsIndex, + deleteAllExceptions, + deleteListsIndex, + importFile, +} from '../../../lists_api_integration/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + const siemModule = 'siem_auditbeat'; + const mlJobId = 'linux_anomalous_network_activity_ecs'; + const testRule: MachineLearningCreateSchema = { + name: 'Test ML rule', + description: 'Test ML rule description', + risk_score: 50, + severity: 'critical', + type: 'machine_learning', + anomaly_threshold: 30, + machine_learning_job_id: mlJobId, + from: '1900-01-01T00:00:00.000Z', + }; + + async function executeSetupModuleRequest(module: string, rspCode: number) { + const { body } = await supertest + .post(`/api/ml/modules/setup/${module}`) + .set('kbn-xsrf', 'true') + .send({ + prefix: '', + groups: ['auditbeat'], + indexPatternName: 'auditbeat-*', + startDatafeed: false, + useDedicatedIndex: true, + applyToAllSpaces: true, + }) + .expect(rspCode); + + return body; + } + + async function forceStartDatafeeds(jobId: string, rspCode: number) { + const { body } = await supertest + .post(`/api/ml/jobs/force_start_datafeeds`) + .set('kbn-xsrf', 'true') + .send({ + datafeedIds: [`datafeed-${jobId}`], + start: new Date().getUTCMilliseconds(), + }) + .expect(rspCode); + + return body; + } + + describe('Generating signals from ml anomalies', () => { + before(async () => { + // Order is critical here: auditbeat data must be loaded before attempting to start the ML job, + // as the job looks for certain indices on start + await esArchiver.load('auditbeat/hosts'); + await executeSetupModuleRequest(siemModule, 200); + await forceStartDatafeeds(mlJobId, 200); + await esArchiver.load('security_solution/anomalies'); + }); + after(async () => { + await esArchiver.unload('auditbeat/hosts'); + await esArchiver.unload('security_solution/anomalies'); + }); + + beforeEach(async () => { + await createSignalsIndex(supertest); + }); + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + }); + + it('should create 1 alert from ML rule when record meets anomaly_threshold', async () => { + const createdRule = await createRule(supertest, testRule); + const signalsOpen = await getOpenSignals(supertest, es, createdRule); + expect(signalsOpen.hits.hits.length).eql(1); + const signal = signalsOpen.hits.hits[0]; + expect(signal._source).eql({ + '@timestamp': signal._source['@timestamp'], + actual: [1], + bucket_span: 900, + by_field_name: 'process.name', + by_field_value: 'store', + detector_index: 0, + function: 'rare', + function_description: 'rare', + influencers: [ + { influencer_field_name: 'user.name', influencer_field_values: ['root'] }, + { influencer_field_name: 'process.name', influencer_field_values: ['store'] }, + { influencer_field_name: 'host.name', influencer_field_values: ['mothra'] }, + ], + initial_record_score: 33.36147565024334, + is_interim: false, + job_id: 'linux_anomalous_network_activity_ecs', + multi_bucket_impact: 0, + probability: 0.007820139656036713, + record_score: 33.36147565024334, + result_type: 'record', + timestamp: 1605567488000, + typical: [0.007820139656036711], + user: { name: ['root'] }, + process: { name: ['store'] }, + host: { name: ['mothra'] }, + event: { kind: 'signal' }, + signal: { + _meta: { version: 35 }, + parents: [ + { + id: + 'linux_anomalous_network_activity_ecs_record_1586274300000_900_0_-96106189301704594950079884115725560577_5', + type: 'event', + index: '.ml-anomalies-custom-linux_anomalous_network_activity_ecs', + depth: 0, + }, + ], + ancestors: [ + { + id: + 'linux_anomalous_network_activity_ecs_record_1586274300000_900_0_-96106189301704594950079884115725560577_5', + type: 'event', + index: '.ml-anomalies-custom-linux_anomalous_network_activity_ecs', + depth: 0, + }, + ], + status: 'open', + rule: { + id: createdRule.id, + rule_id: createdRule.rule_id, + created_at: createdRule.created_at, + updated_at: signal._source.signal.rule.updated_at, + actions: [], + interval: '5m', + name: 'Test ML rule', + tags: [], + enabled: true, + created_by: 'elastic', + updated_by: 'elastic', + throttle: null, + description: 'Test ML rule description', + risk_score: 50, + severity: 'critical', + output_index: '.siem-signals-default', + author: [], + false_positives: [], + from: '1900-01-01T00:00:00.000Z', + max_signals: 100, + risk_score_mapping: [], + severity_mapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptions_list: [], + immutable: false, + type: 'machine_learning', + anomaly_threshold: 30, + machine_learning_job_id: ['linux_anomalous_network_activity_ecs'], + }, + depth: 1, + parent: { + id: + 'linux_anomalous_network_activity_ecs_record_1586274300000_900_0_-96106189301704594950079884115725560577_5', + type: 'event', + index: '.ml-anomalies-custom-linux_anomalous_network_activity_ecs', + depth: 0, + }, + original_time: '2020-11-16T22:58:08.000Z', + }, + }); + }); + + it('should create 7 alerts from ML rule when records meet anomaly_threshold', async () => { + const rule: MachineLearningCreateSchema = { + ...testRule, + anomaly_threshold: 20, + }; + const createdRule = await createRule(supertest, rule); + const signalsOpen = await getOpenSignals(supertest, es, createdRule); + expect(signalsOpen.hits.hits.length).eql(7); + }); + describe('with non-value list exception', () => { + afterEach(async () => { + await deleteAllExceptions(es); + }); + it('generates no signals when an exception is added for an ML rule', async () => { + const createdRule = await createRuleWithExceptionEntries(supertest, testRule, [ + [ + { + field: 'host.name', + operator: 'included', + type: 'match', + value: 'mothra', + }, + ], + ]); + const signalsOpen = await getOpenSignals(supertest, es, createdRule); + expect(signalsOpen.hits.hits.length).equal(0); + }); + }); + + describe('with value list exception', () => { + beforeEach(async () => { + await createListsIndex(supertest); + }); + + afterEach(async () => { + await deleteListsIndex(supertest); + await deleteAllExceptions(es); + }); + + it('generates no signals when a value list exception is added for an ML rule', async () => { + const valueListId = 'value-list-id'; + await importFile(supertest, 'keyword', ['mothra'], valueListId); + const createdRule = await createRuleWithExceptionEntries(supertest, testRule, [ + [ + { + field: 'host.name', + operator: 'included', + type: 'list', + list: { + id: valueListId, + type: 'keyword', + }, + }, + ], + ]); + const signalsOpen = await getOpenSignals(supertest, es, createdRule); + expect(signalsOpen.hits.hits.length).equal(0); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index b6d88b657f25c..57b24f6de2a48 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -19,6 +19,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./create_rules')); loadTestFile(require.resolve('./create_rules_bulk')); loadTestFile(require.resolve('./create_index')); + loadTestFile(require.resolve('./create_ml')); loadTestFile(require.resolve('./create_threat_matching')); loadTestFile(require.resolve('./create_exceptions')); loadTestFile(require.resolve('./delete_rules')); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts index 257c6a4286982..12841b9072624 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts @@ -26,6 +26,7 @@ import { createNewAction, findImmutableRuleById, getPrePackagedRulesStatus, + getSimpleRuleOutput, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -61,6 +62,21 @@ export default ({ getService }: FtrProviderContext) => { expect(bodyToCompare).to.eql(expected); }); + it('should be able to add a new webhook action and then remove the action from the rule again', async () => { + const hookAction = await createNewAction(supertest); + const rule = getSimpleRule(); + await createRule(supertest, rule); + const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, rule); + await updateRule(supertest, ruleToUpdate); + const ruleAfterActionRemoved = await updateRule(supertest, rule); + const bodyToCompare = removeServerGeneratedProperties(ruleAfterActionRemoved); + const expected = { + ...getSimpleRuleOutput(), + version: 3, // version bump is required since this is an updated rule and this is part of the testing that we do bump the version number on update + }; + expect(bodyToCompare).to.eql(expected); + }); + it('should be able to create a new webhook action and attach it to a rule without a meta field and run it correctly', async () => { const hookAction = await createNewAction(supertest); const rule = getSimpleRule(); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 55011ec055190..0f57728f99d67 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -683,7 +683,7 @@ export const getWebHookAction = () => ({ export const getRuleWithWebHookAction = ( id: string, enabled = false, - rule?: QueryCreateSchema + rule?: CreateRulesSchema ): CreateRulesSchema | UpdateRulesSchema => { const finalRule = rule != null ? { ...rule, enabled } : getSimpleRule('rule-1', enabled); return { @@ -888,7 +888,7 @@ export const createNewAction = async (supertest: SuperTest { + await esArchiver.load('fleet/empty_fleet_server'); + }); + setupFleetAndAgents(providerContext); + + let defaultOutputId: string; + + before(async function () { + const { body: getOutputsRes } = await supertest.get(`/api/fleet/outputs`).expect(200); + + const defaultOutput = getOutputsRes.items.find((item: any) => item.is_default); + if (!defaultOutput) { + throw new Error('default output not set'); + } + + defaultOutputId = defaultOutput.id; + }); + + after(async () => { + await esArchiver.unload('fleet/empty_fleet_server'); + }); + + it('GET /outputs should list the default output', async () => { + const { body: getOutputsRes } = await supertest.get(`/api/fleet/outputs`).expect(200); + + expect(getOutputsRes.items.length).to.eql(1); + }); + + it('GET /outputs/{defaultOutputId} should return the default output', async () => { + const { body: getOutputRes } = await supertest + .get(`/api/fleet/outputs/${defaultOutputId}`) + .expect(200); + + expect(getOutputRes.item).to.have.keys('id', 'name', 'type', 'is_default', 'hosts'); + }); + + it('PUT /output/{defaultOutputId} should explicitly set port on ES hosts', async function () { + await supertest + .put(`/api/fleet/outputs/${defaultOutputId}`) + .set('kbn-xsrf', 'xxxx') + .send({ hosts: ['https://test.fr'] }) + .expect(200); + + const { body: getSettingsRes } = await supertest + .get(`/api/fleet/outputs/${defaultOutputId}`) + .expect(200); + expect(getSettingsRes.item.hosts).to.eql(['https://test.fr:443']); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/outputs/index.js b/x-pack/test/fleet_api_integration/apis/outputs/index.js new file mode 100644 index 0000000000000..b799413638d47 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/outputs/index.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export default function loadTests({ loadTestFile }) { + describe('Output Endpoints', () => { + loadTestFile(require.resolve('./crud')); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts index 5a0ff90669def..0956b6b0f515f 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts @@ -174,5 +174,42 @@ export default function (providerContext: FtrProviderContext) { }) .expect(500); }); + + it('should work with frozen input vars', async function () { + await supertest + .put(`/api/fleet/package_policies/${packagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'filetest-1', + description: '', + namespace: 'updated_namespace', + policy_id: agentPolicyId, + enabled: true, + output_id: '', + inputs: [ + { + enabled: true, + type: 'test-input', + streams: [], + vars: { + frozen_var: { + type: 'text', + value: 'abc', + frozen: true, + }, + unfrozen_var: { + type: 'text', + value: 'def', + }, + }, + }, + ], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }); + }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/settings/update.ts b/x-pack/test/fleet_api_integration/apis/settings/update.ts index 2c4992b21d71a..31fcb74627915 100644 --- a/x-pack/test/fleet_api_integration/apis/settings/update.ts +++ b/x-pack/test/fleet_api_integration/apis/settings/update.ts @@ -38,6 +38,18 @@ export default function (providerContext: FtrProviderContext) { after(async () => { await esArchiver.unload('fleet/empty_fleet_server'); }); + + it('should explicitly set port on fleet_server_hosts', async function () { + await supertest + .put(`/api/fleet/settings`) + .set('kbn-xsrf', 'xxxx') + .send({ fleet_server_hosts: ['https://test.fr'] }) + .expect(200); + + const { body: getSettingsRes } = await supertest.get(`/api/fleet/settings`).expect(200); + expect(getSettingsRes.item.fleet_server_hosts).to.eql(['https://test.fr:443']); + }); + it("should bump all agent policy's revision", async function () { const { body: testPolicy1PostRes } = await supertest .post(`/api/fleet/agent_policies`) diff --git a/x-pack/test/functional/apps/api_keys/feature_controls/index.ts b/x-pack/test/functional/apps/api_keys/feature_controls/index.ts index b8cfb9c97254b..134383fd65f02 100644 --- a/x-pack/test/functional/apps/api_keys/feature_controls/index.ts +++ b/x-pack/test/functional/apps/api_keys/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup8']); - loadTestFile(require.resolve('./api_keys_security')); }); } diff --git a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts index 3cd23330ff56f..85f064d5597a8 100644 --- a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts +++ b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup8']); - loadTestFile(require.resolve('./ccr_security')); }); } diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts index 192ecb0d4a1f9..82f53929ada31 100644 --- a/x-pack/test/functional/apps/discover/index.ts +++ b/x-pack/test/functional/apps/discover/index.ts @@ -16,6 +16,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./async_scripted_fields')); loadTestFile(require.resolve('./reporting')); loadTestFile(require.resolve('./error_handling')); + loadTestFile(require.resolve('./saved_searches')); loadTestFile(require.resolve('./visualize_field')); loadTestFile(require.resolve('./value_suggestions')); }); diff --git a/x-pack/test/functional/apps/discover/saved_searches.ts b/x-pack/test/functional/apps/discover/saved_searches.ts new file mode 100644 index 0000000000000..f811e705e1967 --- /dev/null +++ b/x-pack/test/functional/apps/discover/saved_searches.ts @@ -0,0 +1,52 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'header', 'discover', 'timePicker', 'dashboard']); + const dashboardAddPanel = getService('dashboardAddPanel'); + const dataGrid = getService('dataGrid'); + const panelActions = getService('dashboardPanelActions'); + const panelActionsTimeRange = getService('dashboardPanelTimeRange'); + + describe('Discover Saved Searches', () => { + before('initialize tests', async () => { + await esArchiver.load('reporting/ecommerce'); + await esArchiver.load('reporting/ecommerce_kibana'); + await kibanaServer.uiSettings.update({ 'doc_table:legacy': false }); + }); + after('clean up archives', async () => { + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + await kibanaServer.uiSettings.unset('doc_table:legacy'); + }); + + describe('Customize time range', () => { + it('should be possible to customize time range for saved searches on dashboards', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; + const toTime = 'Aug 23, 2019 @ 16:18:51.821'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await dashboardAddPanel.clickOpenAddPanel(); + await dashboardAddPanel.addSavedSearch('Ecommerce Data'); + expect(await dataGrid.getDocCount()).to.be(500); + await panelActions.openContextMenuMorePanel(); + await panelActionsTimeRange.clickTimeRangeActionInContextMenu(); + await panelActionsTimeRange.clickToggleQuickMenuButton(); + await panelActionsTimeRange.clickCommonlyUsedTimeRange('Last_90 days'); + await panelActionsTimeRange.clickModalPrimaryButton(); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect(await dataGrid.hasNoResults()).to.be(true); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts index 66009dc4e0a71..d8d6e94e677e5 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./ilm_security')); }); } diff --git a/x-pack/test/functional/apps/index_management/feature_controls/index.ts b/x-pack/test/functional/apps/index_management/feature_controls/index.ts index e65a8e37dcda3..ff2971094ac66 100644 --- a/x-pack/test/functional/apps/index_management/feature_controls/index.ts +++ b/x-pack/test/functional/apps/index_management/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./index_management_security')); }); } diff --git a/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts index 58b0e63834008..15571516a8e88 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./ingest_pipelines_security')); }); } diff --git a/x-pack/test/functional/apps/license_management/feature_controls/index.ts b/x-pack/test/functional/apps/license_management/feature_controls/index.ts index b8500c43f0d77..1b2e8f82718e5 100644 --- a/x-pack/test/functional/apps/license_management/feature_controls/index.ts +++ b/x-pack/test/functional/apps/license_management/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./license_management_security')); }); } diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts index e7b5df70c99a0..4de95a5d82054 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts @@ -16,5 +16,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./classification_creation')); loadTestFile(require.resolve('./cloning')); loadTestFile(require.resolve('./feature_importance')); + loadTestFile(require.resolve('./trained_models')); }); } diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts new file mode 100644 index 0000000000000..43130651cb121 --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts @@ -0,0 +1,31 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const ml = getService('ml'); + + describe('trained models', function () { + before(async () => { + await ml.trainedModels.createdTestTrainedModels('classification', 15); + await ml.trainedModels.createdTestTrainedModels('regression', 15); + await ml.securityUI.loginAsMlPowerUser(); + await ml.navigation.navigateToTrainedModels(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('renders trained models list', async () => { + await ml.trainedModels.assertRowsNumberPerPage(10); + // +1 because of the built-in model + await ml.trainedModels.assertStats(31); + }); + }); +} diff --git a/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts b/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts index 62358090804a1..869c4d47b34fc 100644 --- a/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts +++ b/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./remote_clusters_security')); }); } diff --git a/x-pack/test/functional/apps/transform/cloning.ts b/x-pack/test/functional/apps/transform/cloning.ts index b736404eb529b..c36a89f6ecb50 100644 --- a/x-pack/test/functional/apps/transform/cloning.ts +++ b/x-pack/test/functional/apps/transform/cloning.ts @@ -32,7 +32,7 @@ function getTransformConfig(): TransformPivotConfig { aggregations: { 'products.base_price.avg': { avg: { field: 'products.base_price' } } }, }, description: - 'ecommerce batch transform with avg(products.base_price) grouped by terms(category.keyword)', + 'ecommerce batch transform with avg(products.base_price) grouped by terms(category)', frequency: '3s', settings: { max_page_search_size: 250, diff --git a/x-pack/test/functional/apps/transform/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation_index_pattern.ts index 2c26a340a2a26..b01d5d8f66863 100644 --- a/x-pack/test/functional/apps/transform/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation_index_pattern.ts @@ -17,6 +17,7 @@ import { } from './index'; export default function ({ getService }: FtrProviderContext) { + const canvasElement = getService('canvasElement'); const esArchiver = getService('esArchiver'); const transform = getService('transform'); @@ -40,8 +41,8 @@ export default function ({ getService }: FtrProviderContext) { source: 'ft_ecommerce', groupByEntries: [ { - identifier: 'terms(category.keyword)', - label: 'category.keyword', + identifier: 'terms(category)', + label: 'category', } as GroupByEntry, { identifier: 'date_histogram(order_date)', @@ -85,16 +86,16 @@ export default function ({ getService }: FtrProviderContext) { ], transformId: `ec_1_${Date.now()}`, transformDescription: - 'ecommerce batch transform with groups terms(category.keyword) + date_histogram(order_date) 1m and aggregation avg(products.base_price)', + 'ecommerce batch transform with groups terms(category) + date_histogram(order_date) 1m and aggregation avg(products.base_price)', get destinationIndex(): string { return `user-${this.transformId}`; }, discoverAdjustSuperDatePicker: true, expected: { - pivotAdvancedEditorValueArr: ['{', ' "group_by": {', ' "category.keyword": {'], + pivotAdvancedEditorValueArr: ['{', ' "group_by": {', ' "category": {'], pivotAdvancedEditorValue: { group_by: { - 'category.keyword': { + category: { terms: { field: 'category.keyword', }, @@ -156,7 +157,15 @@ export default function ({ getService }: FtrProviderContext) { rows: 5, }, histogramCharts: [ - { chartAvailable: false, id: 'category', legend: 'Chart not supported.' }, + { + chartAvailable: true, + id: 'category', + legend: '6 categories', + colorStats: [ + { color: '#000000', percentage: 45 }, + { color: '#54B399', percentage: 55 }, + ], + }, { chartAvailable: true, id: 'currency', @@ -166,8 +175,24 @@ export default function ({ getService }: FtrProviderContext) { { color: '#54B399', percentage: 90 }, ], }, - { chartAvailable: false, id: 'customer_first_name', legend: 'Chart not supported.' }, - { chartAvailable: false, id: 'customer_full_name', legend: 'Chart not supported.' }, + { + chartAvailable: true, + id: 'customer_first_name', + legend: 'top 20 of 46 categories', + colorStats: [ + { color: '#000000', percentage: 60 }, + { color: '#54B399', percentage: 35 }, + ], + }, + { + chartAvailable: true, + id: 'customer_full_name', + legend: 'top 20 of 3321 categories', + colorStats: [ + { color: '#000000', percentage: 25 }, + { color: '#54B399', percentage: 67 }, + ], + }, { chartAvailable: true, id: 'customer_gender', @@ -186,7 +211,15 @@ export default function ({ getService }: FtrProviderContext) { { color: '#000000', percentage: 60 }, ], }, - { chartAvailable: false, id: 'customer_last_name', legend: 'Chart not supported.' }, + { + chartAvailable: true, + id: 'customer_last_name', + legend: 'top 20 of 183 categories', + colorStats: [ + { color: '#000000', percentage: 25 }, + { color: '#54B399', percentage: 70 }, + ], + }, { chartAvailable: true, id: 'customer_phone', @@ -403,6 +436,9 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertAdvancedQueryEditorSwitchExists(); await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false); + // Disable anti-aliasing to stabilize canvas image rendering assertions + await canvasElement.disableAntiAliasing(); + await transform.testExecution.logTestStep('enables the index preview histogram charts'); await transform.wizard.enableIndexPreviewHistogramCharts(true); @@ -415,6 +451,8 @@ export default function ({ getService }: FtrProviderContext) { ); } + await canvasElement.resetAntiAliasing(); + if (isPivotTransformTestData(testData)) { await transform.testExecution.logTestStep('adds the group by entries'); for (const [index, entry] of testData.groupByEntries.entries()) { diff --git a/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts b/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts index 030748026af91..6add0a3ca5408 100644 --- a/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts +++ b/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts @@ -53,7 +53,15 @@ export default function ({ getService }: FtrProviderContext) { chartAvailable: true, id: '@timestamp', }, - { chartAvailable: false, id: '@version', legend: 'Chart not supported.' }, + { + chartAvailable: true, + id: '@version', + legend: '1 category', + colorStats: [ + { color: '#000000', percentage: 10 }, + { color: '#54B399', percentage: 90 }, + ], + }, { chartAvailable: true, id: 'airline', @@ -67,7 +75,8 @@ export default function ({ getService }: FtrProviderContext) { chartAvailable: true, id: 'responsetime', colorStats: [ - { color: '#54B399', percentage: 5 }, + // below 10% threshold + // { color: '#54B399', percentage: 5 }, { color: '#000000', percentage: 95 }, ], }, @@ -84,11 +93,20 @@ export default function ({ getService }: FtrProviderContext) { chartAvailable: true, id: 'rt_responsetime_x_2', colorStats: [ - { color: '#54B399', percentage: 5 }, + // below 10% threshold + // { color: '#54B399', percentage: 5 }, { color: '#000000', percentage: 95 }, ], }, - { chartAvailable: false, id: 'type', legend: 'Chart not supported.' }, + { + chartAvailable: true, + id: 'type', + legend: '1 category', + colorStats: [ + { color: '#000000', percentage: 10 }, + { color: '#54B399', percentage: 90 }, + ], + }, ]; const testDataList: Array = [ diff --git a/x-pack/test/functional/apps/transform/editing.ts b/x-pack/test/functional/apps/transform/editing.ts index 1f0bb058bdc38..0fc698b0acd86 100644 --- a/x-pack/test/functional/apps/transform/editing.ts +++ b/x-pack/test/functional/apps/transform/editing.ts @@ -20,7 +20,7 @@ function getTransformConfig(): TransformPivotConfig { aggregations: { 'products.base_price.avg': { avg: { field: 'products.base_price' } } }, }, description: - 'ecommerce batch transform with avg(products.base_price) grouped by terms(category.keyword)', + 'ecommerce batch transform with avg(products.base_price) grouped by terms(category)', dest: { index: `user-ec_2_${date}` }, }; } diff --git a/x-pack/test/functional/apps/transform/feature_controls/index.ts b/x-pack/test/functional/apps/transform/feature_controls/index.ts index 92961a323b3dc..a00054c185438 100644 --- a/x-pack/test/functional/apps/transform/feature_controls/index.ts +++ b/x-pack/test/functional/apps/transform/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./transform_security')); }); } diff --git a/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts index c6d1dbb69f49f..74adf4f0652ae 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./upgrade_assistant_security')); }); } diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json index ebb5b19387faf..c66a45084441f 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json @@ -161,9 +161,9 @@ }, "map" : { "properties" : { - "bounds" : { - "type" : "geo_shape", - "tree" : "quadtree" + "bounds": { + "dynamic": false, + "properties": {} }, "description" : { "type" : "text" diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json index 12cdc07e1d478..dafef0fa9c2c7 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json @@ -161,9 +161,9 @@ }, "gis-map" : { "properties" : { - "bounds" : { - "type" : "geo_shape", - "tree" : "quadtree" + "bounds": { + "dynamic": false, + "properties": {} }, "description" : { "type" : "text" diff --git a/x-pack/test/functional/es_archives/data/search_sessions/data.json.gz b/x-pack/test/functional/es_archives/data/search_sessions/data.json.gz index fff020036a8e3de67513dbdb85f1bf96c850605d..9528149d3c0f91f0881f6763a5a97d0e7c61ca74 100644 GIT binary patch delta 2676 zcmV-)3XApH6!;W>ABzYGi3f~e00U%UbYU)Pb8l_{?VRmX+SnJz-|t`H_3`uA%9;NuRij_?pgCwgVs9{N^We;-JRC=ap!Qqc}AUW&<5(B9Yx2T zUS!`QVuyUtp-tL6Jo@18#<+3SAhXI9rkKtWapTb>2}PKXW>AL9+j2bQVSDRu7$lS! zqRYTyreY$Ef-rH=6l;wbr-51%V~`(rpckLtEc}8;yGX<}J?5X@As0$z9`JmFh>IRt;Y`X_0bXXzv&j z23aY%MAjNZp&z7O34(a z!UtyIwnGvzLd-F4W8^&dbWsDnlXkMd-+ag0-TFzz@K@{O3x0+IOJIHG18b|K_N|$M}JJc#ncKA&NmVsg2~tLZ^tz~icoZW z`mP@Nq<-D*?$K_oN~s~Z)xuY?Q%J{(Tcj!Cu5^r8VlSoCNKp?r1VN=u5Gq*fC6?L< zgHd44sKDK#Qo2{&s2Mxbe;;)kw{3D{qMB|vbaHcYR>eSS%fg?VV(!oNXH za@Ohe>Xs%V!o3gYqnoT-Z_)EnuXkE0{C~Z>6~ni}2_q2fOky0kwV)Q`G{6E}&XW*? zo1tHTU?{SR#saozgiDNJ5d3-(HT82}yS(X*JdRFVXWi7FbbFSapxbVzb$`}9 zY~GyE{ofj=m!C}1pO|hH`*y2tvx0*~NCm+e~#gLF^~OwxuE?t6tVoBw$)APIrpcZ_x*4YGrAP&-CI zM-n)AQ6R9@;KwAmd>%ULo zGSJG--#9T))_?N<+~T6yJLYX?d){@wni!W4X)mE~am! zQnTo%S@JHV0qk&`&8I!sYai|(@3jst_C9>Lh+4m2ysI^9t-a21Ykzy5O7>Z*1qkS( z9JpUV_X_)_?JwUn@nv^^VTyl^r?iW$zkm7Vua%!+u(E4l$m~FyhIbfP;*J4#-a#V9 zMk;Kn!~exIj6v?k*O32`Cm71<3|9r*@;#{>k=GxNDAAb**vP|mH{zs#MFvtm{Y zsKz++A-gD-OnJnI+(DwVRG-J6mh$Mh3@qR3z`EZe|1pa;`dHM*_fV^9dTS@G{83p{ z#+;a81wz4Z6eY_r_rCr1xLa%e_mo=*hyOp6`}mPk`tc)vX39B}UYn&)>E-KS+#m&$ z8DBzL`37^Z2!C^QgSi*RTvgXwJ89*Q%3_)0JVZLVUZj&xO*-<-q`MyEQ)_lxrw!#^6Xp1Za_d34A9~)}N$VlX{Wyz<+<)dF#_9E9oPI*av9eO^nHhJP znMr>M;P5N?WG-acINRXu72!>6@b=<(t7dm=C$0QZS$yv`55Ntt*JpD0DL<3rWu5pl z!z~HvvQOmjlYMVyz1g<;t&>-XH@U&vi{q`D;jNvt@<(M+^|wwiUoXP(CnTIGOC_F} zaAq=E27kB*$h867#=w3Gz^M)3UL0`M{BG@}l|L$r%7A0*1vvJEfRkmZ*8LCu!xnj@TmS%vV`Sd| delta 2646 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 diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json index 6df7a19959b29..5e01af673ef2f 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json @@ -161,9 +161,9 @@ }, "gis-map" : { "properties" : { - "bounds" : { - "type" : "geo_shape", - "tree" : "quadtree" + "bounds": { + "dynamic": false, + "properties": {} }, "description" : { "type" : "text" diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json index 12cdc07e1d478..dafef0fa9c2c7 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json @@ -161,9 +161,9 @@ }, "gis-map" : { "properties" : { - "bounds" : { - "type" : "geo_shape", - "tree" : "quadtree" + "bounds": { + "dynamic": false, + "properties": {} }, "description" : { "type" : "text" diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index 72a7368e4d0a8..e9a3a965c0523 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -51,7 +51,6 @@ "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", "alert": "7b44fba6773e37c806ce290ea9b7024e", "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "map": "23d7aa4a720d4938ccde3983f87bd58d", "uptime-dynamic-settings": "fcdb453a30092f022f2642db29523d80", "cases": "32aa96a6d3855ddda53010ae2048ac22", "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", @@ -2128,7 +2127,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json b/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json index 5256e29956f4f..a352155899e79 100644 --- a/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json +++ b/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json @@ -22,7 +22,6 @@ "index-pattern": "66eccb05066c5a89924f48a9e9736499", "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "map": "23d7aa4a720d4938ccde3983f87bd58d", "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", @@ -437,7 +436,8 @@ "map": { "properties": { "bounds": { - "type": "geo_shape" + "dynamic": false, + "properties": {} }, "description": { "type": "text" diff --git a/x-pack/test/functional/es_archives/infra/metrics_anomalies/data.json.gz b/x-pack/test/functional/es_archives/infra/metrics_anomalies/data.json.gz index 68ad6965d0fb143f50e9af9072a651a85a066c1f..7026c69407c7a6a2aea4786be87f1c1058f40eda 100644 GIT binary patch literal 27992 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 literal 4289452 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� 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

O2H=YS!$_%a_X+IhB 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%0AZB*rOAAK_9&d)A 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 z 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

+ZGcv8{)gBso%!vivk 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∨Uet>&)VgNnvPiH@wlREj)iLP7HRaV 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∈`WK%~tbkgwuWV0vGulBD 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

NA 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

NVuZ!l$fsMnHXvsTY2tyGi06 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 cmwg+n1cD#})kfFH*+1Nl1(cKWQ>aGwu@z}G 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

E-LaO#WL^~N$)#nW{&GlNZ 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 rkq4Fpl-`}>NkNB0>)J79As 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ℑ0>SzuXS(gW5)XBbcY#LoG0g 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%0AYS#@ExOy_pygepd;EGFTD__cix`$x5 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

d1SKJBjOT9oX~T; zbRJ4Ng8G6#Y>b0}n((SF^Su)Hv0qy7X2mc5z15%3YQjrvYP0OA`X{+CX504=l`4Qv zUzK8-$I${yTH6Eo7yvqo0*P@`aQX04MpsP=&UPK=EAEmCzaj#WD*pU9z2`7_#-m5^ z_6BgWspwW5SBp4vdUhArSq-)rDOzw`VD-8cM}J^*yR*9=Jsv40`vTnA)$;(lZb zcnWECxw4`%(0trnP3;I^^y8@CJ96rX$mm>(h=V7-4TSN>lXIw}63gr;)QjS|(9H=c z2J-q2;LKxP*Akmze|#lIL7scIz@rLF^aK(-x>FJ!E*qK(6=p>^GztxBF55A>X?UT_ zZ&kcA=)D$f+wpc$A5`mtkOYWJ=pYfiHe_z_>9a0~MhfZQtf&t)*rvUf!nGjWKtmPu zH|->76_@`(u-e~m$Kn9xBQFabe55keM)B_Aqk(5&hbAC zW4}XEnyh>*EfSC*UDf79Qct;_E-FzFFZ%af$}w>o;*spCp!elvQ2 z$Fp(~v%=1PdoVBs(V1f2eZZ+vEQod~hku_VkJt%vKpM?|&_3=ea3L^*JfbY&4tWp~tnEeWuMS5K=4Lqs;W=&=MtO-GXa!A3f=`bii2F;qC z(5(4DU{RRnY3l<-$&}#70Y;pn`Be#>7Z-^djw0%!-j-V#Qor(sbp;y=oi4aZG z&vL#+zBAOeWv9!$*-#RH6=j4^;+JA3+xXllbuaaS-!^l!ghe{B=GP-cl0a5jKCa!L zUkLPR8IPe%KKrTyJm-KUo3^srOJq%KyKF6uYVffJcFZl$}R_ z)NP#eCq|^07v#OYPsRZe4weY(DP9-S=TdUleIn9_39nL%_21S0DK1H_5=?X+ZrLvK z=y%TB6b(a-RE_MgVGozfpl(Al7Iv;;^=aJ+sY}T!=NBmN{GW)Fj(4Cyc`S?jBSdp;k}f?Vx$HLBhj1+DGJVQB zWZrkjexH7(&lwR&OpI?@&bO=@mIWDouDk*EAsGB=OI~A_S>|Hk#@>( z;Y-vo?^E1|p~|!q3;x$Z$O+MxEM!-A)4H3Vq>?j=%nMPG$Xd zPMv8hbpIv!Dm)N3DGPNMwLGwz0_a^`!cRg0Rnie+Bw|0c77#?#MJSWBt25$>1p)Bn z#KSdpdSE{AR z`c*Jf6J@W;s7@EopO4|#8g!&Qu#F$RV&|d5ASqlBQmy%TP|^3v6$cVpMW=uU9iPqx z@x42Md}avdPzrqnwYG%f>C>>k5Z?cpLj|FAsY*o#?J?{m$t_+c|9V-l6i)b!mRPuJ ziO3j+^RvEoS(P~9@sqA3`rBpiSg=+Xeh(W*49t@1vBZCQTuo9o0uYZc2#Kr&Nxh+o zz6la=1%AKZj8@xeN-mo?bF6HEywLpWfr)ELCc#g<{xN9*am_22Z#81b+5%7>dMNOYHBE7v7{|OaBdeydV6o#WWV)h zxkw5UWSO^&$-7r&L?aPAM}?A$sjClHRBL+pH><1n6)dW8RaoNFqY#L9)`1`H6Zk#Q zH!E7}53U`t7;`mWk-Qd;k}bfO##hNpq<(FG!2o%vIqAD)_9;Kcw~j|6>T8W7?-^x9>Fp`UiMS>m^dfG_)5Py%t^y&)x%PXsBWD6X0^-@!PAxl2yxQxpVTT z5;70H`{HSgx1Sd+#O_31^~s2*cvNj4g!oR#EMj$Cu%yQ(u5~R9$O?aY>Pn2hiwr226uK1{ZZwd+o_rGr#;!KifdI9O%{x&dQ6* z^c3b>Zs$8X)T}>WbkG3DPRqfU1k$1WhlKWg7HjhiPW5W9k3@(@M%~sJVVk% z+AWg3^p3)NA;!(Oi=J+UC}y8aeifHRbVO#FK*9*bWRe;dFmz&ID-Y6z9TOUoxh>D}9?%l%jt=mS(Pl zUV}K(k+57HYkN8V*(mPVLyrK^^y0T%K z8)sr8t(@O|>0avT=;;vS=H>#t;>kVZ*Q}1;^G6r@9jrp^06JncHYwYIe;11O4Ql!<|I`3jo30f~YFBmlX z|J><2!P`O%^p3B+ib{t){|#dKpa@!N5rm%knMnCFDo$@|4i0F1Q&anA+y_WHP&sO5 zEB925E8|LR$&DlEsyWq1U(GyZbcz87c(+B?Sr*TyUk%HI24M!MugZ13yY8b}M;F)_ zsFZ=lEA*#o9b9gF*5DCb=eL=7F1|w}739dzUL$C{LV>B{>%ve>%ReM`{lu73E>b1@ zlhF&)?O-MW@H)~}7{6C2foGR1M;J`EyiFqFqKIrCIw|mDPdki2_(^0J zTd6|R+W*1P=*e4fm4j+B`Dad7Ksm`>P)-u?ubkvDzMLfcNvaO!gWLKe7k#WW-b;G^ z3kg)?tV1B^dWpZpHDYumZZ@JuMY)&c^|k1bj;-_>l}B$)@*AFZ<~qDUFtOl*sEFhp zpV8sNk2MGbM4BU`Y~i5y4{6|l{x^4a|1YJi4*>K5rEKDV%%l(a7lq4M$2UaF9oLFk z=eQ8NcCp`-O~-Y9oyGmmsw`x)_@-N^!w?s?{ft7!ZI)5Ll^iomC^IXkKKXE9x%J@k zCzk}6;o3$+Ear3*&(M#>*JQ@4x|F+0A)j&59<%6i?G`z6@64zaVB2yMu~0KMFSx?j zV$a8*I5iS7gGh>Gp73Sc2o6q0pGjXmsKd~nYL#uqpNN>HHvaYKRu=BXhV;~k=vlq_ zYhIj&P{kj~_@R&<1HvDlf8woU<-4o`DAuT^=rRTVD9vYA>M%*bp9|HF8UY%uVz}_h zu9SA$ncN=u83pbrO)E;1a|+0D_=3q(j9}#*!eDy7b@eM@h8Y`vNwl`m)twNfIb9;p zOB$sdlC`2(e2(%e#C-#XkV(i8Vvq0x3hCiEz9lOc4MG}@(b+m2k*56?oF5Dzr!6}ZXw)}kPO$xjK08F`)ge2fq4Y=A2*%ydi6!~r z+fI@Sr{D3&QbM$v;dW-@crUL7ILFoZp;FzQ?V-B7 zTvK$b08(29`(ykACW4Q^SPbi7;###p5Ra=6hO$pz^loLKJ~_L#+LFXr#Qps#J8$?Y z@=k$m^Te&@FH_fCDt*oxVcda&E3!yEQtHi_rC`w=j76QKn?t{(1x0ZlqvzeN0}7c- z0=3j7?{cfGdi5wiuGj!z?FsG??3koy;ov%MOp2@9G^<)6qHxV&J$;5CM*2e|7 zmA$$SMpUO8A!X5}=YZ9@rJdb$aNw`ixeoJR=co=4*@XvD2}yzlm_g!SnDU;|5RV2` z2P0yhLS}meZrZfReT3s--i0)5*oS#ngFp9uLxVB|YK=CG^>#}*B%3s`O~%h~nbbt2xA(TZCM^oEjVL2J*N?LuYP*PdCKT>%-Wq_b6;J1PIz8 zUB8E#-EZJ=cl(>p1&~s36}jDb8rnAcHlJ1C^4&pZCA11T?2pZPy@3gFD2sI=u>-^& zM#x9+pm1HcVS;xLg~JfKPqjUaa>_@)e`PU&9as3v>h+ATeS6UfBP720P5tWOYusO^ zRybA+DTd-Ra^UIfufc#{`X5OTnDD`g36!pR$qZqN=Qr`Lr93ofb0imiuf-r#?n`H}W%3_RHR~&`ZLY>6V_w`nENxj5$Bpij}sXc-{n^Mm7XJk&@+Z zuDzG+Ze^gophA+LC)Hov)_R~lewD@&-A6Kf*^H+V z1cQ2yFhR3soK4_zvpG+z6nJA|7^_RNMK7VN9)RM4fTehK&cLb&sO_Qm@YSOhz|9J(N0XX#&(?k0r9=HmuaAn9c|1dtAr;7R zITZiArfNW#gBQzUMq_ui8wc1XxK=3NVO;8ANpe*SLb^U5m6#eRNi7BNXi|qh*nAD_ zV_iG}PDWeyx=gAp-kksceexv>m(3F9+7BfB;)9AFLLE;ri`HD6=2)lPT)cBB5On5& zAPId(rSIX%p3p!)z4D zboJYwqx_K`d`}e)JzdaM?Ti1lyyW8B$TXp+H3HQKOv`=XAPUM?A@9DKIKhv^~e^WZA~&Au*V{gU9%;=DR6tm;|alAw5; zN_lzJ&7Po2QS|9nw|$3whX+6*-Zwl)=BjtiRG#Rp?%B#<7q-W;?&qEvnTyorhuE?K z$d+(k#I89&`voR|s>1_z{ow3X5Pu9Ra4M`oYNdk9$Zm4Ka7P?9RGxG2M=3Yh3VFsR zy1_=C$yi?2@;8dUQ66N}qi+5ckVjLawetv!S z8_$62{}zY$f*PpK1zY&byDD(K3`>RAaC^?*y(a|>%f;;_JqjEBqcdgxhho{Q!LW*3 zqb8w);*tXZ0Z-js3VNO-*3Uf~gML$BuYdmI``^w0!?t?ULtD?r(o@~_UUd^k_GiaQ)Q0{eXnMondN*Ewy-?rj@m@; z&pSBxhh?u5IO=D5>{j_b!*USA0sM<0I03|l?FLkImsK!$(r|M#GuGi`>E2)*gL6H9dIkh9UALGgXpOA zg^_PxpA_3)N01ejLoi)?0{1YJU{3wsm$B({JwMp3pLnM^UDM&By#zbnwP9fqgO?NL z+&GHidiCzHz$#(?4nO$tvxX?kag+ooe~yWN+*$-Od*^_=9wJEyLA7}iNHu*gnauz3 zEfqouy>!^e^bIn7tzz?r%c6wc^U{b}gNgD4&YQ}C-6?B*!OB$6<{qlFwKz83L8WvL zcMTYxze^_&9ztj$I4lq@qTMn0jafL|RA<|)t}nD2pVY-dS@O;&%UQ-)%a4UcoMZi( zhz1Jx$?lfXo{nusi@|H}8hR)v-CZ>i-^H=@Ue25DQj+sEOgS>x<#TfJBG>J9-~LqB zLKG!s87U{|x!Y_BtT)J~9S#|GO&vT|+8vtD-ia=cf<#WHAE!3o9srAomBiCIKVr>g zJnck=&DxzH*_pb_rsJ6iIeh2qm2`Yar-LQ?4fDtifiUb#5{!?kffnthOz#sq0;wqw6T$iwhr&)iSlxPnWNSZS)JN2-J|w%>^8W0|bOo{_e1WVe4dC z;pz8dXt4rjd#DITO=I4Z`I1=TjG?>=R%If3aiFt=x?wW}pMPqogqq!rywdr?brffr zEVw~l-@n&tMN}XlyINqqb0zDZ*Rf$e%g6DmzXNN9ih*vkTa8XCwz5#9!vPHy692mA z6?_*eB<8_0_g==?-if2p+VFpI8E9hlM)(zgiSkI*tDtk2RP@S4Q3l=wYWq=_Q&7|e{iYE%;5Su2yHAW86EC*GRQEN+>KA&_gq8xRY0(Y7m$0g9M?BV{`&a`>i!CEgeliWqJ*>Y^ z`pqIGVdGb+RX?ja{wsyB8gx#)?!`zjB=D-!2N_kN_RijA(5*rv6$_l<7r z?)IY0VSs%0Az=8Vdi4gFwLtmX7yR{UK2t6v@3pF=flCueG7+o)L9)epbew!vQMUj@ zz&M9!qxPvD@;1#a zP7P;(H!H|Jut)9P^TR1TpBA41(eu*hUEL{^`T`*O4V3x;%uk@yzx;P0jS+<|q#Zq% z#gnXgE5pJ&UWUcFyw`3`cF^|5rMV^XHY&>?OIwsBFW-E#cdPCO=eTwpf~)&0vj!)V}M#x0qd2El6}XV3cqY+@JI~Z7E-r>t^P|z z5OfsxnP5n@sT;%gfaevfcP#(*)+ll}4^{LUR1`rY!MC$CMKUIXXKP?&#bt%22AaVT z3i>`B7gX`iNYvCo#Z8PU&%R9AN>Qew16LH<49+_bI?C+f5(K}-C5HeZOrs2axiVy0 zb7=}XssO{`SJrF69~1ucWn9Pxh3^x<_ptQO_Yf)rC02vj_zn!c5vNGJU6dE{kKBrD zEWTkuqo4d6Q>F5ZGoVtabWY2%^1u~YnEa6Z{;o1^onsZb0lMit3DdBQ% zweRAxPHFdH%qKLwNU2rl-j2|=9H{g485G!c3a%bYJ|BxcoUWyN+_|_TY*FKTdr#or z3L`|75)OyjwM++Xatp7DE6lJYf8+~luIRbUSxf1RY>6oEF?kukA8%xBT8?r8rz+5y zZSM-3nKxpa%2H>yY(hZ`f!Xv88AaZYkW~6TOA&+^A+M;w5LmN-7cT;SfaF04kwPoj5lrMvg31tH4Bf9ew;!Q!RT@?|#*gtqag|5INQBlo<) z*QToPl1LY#bF5Dz#lwL&kT^}lk27(IGX3lo^=V9zn!tD&wv4munNL85aOq8!%@tF5 z^AB~W4_)GtYv4&l`abWB(c$=HsSO$!#A9Cd2}eoXa%QCNd(w%>t$!7l%f*_v$h!c& zQvZNcfGao!|HQ(ZT#K}<^RwNA;wldwQhGnc8TViGPPKl1ZL->)w_@${%7?K^h!1;5 zIFCkF<4@9=!5Oi0GcYIPoss83m0>yT`HVk^(=`iZqW*?NF0XQVjY{8 zjTzF8(jGu(D*&DXy^j_VI&wv(h_m;1X#*%k!gJfynX*Y9yD<+VFe!7tk-g(@I?P8p zFF7#Ow_Qm6LCSp8ncAf6)2We2xW>;sjk#O@EaigWcGa%jdg%RZC}Ca)K|JDZX{*vu zTW)oGH2wVpFfLbhjlB?O*29b|vOq26L4zN+1#rt@vGA&3RJPun)a{)8)E=IyB)wCg zYqu;gTB$Bf6vqwqB0Y#{ZN^n#KbgZ!ZH!UxDqvq~Q(k|OdPJIg)CL^*H(o06&OG4H zAazfK1pZUte&We|ZyT8sREm@}d`ohLB!Y_b3Dn;5eUbP-n;uLJ$xTsiN$YNR?(wxu zN;Vs-qyAh>NX%mzzV`%C+AmXUCNhX?u8NnyuWGmh*_l*tOn>-KmXTCBRo@d&yoD50 zltQiVVNeiSB_-j-GRQNw2m6RYtCj^66S;z>o~>Pei>ARE9KNXr^BCNvhZwK0kF>^mTRG8ZiWPdJt$b7M<|1kjBmbmjn#Rbp*QKfr+6Eg@BZ2FcBJ8aXIBmZ-lPZhX|tf`Y=j)$U$|_M>;HT~ z_BnPjmBC(HzWi4-;@D)q59f=pUl=pS?**e+Z|TK^27yOLjj$Q0{ki`1!z)H0LfZoo zdY(r3)nriZX#kZ>0x1q`UX*v^3@_QC3>z->H$e#tTDQgRh8No83p}4~j8>Xr1!hU~ zl(L6MQ*FJ7ug9jyI#iNM7X|J9p61t}FyFNvoli46rse3C0S57i6ceWi0^PvUgY@@c zn=L&H-mK*Mmp}`Ec&N-{j3)@*@f;YQN1Pf$Z`K$DB$K8@z5F&P)N)wX#(taG1UA!h z2L6b?EQV$Mj5_kd;5%HPhO{I6dl-`Q1uK$T`Rpgwuw+H90j8veVP9*QeQ8t_CF4xi z`ycB}zuoa^{ccRc^X0RiolUqK8x2UCt=oMU?T|h8%9Y3I=M$CX!!Z!l7z*Esr<^B6 za^{7=%x_J|sjYb{BNuo$ce#fQT4M6x^q4KxX^Xqf%hRZ~se~f=YJu zi(tM1UTeHJ|!oYz7q@DIlj1~2i6ri1oXn6s`2_gRYCUBDY&WVA_A1;)%I!`Wb+!&Gz@T=-zd+a$6u_W z`m&!XE&cl2?0I=HyMdRA15qK$Vu|ECY|k*>AElymzc^RRA++*OOx9~*SPvsXR=L*x z<42)iqf4V+f(2l5;q5V~dtU_SfIn_wgAFn9$NNx~ZpRz}54h9Xhw63$_ko4>As*QW z@*?d)lRKaLg2WXbUGS*_7Shm9cnfJFWFf`#K#KFg3Cm<#9uJBJf(w)w zQEB8PDBHt0vHNzvRH1N>p`4ftao#2trAH)MwkGoO>cwA#qdd4rQOUyr42~2*8GNPk zC6-MOI?IgZ&ojqjW{i*;(ukhOsj6L;xzoQlfqXC+}HfOUc&S0Kb$zS7zxTty_lS z+!PeV+2(Mw!PEAelhDQm&$5yi{TatFJ;RMo*3zY=A)2&A$IBTKVf#H_qb+v|OyZ)N z`8_0b7i!}q!A&3>RVF6|x2{l2+LLY{ujcI59?MrA;(V@{OLB{+GB1yH%_Ew=A=r`Wg;_y-SUS zp7dh$F*8_a(Kp(_>+A`|L<8)=82O|sYQTBvd#$ItSp3LGR3RIR=#CiFTk5j=Ft#uE z04YC?pMiQ+W@ z3Ut^<<1TGwb<+b(4M}VZn=VNT=eUd}g}c(mS1#OKJn?{y9YvV2HnYVQLk9XfVz50g z?J_sj8UG%=U81r{LoQ=+aW&o;Mc9(Vl+FasRtaEK9Xot}(O6(}ViI9mJQf#a62~8Q$sC=AqgqsLux-4n@YQyhd&R zgdBGlVmnTDUpyf<;3iYRb&Efw%9pbl7FYUY%fv)0#uxCk1sp@cjd%X_}0sG8GOV1UV0fNr<$+d966?nz&FsM)Z zxBbZyLP-b3xi(~FKEzeIsfm6M1WsM?-e=Ou6!2AGjXS)5g|c?a{qD63#pou_L?@a} z(LE;yzcrX~g#8_F54twZH2;#w$(f4b!1r!BY=1LN%4xvGWBAHFH%q)&#qcEi&c$vt zS7S=dLBP?A1>V&~AOK$ELjz!6A<(Q+#qPQ}y6hOW?A~{dbH#b`TyeacT`jCz#{7L< zC-3b7+7($nd+Sisyo;T0k1^$VARyb@G$QC z;njxXY4DI*bm5PyK&b{E028GwLj~D_qMC;r<~1ZQCls(X26P(BGIuFl4dlfyQR5ED z+A&!beT(UnXT~F4l#(AUNtkmb4K3m(>{SJNRT~TQ#Wy}DU8Ra$Z5T!z$%>1Ki@6M@ zqb+g0Zgt_4A(;XR85h}OlRM0@zmHwh3Mb@myn_NxnZkhF=a~ljx;UqCGR4+DOXIVM zHpi8EqmkmmFR#&XuFYP%8;^EP%l?*?tzx`KnpFa4UX7wG^g@4u?|%H=D=IKcnGEju z&nF1HGAf5d-6g}!>}AVTx`?4f0uCiwgXeGYzboDt1+usPLn8B;5oC zn|?)Q{V|K2jSI3L7nByi1za{96E@KxO*ia^^+SAa$#j^m@6Uw8dCH*cI~YJg2Q2w? zh$X*> zf8U-7$4FF0j!i~JDm!rwvW~s?UXc+YD@8atCo8K%9D634jL6=~PBMxjgy?>M)c1G) z?mzDTPS@3S)pf4-dB0!d`FuRl%qb6gjJROqW0aA*fpcQC-4AbF73VMNe)(Qs-R~)m zQ;BPq#v(K38J)HxA!BLPEepZFRcQeb|2N|IhXXd8%_-b#Bo9+6NJxEmD0oZOXWz+d zK@WbKz$pQ-vD--Z03{F0TkoB_k^4Q+R^6?q;JDK}Kg&}{jB}20Dv`$SuRL-aVKlN6 zPSAM!i^3GbNa^EFo*}~j1zXOuO=7IZbD)J%he+oVth%1>(1O1ZMwo{~tqMAsv$L11 zdnk}6Mu7edg+JZo#L)5M70wa$r_*5r%$s;L)W&mH#3a;0ouciI#Ui_uz8nn4$|w+a z9j8UYwog}9y*Dnb{ZQwap0D@W)#>IIfLOBspXg_u(`hP1mAqFDn>!<}c=K@X@(=1oW~Wx+|bSEn8^+ zSBVDlHyjIyk030Iv;e8qJ{x6!QfM*&1^MUm7rXb3{!r)kBEiTH{OtRHlx?bDq<)=B zEs_+qR6`5Jy{BhOfugehJLo>`*RKdiG&AduNWj~4>$?35{;HyKA;dYz zeRd(Y8_fJOvZ-#>L|NQ%Bek4{y)~@ch;k_C$WFxQ&l6bS;e`zSt1FqxPu6|hWP#*V zuN+Nwynz-gQ`O%YlB|+jAG1+>1x1vXqhpUCK<}x~{!YW!)1H3_I--%!R~h)XmYk9) z&#l5dY;WRGwg~RHOP=3h(~S7@HYEx=oZD{$)Je$EKUFd=p&wrlh}9YFUDUt#RN9v0 z!2;Zbmngn|1#kP)brH}zpp|vN-yI-I%d=+xXO&gP`S+Ys)YzA`gPKLPk_va}KHV|; z>vIA%lo4{yR#+@-isj{OyCX=%tsbZSP)+IvdVh1*KOe_VMkf_V5Nr>&7DAMTn|*ZU^=4Anx^T5I7b`-w5G1H|0+`kxHcuc25C*z@IqLq(2}u$?^}UEF4zNFv^Dc{b8HjEI9sb< zIevpSXoopZP!bo2=ni4ZRcb-m&-Cr?pS!ssVW(#1J3b}HS6d*Mw7_4OH1`EQ{FyU$ zJ&D86#QdTgqywNOAHsesArXHRfM%R0(ngD9-|YHWaNCZZO`7;Ts7_VyCo(AxkRiDF z3oi~TjmPs}l868gJu%ASi!J6B4&uKhmPk8Em#{kkFZfN*1=#G+dJHMuCTC)Big5TPK73MP zK2_WEMc8EV8D{i^p*R>%M-tk9E3}#MS12QWj?;oXd1k=q2;|Z3nQY|>nC=>5*vFVv z*r!ZkvI`lH(537#;Hrma&u`U#(NEfIU3mCC4Pywt}psk#5gfw%ZAV zdM1s(r2v>e3mIZBR6(%Hdtse{ehgCqq91`FFYMS78c=P40af~c1FEa111do}9mZL$9K_}i)et>Jp(-51P}eTGuc}EHG*PzVsA!!GD^Uqyo8*Mg)EG->b7Ck_ z7u#PE^Ww8{7l-s|#=!){sA?X@SMP-%@Tt^XdO4suuraY)%t8H8z`{ z_Wl2uP%>VU4#SPvjk(OBGDwA0rM#!tFb*rDdjH=d{cBCs< zsDwBdtp|(v6Vh9+0ZZg-J&W%xsA6E+8!55ayHQoj^OjwHil9U{)ItvKw;3a`~Z+L%L0W#f9*H#{Br}E zPIUp%spUE0;r@sCPj8XM=!QCHbNB#!EzMPn1k2WNoFqDS^< z;(>%Ux$$3>1&{+>Ev#(kG~+M;W>PSpGN_f;s=ZTtN|%;?i3FQf{9J3aY&Aele;I{q z&^sqvjGK%{SpP-CsGSNdm|Ej?BpEMHHvgd2 zed}hGoqS8z%_s{Ts9be%7Fg_RE#m%pqh zmBWLx-#G8BUdhi~-=%Z}zMkVYzKp*@emu#@c{gNN!C9(1{@8a8@2t)KaLdjcqXl*w zQj8|UxJqBZo9_9qQ9G+Eh4yReAKd8^bXE5NKxE-rAdkjD3#!jFhERaiIm=axVGU*w zt1D=X#4o7d`_>(F;oV;+{$HgkXDeHFIf~cu8-q#%r8DJ?_%CE77Rn2WO?gk-UApiu z3oms}ZxG)|7tq1Hll62lvb$hw4!*{=(~Bvj8cymIJ-Fs@KbNMYMG@&M;Sg56{zIEk zk2c}0Vq)_ViO|d)i>-Za$f**_xC7a4@7^rob|mSx^Y?{1dCbg@RR3ZC6&=o**&# zh@~g5vJEKO{|wHhOWsCHen|}_%|D{t33-#;QBxO->qzoa{-pMHShn3O`on|cx|a>> zZ_(be1S)Acu(|Q^CyOfjs$5=kp8_)j%$oA;$Rzz#a;x{N0G@J}trtBW1!zc`z38|p zyg(5hv7EVauBrbM>T}XJW{s4lL|_}V_Tgg&y|52KUlVfbw}cAmlC8Kj2s=qlV16UK zm)A^`D(BOihSc}1k@gZDg*t9~{x(ms-#cwnBJI-wgwM7Svz;o9z%JVOP9+L%` zEGR!N!O20K5U39ZhxPgaDS7QeOkhb^-U5EIGDx{N>~Em~^!zBaJ0OI12R+8Rr2K~0 zy3A1d%R3HN=U>42QBelRq#WVCipK31P$6C~;5WWr(|luqB~g97ZC5J)(fA&~QPkW; zauKeLKbR?7rF>%YBJafA`9aBtFZ(T;e=q1R^tqw1chMTVSICyeyY&Tsa&$;ryIXpf zc!mrU97^Xv1@Jb>`wdph9%~#-Qtf<9&70% zG?>=ZOK($Zk2DQ@1huv&Yf0LIf-o#IuF;@$aPQ0V;dgGmSsE>$f#5^uy+?Lwk7=~F z3%$@%4N7*xNr#JiERdvfjzs9|ocxg&fnPTZb%|~ISyN zm;l}bTPXijJ#?40S@XB%ZyF*7cypS%7P6y_lFczHiuK@b&d^0C0wZHb`X2gnHQR%?9wLPh@}(L(me zr_e?cmoyld8pK`lhfzc>V`$W>XFK_s$c}a2JWIIYtP48v4oH z3VV*v6BklIUwyhJosExYBzJyOX-OzJEGuaUUs*?qKDqb_0WN4FEJ^uoD61TF9reVZ#y0Gh=FvScq75)W zeJzpHZJx*I2WE5fhlTi-A2rLl}c)s zH&%ONE~p|nA~dB|5T+eqA&ZlDyr*h16S8#=lNe=Sp%?OJ7d8BA1~sA_8i&Iat4xcz zXmV023F2@(5su2hy;emk`vVvQ^`YoA@vM_!-VcIl_n;*g53Zxnh(qi}cm z!xLmFjEO*!H<=`NvJ^I}UEM4B79W?3NQ&@M*Nhh4>DX+t)%_E9v0kNreOZzkQNHEZ zHhf9dkA^Qiu!eC6f_CK~K_!cRqdLRLeWNwrRW|sX`E~$nnSFzViG1`fp>+@2K#deu z=!$zB+*1r|XKE*qnX&lOKxXNUak=+nl(T-S&cj7g_Pj7Mxk&+KBVa4sez zLBp##eZa5gc_+q#f8kHSo-*%3%#~CnpvQU!U8Yi}(ySdqhIkI{cP9H|2=x$%PI_&C z>g(K1!RAPeQoS<#8TqWzl?y7;&ccrJKQkV6r%-l0u+qww7tuGYtvY~dwj1Q?_iNOM zKUOXS-`;uOSeg4xrWbNG%imSJb^(;dM!4`su*r7d~Km~swouu&5xA$d!}$c(%)fjgrl5|BJa=c5DkX!M7{37%iJ3! zVW9oA(*!Sgr)VyH7bUXN-3W!F{lM~ZPSmhDOW%^+vjCxqeihkOMk$@`UT}teOG+US zqc9tQJS+;`c3<*b0y9_OTU|o>-d)S)inDwFMQnf8v^_g%7FV;v8G6?L_cKdXl`%J- zv5RJ!Dhbk^&Rn=y#x;2k?--FaUItjrz?NF&C*d}H+ISm5L&QLh$-|SLQ=}|N`4J9O zr+tUr^|{=XMLz29f^>HMC=O*GI}0<#sHKL%>+zwXa*m=UU1XY1XgV= z-_X4nOzNl}O3{GgHw#US_;*g@F;(2-8|G`KNRE)tLWTR^dWKwY%I@=4v4h0KVDPgi zPRY0m)&`lw1ow$k%m_slsW#(HrQDwooo=>07tif^WHDJPJ)TLR%x0-hSmOBw)0HTc zojm{On9l-6WhO84m^JC1oQ1BW1Zz=|e@kYBZ_lRUmsYssc@AYG^JbR|UmsV2Ak@6} ztOx1Pv;Q|m#l6P~DW?IR{SRpQrmUIt88&F%m0t~4+)pxv7vuHu zdp+9fQ9B0nm$bPf97(6tdtoKLcO#k!`_!;q=u3yT+~1VpOc5(#tsNS$$}mLv<9c4@ zGVEp*IruuYdy)eziHU8AZQ+DQ2)#x& ziQ#0&7ivE5lw@ypp&u}}QpgJRR#4i9pM1vWD1dGe?n3u!&lKoN2_WtmV&8pwd!K%N&Cm_MQcrTYFe+!i}&3X0>44vZlJEvd~u9^j}%Wba~&y~Z! zRKb;|gRZULsf>tMg(9h>0XM~eP>xqyUL^&^uI+QNhf8{xhW9FQ4^BF-Z;eGX+}&yd zA1j<^I=H&#ram5UNuASz>FlLZ3;_B>2^VghQRUnZ>fYus0m#Y1jmQ{d-ac9DcL3zo za4M#KtKBomC3tZ6^h(8nvx>EY8}Fv5${S{opmNMBG5VjG+MN|sB#+*MP|>$}+ZA{e zC6RowU{2b*3MwVn2>6iJxeV@7j`u-P0;NvpQqY-3!uSGx=_2odl8{#|zoek;fd&{e zXI?=Nhv;c)lJh>gb$_|0yyGR0-aWmC-O=yaf#pSR(!{8;z7l*=MU=a!eeUPHh5^d9 z@7M)h^^Ed`m^on~F}HH!q(otNF!RolR|PX~r85Yck1}71UN5JXN7lXq&pvcu7r}wm z4+ICc&9(nkmP6UQ{6b)?&Wh#dEd8t_Xhk#h%qJKU;u8b7wSuMel_ZmaXuLhuooh`v?jlHe1G*P9FQyF4>0ycg)0 z3uG^kZ^Q=-&pC!;$L^3vkzbQ(FwPhNpYy!91f_X>`e-1!N+k(#QXw|g+8If~e_n3dZLgMI#YUL247_E&W>_&(bSLNqAklc#HI0-)+3g3%zxRt7)3K z%+RDvsSHCzdn&`k0HSA)zgJsY*UBfQl*^ z9y1LqPNexrFf3#aZh)zY!EY$U-E~0jywm0(`6?F8g|t^sI2HqqDmY=~sy0I#%?g8Y z@cNtQ&l^73pQZ#^O4W9~jhslW)H{FH8QhsQEh{*Yaty{J#TB|GR8`FDZE_H|urU@C z{h@pOgfl6(c7NoUef9+uDr8zwS*R}YHy83e_}2ont*~%|oZMH>;JXr^nGvY)`)_ZM zb3UDo(BedL7eDZ?nabMRRs=lHvBD>yL>20@fi3`0qCzzl!1exDqJ}{vKYE@IAI(>} zb-kn7gJVU#OSUCbInm>{u2gq$YO0Suc$CZTBd3HeNN;Rlum%bJNRN@R_MbtFX>6s!{P64_Y_*Htz2?TLg~xnsXk zUSkufz+;DsHuD_}&vIRP5-|{lWaKOVk*INZ)K&lkl*A$ommE?>-$yDUdDga6O~gmg zw0dy)C)LY;ty+K-(qxLpiW++NqUVx-Q_sFDQ==T8Y@j{+CSokqJP~U&*2bZ=WM)s# z{u=w!!KfVh`jg^p1yY_dV3IkL`8PmN3pYD%+FK6}lE&3i4CxN`GK3tf-e~6>lWas4wHL z?Y?(wHM9K-HL|_E_Jl4yYV~sj>`Em3=3ck9IVCdV1w&3&$WUH+$7}Fp@dzC}uljSX zM@|9M0&kyXNMt=oi|5Lkvw8b0m?u+Z-9g`hjOcQiUq1yBqz#kFS^YUN=7jq(8cG^P z5jD`ysa(k@&?k@}UVC`%Yf5>CCjb45wkS43qm2G!uSC>v$@$}Oz6&Z)QnDgOlqFX= zpzbyX)on2bCg38>zRc8BMl5){r4vDGKyrD*W)1p&%aHW{q9yVp;R8u1oIv#ZCyj?u z-xBdxNJfRZar;}L_ z@J$Wkw@riLxnno&D>q7aLg3Tp-I6aKt4x3;Yv0S>hK1&r6SZj8Hxl396h)4!xt2Q> zqu_VQECeSV|MI2zx0+dveS~*G+E!HmUxka3V8-ef&k-C`McB$ItKVD2>d)S^BT2cz zCUap<$qP}@7E)lvzICH$jDl*xmg=nb7~Zjot&xu~YmPFe`g*sI(zrZXfQA8dw-)2h zbKBhVIZJV)s~!{tck{kW&pYIa3Pu6)&&HOjfp{gFmH`~o;$_dx)BORY$iA2nDJ?;; zO+mugb#CtCq#-|b>S_mNjp!9N$A6;Djj@+#3afBVPlVMn?lRtn* zEMrkFKkPGcPw$O$|G=O7Y@%^If8(_Azax%EpDVKIJSmK3hLgIi939S&-P+k(jaZGn z>SA#cRsvZERC=~YKy(NTU!mtC+|^wj&*dV&z>q z(Zg$lh@$U2w{l^FDiRxxpQk@pzT%cqL~+)+sJ0l8tU_EmU@7Der1WO-$T8l* z68`$ic#(A+(N&$?)mxwuer=emU|!l>M31uxdgxn`o#({k{yRINi; z=njaOa!foA!8}Jx!7zuUpb>mwRWefa^ z{c3{0(i(5D^C@zbotrlh+cV6Yyn9ET_E=2UAQzoRJ(j3vW+~Yd$Rm*St>>MqU(11X z6`^d8oC>&7iIe|csktd0HpI!n_!a4g1vO~;((#(b07RGGHW~0`Z5sdJ=E9;#9!5VZ zql(Om;VrBWbLF?T<^wDJaI^qtHd`R9X!lF(EC1$2ypG9HT>Mtb#mfljPEEEl2$p?} zOKfq9Z1AcFN_W4)>?(aQtrk6<13-x&vjvF5qWyWE6P}ZQD+HC-kz=({bmZt&I7(e} zaLUvGwd=GEM^Wl0y&Afo!i{QLXB&bQK7PH)phf-Myqa6+qp|v0zRPu|SX&11wqv!O znvQ|}Q{AvKHm+p(b5K^DrPKB295ahAoYcKEAL|z_nQWN~-~CMN2Gv#W@b^d%Y;5WV zHeIF;h7NqdUt&i+qETxUeD%Ph9tWFSmlGYqmQFnsDb$htd88H}Qr{t^iZ|6$gweZ) zKmVC^+>kSQ7`OAFR+)E>{7S0$R?!c5PzmKOvs8TaBwO<`RXch}O?Pwl$H{#N3HEPV z)yf?;P8jK2iQh}BjJpTES(th^0iue>ZY<48|ft2td>MNB+ zo=_9Z&XZEyS<+Y!g_qWS4_E|am#ywp<4E2$_F+mY25_hAd)G;Q^`(vx@YvFf)o3-Q#8Jm5O zL7sgEr=VP6{G=<3H?*KT%_2_NVWpiPuXiIT*xHLRX$vB|VRjXM2> zq~OR{kEPNkE%BG85fZN?Nu(pnBrhbaqs-vWNj{eFo(TshR7@UzVS85U3&2)Bt;RKf z&Tsm{<9AH%{k+Lm%ba($n8D!%ARclb8~#VU=pM9bIbBOZb^+ypSa%kT?$;T)pa+|! zfOsKRN)jm$fM4aj-LxRYDal-_v6g!NH_fb99hH(~d$!m1Mdw&O9@z=5Oz9Ay3wfL!3fJzD#hcfq=XHsLCKLUg;JI|bQgwoRrBUMI-m zs@L%~VVj^qSD}&eZmf#{qIUJAi{N##=K&k0q783fzrM2UdpA~HbnJk*y>Hd(D791P zRSFXJwqxVBP0txnQh|J#NvhC4?h{m11}q(*;P>c*+lm~Bnr~O7AWu$C_fk_(BPIU) z=UBQ1(PFXN^A32D3-*8_yfI+r(9n~Tw4SH1F7e^Tt!P_>{@p-LPYluhS+7S7AD4|Q zK9#k9H7t!(kRhH{iZA7H|9un_8x|V|FgGUKHkx0<+@F%F(vzOKf6i#BjSZ3Jzm=Mt zAOop};xR&3+(64;2>U9s;C_y$Rlriq5WBoWU?Mi)MPnb!+2vr3X=w~PC=pt#kdGEg zhS#`p>>cKL_@xUM!1BR!U!(&Ml~2!pwyBKKh~4kZhP8|YABKkH#^4lOkK+`1qHv;Vh~L`L!JIDGy>cfHh| z+lUUK5+si;nJiiI*GqS)w3f`Taq5LeeiYVASmXvf0CV`vpn$ajWyCX$kx+Q%|2s*| zfs&*s1cx=#D69zzi7?wh)=qyukIY!!`?#L%shi~a{T(6!CjMm4Y()7$Ar+lAKV9SK z8Qx^kql5da?N3Cc-+GTK+$|Zp+h{_z3PCP_rCl&EHeo0KkZ)4**3v$}+zhjy$NRj9 znT?dshC(Iay5)_P6`000gItVH#Z9S>!mHO)1$CS<-s0Eo7PEmxKo>J_fMLIWuk1x$ zDXx3-Y)HOfEY$;1AL}Y6Lz{}57olE%zlsLTrj2^W0^VXyNBvKSy@K9i%)$i;tGx=D zbmjFT25h0^HMXg!*^iD{jIHtby@24c>@2Q^crBTUM{z1BQW7Qd>y2SKx5w@)^j-LI zM(rB8aJX9Gxkx9h55;feOTJWQTu*x^QYc(8Kf9+u#HW@y-wm*lVy5~l7%a5YfU^U^ zAhTXi%jBs+5aOwl^C!`alz$wo~8(&v$omD{&BeqJwEPVwBj4UUiUE zkFxoGqz^Z!622B?OTsctt`Cp7Q)2)fPw*)TrXuS0%21r{MSSwud1)@aG6aJ8*H8lr zF7nuP?2Ta9<={erwTcGitU?5L_{U^26AWLO8NBl5#h?=3EMZ77fynPVvQg`)k&Qly z8LZTc6s3V^sv2ssD%Ha!?2$>hA#V)GBcch027ZG;Ef}|=mPS?hfZ^D4q{%PF#1QMP z-8M}3)X_(l6IBuENDP0BdS%zdu~E;KU=4hLAS3mIO8lA$Q|iz3dtYyX>d7xByGlDH zs+|KMZY+WR%Cu*83Y4OMetd+)*T6Ldl&#n))q&#~xUh}qDNQW)LP+56ZG46TPHaxzblY}P%dk#G{#bb*M6 zU2E;I!fj-_RpD0Biz`BfJBO;X31*{_&)ZLDsFdcqyf_mAJDBZ=;Mvdo=1bMk7aNxK z?-CvOcZuHnA862UM&872GF*+o1xSWHMj~%jAxjL;zH25`gwD%P zk%q(=7@txmKlBTrGrSd$DiE8`-&U}mCV;!52RBCL5~;m*ReR=Y-+Ee!DnLK{W-j~T zwD3nV^_I7VkLJG!Qhx7ueLU{8OXAX;#Sew~YLqvZgJBd8g6`<$8=Cba>oR0z!VzM< zTji4dl)m=^Qp;G!**kXFsd&spGi=N8@uT8q6 zkFF!Nj%~QTU$H0vSNaYjFP^%kvOYFXxcM>a`YTc`|0S(Ex_QeHzq)`9+hif~v17S! z5l2OYC)A}rr`kX5oD_gb$TC*ouUzobr!8w}fjR|E`X3^F^_(0-V0!Y0tNN}lw}KfBjzBV zPn$WoQ%8S4mBqR!jV{uzW>@+`T=+d9NVe7dNR! z>0YX%&dYD+$69yM0g<~u@krrOy?Ml@D4cGjRn1?gZw*o2g&d5#_tk z!Kidlu5P~tjwrS4`zT|PGsfcKe=tL!Se%ImBK$+kza^;^t|0zMhloB*!k{g+AGe%1nxntknp(7BPv%aeT+EDJq^5cp^ zt=TQ3To;ZUKwUTm+0gOW3I|p!@OH-cMdf0TC5_jP=om)o#f;wSVA?w6!%{#l-v^V* z#_9@eW2Z^(*mBtI=tiNl@*cv@>hXm5SN0jSsa^#g*pyA<&)-H&PKQxd*nrNUSp&SY z3ea56_TEvo5S(>H`i_LqJ>%bLX0ctGbP?aP^t2vzN@=pzC~ZwCx2@!vc*sf$tn1)^ zL)=#GzyI|m(XR5?hLd$s-i?BsSt2VKP33hmNqbl#IUxgDyI@2nRW#Gx^aXJW2c@55 z`qc=M-H4}&VE;_0!KasPj@DZIsUcrapaBF_Zwvx{Nu732R#5k}j@Ee&)aeJtV{qDi z2>A=fdjM=z-|@orq~sytMHwNH9^^wp{EFgds<_}z%y-oV6#gLvj-)Av?WyP!<>P7f zv=ty`t>f})x>48?oFEwJMiFMjqQq~A)d)gfy7ff(AaQad zzgK>}!4eS0r7pj4fDslN7i3YM$sX<{@UWrsdr;u%JGl@ zNg0W%_$GVBK*E{TkI)?EBZU^>9TstoR_^AnT zm}PiFQe#C*VwIDo%eGvG%IpUICC~CnF~6Q%*fZ$JA)q6N2EZm8!e;L%{o=+;TQ%tK z4ty)^9^79{)hj*1F}s62K%xIa z7w$B(TKNcnIAQm&o;+PEmFB?J@2G5?JqP2Fvq1kqH-=k5<7DUXL$MK+BG#PBb9z@j zl{H3;ZdU1BKYPGowd9N!y0n7AuC0YpIwq#yHF$GQshi*#2r75JA7LGt!DsRQwe8S* z@bH|YqCKsP00({7{}cCl-kz8qbXKSOHtqjTEgc!9xZ8haIEzMpUf>WQ`srNoB-3F8 z%MX)PT-4~2=*C)pa~M1fadG+-d)cqJ<7u+W#*6kbzU;2jN4T*slY(maJknI4dx${y}Q#V z8D{L6cIypo>H~mg+b=?|`LVoBU5-JkJvn;84Cz{Fa%TN>m&7b%wSNL8pKBgGL|kTm z7iNjHY|>Qtm{H#Pi+2-w3yJ#JO$jaeN`rRn`m1+(efaFHz&he@d z?x-Y1cus)F+mKqeD9TtE2&V)8U?3sQOnfD{8UOs<1nqc`^Vr{m?h<+(8U zvmohlD)K^VJ%{Mx}0Pc!N> zI15#pV=?qz1#H?#aFV|O{Za9pS?qU6pW7aC57_<41yfnKp6Ed7?*}a*V3)KeD5c-y zxQ*q`RY;u3EuW=)DSY0&$Fv}-=_BA%W^X#Ma-li{HF1ltTayEP_ucifPlC0NIrQ4i zZmIbC8{RRvnZ;j$50*9B$TH|0QY^6hQ*gHc#-m|(wnTo}$}&l9ic^?HG=1B z3rrsftQx$rH@fEELTynt)E0GOS?I|nYpqis3>zysU7CC05tbQ&ZpnpjQsPhx#xLHP z(^kSY<7JGFf4Hyp&D_Oa-5Xi&CDSnLyCRFMcNU-+-;&ERFQFKLw4{LVW4s-#!@6QR zrptUorETt&koE4jijC*=61}(UA>TBb-^hI87h-N%aJb+@2LqGEa1#4I=2vud@o<8| zJK#j;{#?>-iQ6PD&nv@qpRlY8T2j`m7oWU*D6b8?X&*Arjtkl!g=Uq2ka9(2eivKq z=}hTfT=Y2j$l3*4$A-5S950e3l-otZJ=;j`ZDxjO5D23ML_F)lG ztX7zL_^v-g%O9(MhQW|buxjc6KtxEFqgz)HPYmRQ!K@C{?$?mTS+XRKq)ev#NVog4 z+W_fv1cLX7u^LJHopor}>2H6!Urh@vISWgf^}O=IlGu5U+pJz;caDR+Q=XjX?rmj- zsD?>V^&gfbv@=-v-abyPhDRH>L|mMv`^I4mb8gqr#OUiR0znCvBI>^F4(B~JfvOv; zjHMltW2tFplaeEhAaj}slke$skGE?-UCj%>VRHtn`OpXdcQucLw`-4jMxpv||Ge<= zmx%CMK2Cq!39YnF%?rNMGm?Gh>1u2~1J&oM$SX|`1G-1w%Hb$SQi|+I!S$l%Yb_>7 zq4O0kRRt~-W%(Yzv-VF3e~@hD06!+^l4ty01k_$LW9#fPp3KmYy-)QvU7?~f!j16sFmNck`?G2fz;cl=t8P>z* zGS4)b>RQLqKpVz=gITY6tC1;AA+55DiQ_rLf2jm@LAlkJ}cIsO> zi*FSySsWzBsJLX4#-IgP#DZUX5c%RU)VH!JGR|W76jBJ}GBXxfS}VX}zCeXQC@DLu zc@+Pd7GiulmNK49zCKlvWkY&r9cUHt_D0WOuE2ri-}>?yRA1s%GAbXlyuBB#V4;t> z`h}h~*Zqt&ni`KT2>vODI>+7`qX~aB%RX)*j$ReHo)3G9s0>PK@@Bqb-L;hui&9;S zt(<0f1j_ce5Px!r+g2b@(VWhcIYW|^Gl;9@36CQ4>{tVaGP3?g%+g%+v&Ka;*GxM< zds~5PanJN8at?WRzlqY`nANvov-SMJRrG^N1MZMu6l*+T(mH`hvmS*MlKY z4S!$?J6_Mz6hE^N{OHKwTNzS-=HG5+(f27iV3Zp+82-t~RAoD#fC>U|M1 zIm{6WgJ%6GjKH%gh*?z6F1wEOj{5k^%>L-?$$iOP4jr2%?x{OQ6h}mJ0atWJ$QX$* z`1*|)>I8Z?SimbKYtM`GTsYQIxbUyVoEd~#Oyy)~_=&T-3K*39|H(*!CFi#XQcuZj zn8;)_h{@!hj-E|R!IuV!q8Q7{WQ8=;IBWLepySGXd9oL(YlRHOE7+Nt*YQ&RHGEdlG4_u>K z1kzOg>ZD&zI0lcI?cka>>*^}6(RGU%Q`RY&sLJ<7C&s<P>L8)m z&-}6=q;;0&#Le|C_#$M@4VKd3MS+jI4zwvL@9yn9Won;&!p8dcslVYL(27N6bLQ@5q;}pQ4a^8VKO+JK;w)9uO7h8td(uQhBVH952sOkvZ{rj z0BMpXBUZmenFPxbeJ%vf64u5Qo7Cg^9aCWEX>2ntiu(HP08!uK_~Ir>xWyvxdD%dNX^-4NUX7Mo_sVleUm zf9pvZZmqxnGFo$RAG1ofzJ6&S5varjLS3I~+FHF&&Z~f4cRaw&Kf|ZmY5NrPB!-lPy;X?tNI$kA8$yxD_kg zT~iJVOo5c*#SmF)Zx$nMo2UAw#`tWgBm0HNG?FfPp2uF(5@lQIM+-pE%*tI~s~xOa`- z0~D%b7@SB)`U&c|qtMys?_|EjOa64;VFWVCG4Ie7pk1o@Z}$#dj(<_86}}PrSEE## z?1U-v4p**K-nE+Ah?c2W=gA}0@9bLkz99M|`>UKZKPIK@kR;gi5wT9xRfC%aT|7Q- zIJL|bQTEIQv$&roo2uin;j!U_8i1H%)0WkkI@vfn7(GtHcp%r>@iM>CWMk5~FMi>2 z7*yMLLbd(f9@9Q}dt$nOW^<))u$dVw9hLZ)(qV|H1Mas}PoG`M%Hk(1QV6X(Vv!lS zS6~zcXokHyx=?t(HBvLXx;F zGg0OUu=63_#rU`NuE}9l*RT<@LBN9kKWlhv$QOM&NMaFz21)tQ90^QA0mH`_4CFm0 z)i9|v0aH=YGRGV~bxM4>Y|$;MNT(tjHD$h=RaCP#W?;OcMLbu1SD)Uv@1%}-%e<+U zNn{g9FTD0t#%6(QmsOyojFI@n9m?VwLVG{IVVO*J#4MzuF~S_ZUx<7dtjEal)c08) z!ROZ&LyLOCNe@5!0o<)9>R}|si@dH~t;{XqocL^5h5*S9)f|kebcm?*D7(_=`4all zk!``2E&PL=D?X@lg1x7dx>V((<{psXfZLP|+@_^=k5A8RiNWDj(_6AMU1Wj>`bO${ zgd>g8aGmv2h7WVG-PYbTFNxD#|5$BaU;F*^a%`gNWm+QE+*kmZB#BB?%a@gTcxNl- zKQ7;$5kl8x0VoUehfvot_KMB zyOwBfkt2bRah@A_ugnc}GB9kx+JsYn_zO>=9-8x0Oq8F4tz#d3nb6jtig>4M1j(7r z!L=u_VIu7$Z<;7*t?mY9ovxR!I$ZW`4{1dg{)iU3=B(5db@3&{K)aSDWc{gLc{Ki| zywQ0}Agq{+qy&qjqZ9(EdVfYt|5bz*YBZEAo% zy-mTFdm3xXMkQf4E(S}wP!JP=5>(?h|NdYOgg!Z&_LcTiw1O_oQV7mw(Mm9;0--eVY@Y|3$2Vu zlIytHkOEo0rL-Na6p)!&Bs5=iZ)JB8_ODF=kE7)~pskf*{JVv0k3 zq{nMVV_|qyqE9II|6%IA{6WALD+z-L5yzz?4r@mzL8fw zchC8TYd@6fm{wjHr|dZvWpIm-Kn%J3u~0|9G!6i)&Vk>dnUHxo7eb()MdF32K8am> z&36npHtE;0k!`nP4D47CVhj&MO&?iBNT|&Hc`4LGf>9G`4(^t;L*n6;d`Bko>~8JA|Tdks?-^gHj0vnrB}hhD9!){%XBME&<2wV{{Zp z8VPPZ(m%anu=nO`7Dz5xK516me!>3yoAX}__9ggD`|rwlEL$_o&4c*dsA@2I(qsB0 zp{!e>r-yb9sgt5zL=gj$;j^vo{fRu5QYLQNE^|>a1wOMk*=?HYS%>7cJ$U%iE*3>- z&!}@88hB|=fbOjfZDIfF&Jlp#8e}ZxWW*^&y?8eIUgH83^ArIwPbf9JnhN?Nex@YG zF83}BzFp!yupwT~y`iXw5<=4+wrDG3eiHOfW69`!#mx-=87V$IX6Xr}? zQ$x~P2I+9-!h+gXzRp;M+~x#di1wG@%bLLF_3fo>**8u)5SXWkPh591C}qYf&8p|y z9KZZ)f9di3RE%vqtq9vL>UcDR^IwK0M$qO1DS6H}Ibq7cUu_x6aPq(LB6=D>06*eq)%lt5%@ySA=Z zM^EJiM9s?DoxcUURqF8MgdvF~MXnNVR-8+#VvSQt)5ir@>%Tm@;f&As@h@hD9^FNF zI!1Ka$-SBo{e7}QTp0l|J++NTB1N`_q|wFi-K_|uUYl`QRz6GL@%Y%$5Kitf)H^Qj z4NlUvcy?%);S|jez=c4XCGIqk(kC^n@XSX!n5R--F1+kk+cgQl9sW?sY+#1|| zP+H)yA}2lz4FPu>)dW9)soeJF1AipxhioHX$2t%B_C-wPRh0iqW9Ou_kk#c#XB3C& z5Fjq?Z$ewlbC=UMDyQDm+vH*x`jwg!Y~o&{0+Q8`(_bD%$re5wcGp1SsKyZ;C1u=w z5O6ml-H*;niSk<|sfOZCHnccmet(^J!%LJ!sbK_Gw_ch^W#H)FBQ8HETz)oBbb0DR zE>9ttuSw7;!O~j3^#>i&$)Z;-+f>+D=-?f&Z3iM5QH?0Y63Ls?rEQ_QH@GNARL|T@ zwm3~1CfyW&_*n!o^?d);&FqgtmsJ86v`6j%Xxw=LN&!kL0S^5k-9b*dwR2nL+~`QH z{Zy743^82SRC(eAzEOwFLi0I(OZyKVm@{<<9oy~~h44_q(@No*ilg@tF9htnO>hQW z`5Z!>UgG=eCPd%-t@QDP$V^zbeZ$!8MWNeZj?}CY`Yi1md79o`Ing2EUu#=;^X>9I z`DI0cZ64i69x_5jd%QEkG#rWm%ZvgEgm(~mT=1-(uz{qy$vbXSonF9M9nHfN&&}*R z&WP0uw+!)J`?7Y|_fRt6edWFs64XL~$>vVGTIzC`fJ6;<;^nD*H;_uY22x2PudN;9 zpjS02J+WZf2l2mD(p4yxl!|PWDWoPd*ra8Cgg`FN^_M45;W{(YSBuOX=xYvFb{9T> z_svG7qv%rM=WZd8?;Kpiqw0T*KDdfDIo(Z=A2|BbD>Zrx2gyJ7iE z5DvVf?fjj}Du(7v1Po8-Gtt|<%^9}c+rz+k&1x7Q`h#i(pPCDpyQr9vlnLw)+H4%d z{HOE0<1Y507%#ve(KUuydtErsESXUR{{->}CNEC@Zo~khnVpo@oCRoym=N39_jP>b z;n&`+TN(bSngJa*@p_0?33AOtw;=#^P>VR^b$U>~cxH20?j4DssAlaPdBv+*_vmhQ zEd1D5Ft0il7V%^gY)hGRL zL`lBtj#)GMPCaSLce^<81MK5V&CxaSwT}6zozH}!rwTeEhqWyN*oD=Wr02*^>)79J zR;d&zg{8E0Sm8(Muy0!LBC+{cw`h4<+!S;)x)rN(F+*OB^46pW&Xz;>&e81PHD&32 z>N;}8X%!vdoY(lzInVdZ$4LXw9r7ty(j~J17aOT<2{?{&3}i?9Zxa}F7Ah$s&edaD zUQiOENW0}{#^Vgq2j=1rA(#=HtNe-4xm8S2J%MBz5>4cnvO3A^L7T%6Os@wyN(N7o-McD9c+ z^RT-Y|Du5J7sw)fIoe1t=%9C%3IeaIUZuIjY)|F8tD*O^Y)LQrdwOxn;|hL$mVA`h zynMQznDnF+HHDF3#oAH5Ik|d>*+UNb{p9+dfQE)P{jVFKM zW2dX<&QE6R%B+zeLYN12N&w3Vh14T7;mvep&B*zHmUcuVYUQ<1;DPN@=y#-WNzQX& zn((nPd*}fF1>cT1{JucD8_uS;u6uXr!q%rMkVpPrM-OnOSE0}x9#Y|d3eE^+pK__4@cI;>$ZL#mEMszm9n zh;L#FIf6CgSMEwlmK1(bn{x+w1P?fte|Mv(D1Ut*5eeC9_&&XbJ7&=I;`CnMLuURP zEB)Wh6Kq|7eBy*_l4J(!gm3#zJm|q~I?!50+el^hixgOKN9AS??(2_oZ}HTb>7V2C>AR~T;Ab*E4snrO zEhY=IzKQ{>%VWSVzq@A)G!nV_=vu(Fq-r4jaHksmZX*i?LTlPo4}lL35C9jJ6q&<7l_Og`}<9c<_Z( zd>2;X5rDLf%Os$Tw>YlP(z}j=C844HxIe=wyensYmJMg7_Jtuvz;1QuVQK+JJelAdCxX(ukk6m@X@Y~vova`1H~)^A zsf@Tb1VOTqVoO+vf8y6&A@p&I@32)VL(usidUiB)|2D#vi(gMi_!{Bl<-eLcy7AnR zb;ys*3GTltr?dZyG|l?#)noZc|KKmxVt51;dvy>-s|%5ova+!Xiy*-zJl0-h!Tm}g zSr|=P=`Sit;vL-Xx!NB1$t`J|)cD>}%wyCmT3H(>i8~uh3BqqIj_R-5C2r}Ip_T zU6}df8ZUapqpwFi;yiG4O2KGO*?1d@`Ts4cWiT75*@D zAzznEfyivtknv*~ z(V-P=>GIS^G*#bZc0%OdabSLwnrKtGmg;ds@UBe2H%O=afoSY@zSUaJH|~I)H1vmm zLw0Qe57&(QK7f+?;e>y5u_^65?2xFIvjf{Sy|&HM}S)kloNvyJupjq7adY9$nnfh6BD^3Lk*hF-y{S7I;UAhXzI53|CW zRO#cV8t%QDFj0l}Z7LBCi=kgy!a3JVnDc}lHxx|Vazw7Q#OvhC(mU-$G|CMaHXvvx z*<_1wO_>-YBiSosD90axrZ6m(dDN{FjwO*yjP&{ydPteQflc{K) zkQ`(}($vB=C0zEH{Dw(uw7vYwL&qh7F6_$rUiq$xmL@v7Z1UR2ryo^Q%kK}}Z{Y4# znG6EQbm4ZtaLt?&$+}eoV*a(n^aJoHnP>h7BY#euy-!24_iZ+!N67`M(Ott72>jAR z%fLb(o(lVbuP$)s%)7zjS9KlPcH4SOQPEt_{MdiWeC+S=Yd7r_BZupv$Fvy~tvUi` zSkc+*U->Le9)%k*&bB*eRqZOQ0iFnl1c!tzRHl2Cd{@dYF?+OZ!n>o2(tzD(U4~nm zrZ%#T8z^lAq!G}7Q=y*c_1UsOLiKm5uT}7k&$aN6H2mKL%K~iQKEmfRT*vA)kk4R3d6PZ0 z*-aFaKVgVVP#M7=*>lzRPRcXk7-o$K^Fz*FeF@}sV{gX?Qj_|37}7{$tV?I`AlPi5dMo z1CM21{fXEU`(uK?GyQC5u-PM?(=q5#74lVEEqu_Uf#q z=*LgXb~0_r7kFoKOSiK7bKm?Ty$;Bk6|IXPYfz8$eV4&dbKL-h{BO53YRdX=etktY zW+13nzphn9Tjf(`Pc2HrGz+EQ<{1U1FHg^T=(Pv?%?RqX)VzlrV0+VyU_{&lNDXRw z2GI5Wtw&0`n%of(#Kdm`D6MVQela80cS+%`@nJM zK7HiC0`6}h13{M2e8TS%mpEhIVYkM{v7({M)NMD=oEM0+o)0KmP4-ypuM?oN1H3k9 zW!?R^vevug|Mh|D&JHB7+jXm$zw5-6l}Kq&$E5VqBu4C>xNps7I}c_5YU_*~ZAWe2 zd6BO5ez{0%Xo*Aa%u~HrMWtf88Lr16?n^n-n&fuZXjOwKZt}-ooImAUg$k4*d}yux zU~0H`|GPyhe4&0$Kkz}gZp$`Xf#$pCWwxgoOqDi7NGlf;);T?YNMXaId;CWLN%kne zNWFQEX27)Go~M|Pb9TLX<=)9%DE1Wxq%+$}bcMZ`8hkvx2m{|D!=z`+=en3S>5c7^ z19)bGCM@HFOb@PC9%jOBGdp^fH=w1ePcC3-H6`&^5E8G{f!*6@OMC+i^*d9ud2Bs! z(h3`uKtuRF{xE@wTR(R%1wVBd6 zVYL9#a_}xZC_C>=#1Z0mSOpy0EeK1b#NZe3<|WrzXHN70WBop4taljd{f&9Lv~>je zovMM~=@!MK)gpvT&+f;XQ|TZ4;=P3uIS??6_wNL;)C!C=e+>^|3AXYoyHKL;Qe=CY zSp0&I0&Z3zCspAxkBGt@0@4aBBmUsvitWK-e8_YRLRtuYvhWhatl@(SKW32`IT|y6 zl1GK5|3pzN`i3F?FlG1YDh3NU{Hb!ZdzYWIkHYovCB~GM~a&;je zHn3{aVV%d*Q}K!A%cpo}v?@Q{Bt*Rd+cB#U# z*BA5L>yWfv*o_8TtMp&Q@;xdmK9cmH&Kue`_RBl7HB#zo>W)Jtu~r~XV6&#k1tlHh^%z1gn{3D2sNh;Cuk^kH(n>fV1Vs%J)%5nWw`2UG_Nwvag2CGhBA1JXE8Ub!^vgF1n^q*_6NF z?{l?OXZRUEGq|BnSqR@opfl{+lcqVxcvM6bePhn_r4_JBSHh*<$)9EZvTzNWu9&E~ zmrCeH=MhbL=2DHg*c$xKv%d#KnJEIuOq;LFO>XEa-nfOk9r61ZCBj>Gs2?eP;`qT9 z>EpP$v4(pjmD*57aW0dOfO+%u{ViMMqP^C01znt-9De*-)xGgd!kL!ATvMh6fB026 z5Sk=G`s>?+9ogG0sOMLp|9UCD0A1cgba}w^Gf{WzpuUp7j`kxTIwa}-vLdg%N1jHZ>kH<#)@Xq zZk6RBo7TnlzV25$NSDmzBIQ(dQ-1$|^Bsh)zoGoGsdKJ0!Ud3@)I{59<2vM1|+#6wUH(}m3R4Eh+A!r(vg zeBhJ#gXeE-MgR0S5otsVfJPK+3?YVlZGq!S^x(k&6aWEFd3Tc<<#`UKjsp0`i|{|3 zB6&#~_fyIc$lBl${OC&85JMlf`tXa!ux@3a>Cp7KbGF~Htj9fgk8HCVt;O#@?tFQ5 zLa9=^E>&`A5Jh`X(SbUCumv%R0Ft0)IY571m8;YuX~CPQEi82`w9e4(Pk)(!3Y3_&57_nj1brbR}sC-^MKX4C%Eu zPqi~-M9IXh|3-+~z;vBLRkqD-ha6r*01d#=`h40PT2voB^Cd7=K|A0FYR?!gy7s+NlgFGRtpNR#k=zn-YZkAJNu zaKER`rBIwaaRc;Bd6>mdD`5wj6651rXLpJr#zX24`3F%}h*JhqYA5($5cXCA$vscm z-zpH)+zd=r?V2$dW{fMR z2E6;K8Byl9^9;q4Boua3 zl+IzPk%;Qs$G!keW_0XR+X+uPBw3eCbOzGBcf1+QVK+0+a=bG+53-lF==@e8K2t03 zOeOMhj37XdINKGps7>hCbWaEi~0#$&T=eBX+4Nlq=7X`($QrlvkV#oG6boOcWRL-gfq z4#i=_KNOs|dgftaa79BkjLitHXEV9cudlCb-N7vcIiX4*ClqAUgFOgedkE<2dz>ohx_Xt}hpf^~pf1RQg?4l>gLI`gUw1z&pDf z0lf3)|M1Q?6Z|+KymOWx97C^5GA!}l82{LCf3}I>0vJzH$i=9N{@{Q+zwK0!+3RPG zPBsUoAJXd9dpXf<2o`+Xdi7G_Ykij_KkE-d=C>{9l2@Q~Ljq~$YIXd#f$$r}gnWo5 zva`)ci2?xBXbO>YtCk(_Adidh9sduY*Sd|bPHjaf@WFL4stDB=3ku|u@=F8i$AT*rP7D5!7h)Cn z6W=yTIPox=3^6cWDTVz#3m-DIWzd)~6r$XJkL?ulpf) z1_(l5V0+%8CH70B@6bx({jF|9QA=ss%`Nw`+ds)gq3>Q8`tHk?B`_um)T}bUEEr3N zid!-HxcafTEeDV1y0A{7Hnc9;X|HCmCXgkf1(Q6FV~ zN$e2F;#klo6Fp`N2q>d02vo&hK;v#Qe*#^^fctr?QV)rnzV4<&+IIoT)0vG)L5y0q za76~#GXj3A`|(P7*p2W3Eo8iTY!vMzhy8W&<8@&eTbXDyC!y-y8wVxi?yrNj+mw03 z%xzejRmiVb4S}6%KHZx2n)E-Dm|c>-aW@}cB2syy-X~wcBm%snke+h2`)~|g>sd|X zoz2bSZyd7}=U|?O&ElNg7{};?z}WhZ%f$#i*AxLo!+e%~P38(GxMB45Ny9r}J#wZbJmp zEuz5$Bp7z-i2`kGDTps5iB@?YmN@LjBt1+3PTypbQzoAsoOC0%FE>ON33i=<`U{mK_KnF9mJ2x8t- z6?_1LlrMKd18PoTU@!r@J`-{fuV;SoCNk^& z={v|Z3L}={!GjjK?ym~&pmHS1g9||bKk^~3r55kkud>g7e{VgJtrGagPq$$!!R=}J z5Zs>T?}}w37R`UH6;C7cJrT0fL+<%#oiP?K&+`qM)NGvCp%P$oriSFxTurHZG>(E2 zUK2`!3G=Lu_w2sV@ymt;IX2i1&v|Z~`^+HS=(6NO%*ZX=6RRbqnV9nnFz176~!SJC^4bz8dI5myND)%Ns)!vsdj`W_CUD^=t zVezHiarWTHP{Vd3I9Fd_{gdCEl{#)qnclaf;s`VL9y#wJXQ6%NIGk7mA4p`mZL>O} zX(pxd`HPWYoA;*Myt2Pe&UB}v8ft~AU$V}v4TEs5#6+^6#`E&qkz}SGyrOEKpzCPM z+lQcHgF(QQ*svEBLZ733e*={5sW|0VzSI9}!gLn+Fh7eZoPnkUM~_Laf4c6lp1Yup z+9VZM+o+Tj$IhxOvJcF(s-iqyxbb;<#dXLfEMf5d1;L?H$&P1%#CKI!zFJJIaz%J> zfEm>~@VHs$cdT!nPNo**fZCyVh5!wSeI7x$`NB=ab44R- zByRdb^j_x-CWS`zX^R-UZOie8MqG>CsB(`MVG#7&80r5$R*E#|5WA!blMv#GkU(}- zxFAn{+=c995ZB0791U7n-jyq0B-Txj=cwdCajw!eDN3_^dYLYTkV+&P>_=dpRfI-q z;Gc)_W4oH4%>FG>@!>L4u$ZuKD!t@jvGmlqhSG3kTK^F+j{~Y}k41hq_Ruj$$9}i7 zl_7(8-0!*Dco`I!2||gcMMRSq+kbRCAzAq$!5*~y44f{TWKL>|F$1tr^#i-!e6K@b zBuGn{tY3WT++Ps!cPHsa=^6K7>1Dg(T_QFF--?p2?NG_jX3`W4E778>op)Y>YzPUm zXPy_%k&S73 z)trzKbvs+t?`sg{$BO?3uwq~nc(B`{=)2gkP8}QgD45&EX}V7M#?CLyvDka+DZqGu z8XO*h1_!wp7_#ah?zOd>igx)_T*iC7KEr3fwqn}UL#};Ga;oCUs3jU6tGtm(&!i`; zaBI)JeA?A=?VsO5<@!b#T=i5htUmfWrbty6yh!690b=O}2 z#-bY^h>PJf@!lli~zL=HY z+GuG&tP9?7N!Te{u=%-rGC;iCtLeV9-j3v2_;eWBk$okNXK3D2OmfaBb+F)Lf0 zWbE4p7g`WxoW%9oqVGA_Gi&j*t@zr7J2MjFw{$$!tY94THI zWn6W3+O<&Z=l6rroc^PY`vz^G`!#6J5Bc>q05~_r6<1C9A)gUWpD8HBh=rQIaq4<)U+05WDGDC38DohvnJBxi`cp1gS13ujON3HsuO4|^ zqGe{3$q}{Tp=~M`wm><_e>w!sN(H}V{{$S^s`*I z7e)NdWlOxI)@QBOFn6GvXSYggF4G3!4S`~jYFv(f$$qtsbmrVuq}(WeN4BCM#;JPZ zM6LydEZQO%q0}-j=_eC*<@=O>O1syTS21J8{AKDSvc($V`eK(x3+OeGr35F^rOrnp zuyu#j7Dn>VHFHoi@ghdgX5|zWhkDSLfi4`l>Hq(cQ1BKW_x=ltlt4j|Kc7ECkx+no z3E9_3A6)Si!g&wVssR_N&y9P9apbV8q5KZK$XYdL?nKYr?InarKE=f{b_A=-&De8x zXqKeqhm}-a%eN_oOFdbb$lDC!HvGy4vQB;;LF6F@2!%)L7w6mXkVWmQHTJ^!d)5)u zGUOkWCVPsrwMk8Oj&-0)jAa;jM;mdyI_RA2Z}u@ET#~C)wKxh=)@udZcdXEu)k(Y8 z(d2<*ATQFYalr+ddM34D!%xGu8AH6$|8MjZ4MtDEC3Uh39DYT`5M!J`c}9-9+$kM4 ztO&u?+9(;I?gr9`;g5P0?g9Jh(e2yCUJ<60hIUw-8_!v13{O6a$(eEeygc2V&+TG< zn&H-`?yGZp4Y(l7o*Q^ZKF4SdZwg)Y-yrfZD}Klm?f9mU9X}V0E|maPaqaIh@jegj z^LM8)J3~lMJ*<~qPugW~q>cc}06PXBlCgm`XGLj!-ix(atgq0FC?(9&T0MN!y#2NX z=f{Sd6R1GNR(->qE%RVa4yjoVO`f=?^slUR0lXcy;usA_UH7Wy`W2pD@Setv9c$}+ z;!0<#rmydTE=piR3b^fUT9%P(H8qp5r16Fjx7`~1MD5C#gO@n7?U8R6Kh3`u4_#cH z1cn}adEHvRc6H@z(uc!VIA}(S*}s?l9VAD(b&H0NyzUHDoZ3GLqz~2Z{dU?RK*gaa zZv#Q&)O||!4TQsv2H_E)?vs8pGvd>iXzP5{3jW0f`-ut)wc2UdyDba4N;;<_S)!*6 z26W|9$tdi~_#U8ol2gwDMWtIeC9kue#mHSj=Mi9K0k6~%aZ~aG%*OxT(@Nrdnnp}C zLC{9S9(ajVqK+NmJX1R$qIrpuuDPGX@xHh38|wT$8%L{HPAjLL7d=e!0VB#Gyt2j8Hw3UCNk7vWWX<}czT)cusa&)Uv ziCM5J%$wnM^HWG=?u*w2xuT{oub(wVqdpuVbk46|(MN&{X$xl53i%r4col~}tkO(N zEM0cg1GoA4e*h9wptV{Q!TE0nCervi49S?HvqSOMu@AkDFgpo^zNaWY4Wqnjuq{!` zH#|(~V$Ikn!9=!Uy3F%bajQXCbbuLedU*;Do7PIH@DF(iT3`fXgZ83{g%0j2scfU6 z>syf+scZHnp3?OvAN1$wp-Nr25HLpuW9F8G^8#ne$>4f#_tRH1@QDnOr^3h$(PCsX zQ6_k{Bj4LZLto=gx|yw@x#S0*nug-#gFzeDBk%jf4sDq^i_e&4bEb9W4R#_0_rMmW za(y~G@Ojsr#(WOlAxlhH8hontws{IDCp7*R%(rBzm>v1C+s+OoOdcSL zU!lECS2z8;LR7T)cDJvWr59~H-Ya|zG2Gfy(#Bu8t*PGnnT~%_@aE8awIcHFO=ZC; zYW}QkA0eFS%KMWert5J1qv|{z6-F)f6>&A=2iYa}l#gg0K6Cklp!SHKJ#yzh(>2nC z7(??2KiSHkO#)g}#q99fZj};Enn#^7TpLpc{QPxY0ixgwGPvhq5PT?M{o! z-48?Rl1=Af9$ejsCEH-J=Hp^=6!5pv6n%KJBSNeoXF8mH*FPOxoSbC>UH$d?Eku_u zkYGNjuozfTHc@{nryYY#Js@LmvJK~Js^IG5;V`cA+Y@go$oSo%RZrctq9yf*#2%7I zw4iL0>5z>ca;zfCEOYwH9aFRqEp{@iIqJ!qOm>=~7|%CBmlw$-Ybs*@vJV~Q!`CTw z0zE9;$k%$z)Z1-;f6bE{-ak>Y$xqw5pQ)E8mk0qymA$!6*MXl0iSf0lv_jq5m+x5R z$^Rq~TyF1!_Pct=`%4R{LF{#N*1gzS*$+4_S`2=x5OK}+pp{Sp%K6>N6g{wtLY`PbYEukU zOe*LY-$IvAXU3ZOMW(!9-Eg&0mIbMo;NNcv$C8l>$4{!Z1%9r|nKyeZY*SrGqqrsA z`Of2ed+H^Mj(o!~&#=$SczrAa)6U$CJ@w}iv9qZc501V8#>17nQX>)-Me4b?jFIE* zZZmIqJW4Wthm`>QZH9FBdMy;N7b?}OZNZm~GV0gVJwZm;gtpMY`PZn%;94`MxQyY} zBD!A7Z@R#^c`qPB`EW57U#Z#TPX5z8OU33{+8VIa`S14|;cRO5PrkhOW?Tzo!Z!Mxq@j7N$y10#Uzv^H56ZXcg zQAU>wSOYuC5$y?uta7c*JZH(}AW(Cf*+Kg7B*tsGCG-r+48$8-EJuk7Nic5csEQ&T z6!kg=A`R!y-ywPc%Apta1+Mt(sHEvf7C71h+)1oRFxgzY{DX(IBJYyaM#nPMD0`CS zdKgJ6ht4Xni%{QGT-F}+PM|(XDF>MLFmga@oqhopQn(*ZXc5|0w=GHI0IdPZO1ej)tEGb_as8kG^ z;5&hzzhG(hKTDjl!|%5ymAkkg2dpAM)`T)MMUWx5#8_gE+R^E8!svnqwvBLyL85e+ zqGlMD9(79ToCFMmE#n;g<{bPwxAxv^VLup0ju6UoGE{p&=P^VoVfy>6iP(MvjLM`bX*H|xm>$gVY_d;sqVXMI+3W=5|+G*-wM6qVoJzGMSal`r8@ zto?yJU%MH}gqxBzIOCjh&IXNq7_HvkvB|&{*iL8Q5bp2lgMZB__bOs5jXaGUyrUJ% z0oe65o6p|jlMbdsmx7Q5FJL?aX>OW>mlvR1IFX4dnQ`_dX8p;lz1!?Deow_k-_gD| zA{6X^VY3O6&HL1-E%k-FNaXq~u^WxJ2| zk$LvF6Pk=#|2G+pr=X#sfEBr4+a_WQRJqdtrsUb_Trn%J7g5G8+sc6*5j!N(P`D`BBv;s z$SE3yW}`4cfD-R5`JZmD3hDNRC{kaY6uZx*5i2s-c$901{*e`FV6*Yg0|G@w=-a5m zNV~=ws~ikN&NYw)W0+;cdzkG$j+&N{+=|1jQ}Sf$boO!HD?2p`8P!N3V0QcqP)Xx})A0-=+mhpqjjWSlr3JmSJ4V#`LDk zReZmiB4fH`1_-bcjv*85Kf5U*YNR)nVC^=BFO6yf!~68Dlk0oxg7dLrSt#EHC}STw zb&)99|5}9Oi?4W6TvHa}Qz8yWsforv8xt$t8J5V4tw-moku-KpfoQA+3*&&$neq_f z@js=Ok!%iZPe~tn;EaBlmQwRUny8w4E^_Iy`WYefTMxG-i(i3lij8*OJx_uz8Ys!% zxV5pMGl7V0BrJEXK^G?@=;B02#IK z`S`0Efx@hubT=;KNZK$KGsvOGqcyIa>}dveATqqs<&f~@nS#sQ>HjQ$YVO2F7L34J zC)^Y;Fk^p0V*NhRlL~Nczi07PQAedqVLN$$4aEf~9PT3Rk;#GNob*+MorJ38((?fr zsSt&$LwI(}C#iK|;Vs``x+W~5SS97G--;GwC9^5gpUP(1uoOxL6nYe(D7yvsilx-i zd-p(ZzdD<+@T3vWG_5I`GM-JDN^b;#o)FM{q!!mSh~YaYr>-S?X%=h3l$+vrcQteM zjsi`VDBh3+x^W3t+Ixvbx7jEM{re3-_(l z%XEdh19$14L!AM@!tqQCOGaEg!4+eD7EjPT;$k+ z{V%}}q>%SC%$CxHe_pa2+fzcK#A7V(e~m5DqyelnCYm9e>k8fbL?Sar+Y}I)%XdpB z^%53^5l;~~djJW@SEu;qCrneRWRd+gwr`=Aid71(rnCM7AYxk?V`c5!kV6xGlXsAX8Fh00BhZavkt70#9s?Os5J+inTl_JBifyQ5baKxtE?P5_C1StDKFqI0F|i;Vz0^xB-`y(vR~D+K z_tI+Hsr~I@jthyGVH)pGl|BlzY&z;}$qK-E{UVQpg7u7PUkX9S|MVoTJab}``8`>s zl8p`g*zW4(xY)Zp9oI*p`b3*+gBou%R_h6>x|54mUmyp6o@0dd2?q-_k7u>$Ixv#dOz;? zJbPLRODyWS27>SWjVRR}6}Xu~_p)un$l3}njK^TQ!tFqS2LP{elQIHtuV$n&|lLLxrbGVVI* zxN%u=nXWa@OLOBS!q+n0*Qlmi)ALhMp8nvQYA*go6#7L%=Y}`6RhDjWsO*lp9_%eE z=(3SOnl7Iw-Cfu=paN1Ft-u?bw)~zq-soI>#56!9HzBf9nLQS@D@KUYNXpN2Ww_JSx;phvJxxk7NU_GQ0J@r z#UFZ{qaaidhE2-rOm-K!O~zd~w>IK?b+`i9FNNOxwn`rkt)Ee}=4`SpxTaMM=fh8k z+3qoe5BO15p)AzkquJ`W@9=TPlhnqS7G*LTiS!&WuSd(Ms>LylSb(_6KZAn-aO0!^ zH_ly|dW5e`-dyj)cex?=Gd|@CKfFQZe}`@pq`7-zP+_u*~Tok*J?@`){| z%R++uQj^V5&wKfdNrlWN^$SSQAa4}bX24GX{sJJ zEN^GbiXhHi;#rzVFEJ zcfsG@p(@ubvt)deP2Fp+T>Xb!qVzcE6=UX>u;-V4mkDQ_nuAP1Efe` z!uv`$RP8Pn0DD776T<#&A`a-cQriJ?9 z#H@{sVLK9to;(3t@`V$Qng+p&W=>dCXbk)0Tg6G3`$?v03fwj0aJE$oq4`6y2&x7A zKUE%_*LUgpQ5?oYvA4nAZni{RJEb8pDIS9|MCI9jq&kQkJ;IvtF}y4iP_uOvwv)t< zlOUKQP?O@Yex$KUA=89E$B~7$Yo&YV}V?~5yo6R)7l)75Y_5M_a?31OB(6n9x z$&YHbO$}Mk2pYn%I*B~n>wvYh;%FW?MWvCk5ug(@y2Djs?|F3b zHB7)oFD}{rUm;(wVLmCz=9VD#EVNT~b)llA08e3K^ZV8o5~vGeplOkdLym5Ln&gQe zcRoaRqD*J#s(=Df#?0>x|6w1hDXA}o9LF{vT0-P5D%Gp|d%)3L^7A^C6T|w2vCaGg zeY<;DuAZfh^5F8JQxt@3!oJ+o@Y}M6d<*B7^Khq9yr`3Sj;=iEPM6@=o$nUjtpiw0 zs11-gkZ8Tl2Ps@IUwRJaOZw;&&wti^Xueb_jTBhDn)SVG;FW-MX97lQoj$xzQUvxL zj6gNDc>D9$83sSQppRX&Hr$&o5%vagsJPT=t|wI%x6g?(4o;Tx)L!~je(bJd0EKU@ zfD`r6i-rkN4^0O12Z88&Y}UBZkJlN+yRz1}Q=kFp3>F0NfFuEY%o!aGH+4yt0xjZw zANRzth6i+%^_4xHRkV>=pIic|dm`P*YUq8M-z@7W-*jaKrC)NfE zQt8P8!lj4rkoGK+jPVPm`&;jS!r#znmbXmvY5ig!thqM|uI&Okns&`7!ns0Si0@?n z`_Vk0i(ZTF))n~9O-u#Rrva3G^|FJ?|F~m@Jva=B1+#PU%9kpb?zm~I$1lQF2X+2V zazoW47;P2b2z)Bp89Bs0m(N^jg1SyEg7zeLF4BpF+bv?ethGu%xZx4%1@S?B_Ub;T!;jD|OuAOtCaEsz9?CQsbONaas;w-sZ=83(fn4@knBl|s#W~wT`_Kz` zNQfeZ5dX|p#o zSqWH%<$o+9bMW*POP_guMlP{f-cf{6NBTyUtSg zFP`|unnSwMBs}j2O0Qv<<5q{4nB#g!K^8VnJ-iL%?{Acu5%@+%jb^`qPN1_IB8KB) zR^ClqXRd(W0)+(wxwGYIl(d2S)Xgl`CztQ!KoQ&`fIJ5b9i19A8m}1sn9jG>V`g)# zGfA53N~PQ>8Q}ZRhWt$cM#hgU@zA!JNImnkSVUK`=oyY)#^4HuS?D%Df%AoZr+5mS znA&mIL|>L?Z}-+IvuIyH=MFF5J0DzT`E2%W{BAe8sO88>8aC^tax5LHsc^kNM&Q6q z8ZG9=o-28sT!z~YSdiw_MIegfRn@x*>vHG1;L9%q6uzC*H^Qr$$QVu&8r3uIH^RTo z3dpYCiemj9KKGG=+LWFAi1bvtvvta`vw<h!Zy?R;kzqPrdeQBlnFFi2thSV zPG~R#0`A+t@BcH1#(^!r6^^&$yI){#i%q?$ZdDtr~qneO; zyW2o2d>kW#-f)vgg=)sbq#8M-`p0jk;6hyCtYX*eZIpxetGES$Mi(Ol#GEhLmBtYI zfB)0-iv<}Uga5%YK~b)&Mf~<1gr8qS(Mu@xiTmo-10Q)n^tNKLs@?zXJAA+L~1v6GS8!V!-(=601gX?H5#_{v78wc zf$9M}PVXvO8^}tJm)+l`{h1>w^j!!rXzIoN$Ve#AJ2tyf(Aw^X_IQN~luu)1dUBBP zEJ|iPy}s%722RGd8d2?PBY#bb+CVV1JwuKxFUxqhPVl0G@9o}$sK0}rV6s$j6mN1GOUTs8^ zS#{hQUZ7Bm$I@Cf!-nyMuUweX`gmMeQC02KqlAAhC*vO52HUSP&MSm(`bQRF=R(Sq zN9S{t0qrRQ^x1&2dWiP)2Pz2B@Xo}&MF{)vrf5D#6uTQlrb4h067K3-N98H-_nS7T zwaSNjR={wn^`w!uiyKVvMrt8n%BasEn5k0dAbArI75{hZ%RptVUIJG0N z>@q*R{}fms01@{*F&_eA;om#qSgCwmEF=E^mQzQf%Srs5{x>=7h5UqAKZ!dd89Msw z*tk?DN?xNG5fG%i!Q`sNhapjUlacG%M6sGt5){_)CgcBO>b&ErjQjs@OA(Hdka>=5 zl9lY0bB?`tRuMv?L`E`0ICl2ldkb0N$jBaAvsp?>`TJ{9Ug$E&Vay zcnQrPQ?j?d0tczWtHbK{?p=>nQx zKBlViefqJ(r8I?8RpXj=Uqg>@$!z2V{z8`SPN3i;@e8nqC)LS66|9j`4o=d1TrF(3 zEsQy`>$|W-K|6JiL9{x!{yoC)=ufT3X)$s{w;2EtgazO8%=)e}x)8yXI)4V}{*xt7 z{L4RpS;7S_bwU;q&}yv2_6}1eG8-iz*kiQ1hUh=NG}lR_Dv%@7i21FeUQrFkCl$lP zo}auXn{N-mo)fGK=-tArWJ1gDBOfT@d!pKOF8tNPE$2g^<$Q#}4IdYmwmA}wv1M@c z8`wL?(5LJSsvqK4Knsv=lbV*gM~TcN=qg8vBaQA+`r+s!UbGF`;2Zr6&pH#Dfk7hH zE@@i%JiiKp7`6ULU|^VZ>~!MqMH$&ZcmAuxS;BM&)*?IJR%;Z5!2cji-R(gK-qo8o z5F9TC9Fvogw{Wn`U|c}$9-iYTs|2=hMGG4zvO$Q`A%wK=djC~#ePi8&Dh3|}E7@`m z)5qJfuwuph0a`*)f*&s_?7>C}Kf!JHZizD6J&6l)hb7<_|6M2n$nc*^<^Qx!KxzL2 zr?j7edZ$3N^UX?kz0xOc;zYhfi4GcKEcbQTi>`%K&cMonuUd^LaDoG%#hci5(-Z$R zztH4s8BQd6ddXn#%}<}5cbAjw=oo3^Ff_qSq@+(cCS zvQ0e;672VST^^LNV!#D8%_wMMIp=_0%n=}~I2zkt&H7wuhUOil-j;}RBW&sEv}hoa zHb*$z6DTe&?*)-yy-EF3jRp1stXVrBiSKeW{b!&DWF#v%8A$-PT=dA_4ai6czfoAZ zUwVLijDH%R7duH?!YOd|0hY=51r!TL=kNe2*Yn=4U^(b$h5tcWPgwZ_BB*P{z|e{NG4&gbF=xYtR8;D-Qr4eP4>s? z*Eh2m|LQXsDfC+VN$D$|-gB7ZD`BV!@xLq%z^+^IhLSz}&f!6pm&D`_b3o7lU}XMN zJL!u&I!`D9${%U4=;b+U*MhP{%)kKZjvMmRHidktI&twc~H7HK$IZ}8q$ zUKvzUj%PF_nIDlA>$lO`!42);*sOqN-rfD0f3{P=BH9j2D$IpMhb|dnrk?{@Hj1-pf$Tmn zNXhQhVzL<9=_0xRcB+DC?-$$L=7L*V_Uy@-6m8(X@y#?4RHu{R_3P-0`Crr$&B`wt@Uvbge5(z4$>B6uHVVq>b7iN00`Ldo0peFMgGFPxscPdE zAK}~%_XV6E1?7q&NhUuf9`e0mLBEx?HA0WI7>XV zlFL~5zmilGh{ue&kYIiwoCuYqKXE5%7Ic#4pq%yI>$~b}|0cHieY-?yZVtpf1f%Nm z_vrLHv#;~u!Xocy#N^fYye~1ox@^udq`7D&gZ=Bu zf`dWvf~pi~n)OaqeduW%NQvJ?fE~Ps^x%mT*O$m6CCCK66Hwby@hF}-6xFoY{$rrV zMK21V?F>z_DpV=TV$yfHt@kxkTcXQ&%`8zU2nf|u6Q4X~LA=1*7iN@*F)fs4dRFS3 z=?h-b6{viw`d67ol1zXfNvy)H;C21wnMp`y4Xgud5l;wA@c$&s`-YWWBD$n@q`vAS zkLH*mG?C{Jc?m8FLlvTz(*z+mwOj)@JHrafs0@7XN-sCCp}{v!%ug1#wsT@zeB~|) ze-mcGcGw*LcdyX|KFDERg~1+&;;OT5W$>2r9IxW^PAib!iS&AGRQl*O{aTcMm6L{R z|IG|}jDG1-wq7 zOlYii7LgbSQycmB3gJRC)br9X)hAZQ7alB@^K70WnO^eQO?IJiso$8xXR!GylB>KE zV=tBH<;kXkj)CT>%l8n-?@75+ICdlU;Eg8(-o$I{z(W{cu@g*Yz+dhQfK@u?zM>@!6~29q+Zqie8Bpxq zdpk?q1|}(2<;n8hd9}5xlCOCq|MaD`puExyy2@ZA+$mY-C}pe{u$=1c)A}!62bteM zp7Y}^XO*%nFY7O>qz*vDElW(KH-9?b$P!FeSKv6B|W4r0mhW*QYtGEI!A#PQ5~>RP*GTl zx}e)Oz=ltKE9@<>(W@Bfx<+5Mlk}X1%k(DxOHuZnf)Z+5K+F}8<3JDz(B1=xMEd{Q zdpUhFS5@84``9@RhkY>vMYG9rLFLN~D^CoJ2wuR|(s(uR_Z4G!qw+O}(^NgbT{igq zAlq?q$P@M`lowxhxx|02#kFzH`{ZuB#zpmu|2E5FJgt{FpS`(Ns$7Jfj2-`S$^hBw zs0kRZV*lKOSEmHpnJy};ejX~YtBHxjbb2gYZmv|ecthuy`_87@ShUp@IN8T)naTKN^& z%Dqe`ye0kZA9z#BeU!hNffz*0u&TB05y${T>WW$GZ>ebf*(tn@^m58&V4T=Bb6J5~ z&x(||!Ec^WAjm#M$JQ8PJd)hlwYg3y5ZGE!sUOW=0tEI3;5B~nZ|phlVAb{lJsgUj zsHYF4pIa_te;s2N1P6641jS8sGa#WLvofvaaVK>n6MyKkuz2(3*5}vO%-o)l)*MW) zru{Q0lDtmc33LTo3NJGg-U8AE{G_4-*9DE#I|R21I6=(xSE&`P_92L8O_-!UAr~11 z{-dm4LC`D{HdFe4+GIHpD33HqE6}I)xSn#Qg!*r~QxAra56RZwKT&CgU`QY%G4QRU zA)>TF%$(BngK(B?zL-@ZVvLb-xy%3zqTEbqGExV(zGgJQ5N9pTb-5-t(|F1m(| z@Vt@oPzL{i_5$OcV2N^YqxWjwr$j3a?_d9w)a|#Z8o4KPD`-%cad*?Uy{LdU6roU( zuv^2^2@_F42Q;u8U%qyTG{UN6`-PACqRZu8vsxRzyU6!nYO@;w0fqn&Uq=dn-hbft$*0rywGO*9VKhKey$$ z^2qQ~ttms5-ToP0EEE;U;PgN3XFDXR!=w2WL_4189V-=VS(~W5OaIKuG{Ai9nwE2@ ziw*b2ocqA*wr(#n$)Kww*&Ny5jL`%Ph$tucrNF!OiSP#R{GGI-(&Z0h$AkY?@12lu zhJdYVW{`WEj?WwZ?vfB$p#b|09>-Xwh)@uoD+KY1OaW-*2FR>22Eb1j3de$t<6hW8 zeL;Py}*J+lXX<5A!@j`w=` z&xRE8KfkerFj4G1OZ_5?-yAg zz&HFx((rx>7QwqqxGp5RQS4K4qc3MZc*t*JIR$>=L^!a~rs|Isgk_ZTXQPny-(+W~ zCA1Tj!yeA;rw-Um<<~&_4oALw;byw|rvA~4?IMZ4AuL9YZ-e~xxlo=Ig53yqM5E2G zv%llcG<%>&&s)36j<9hYTrf`dE)@TV8C}M)o3F{IUMFQE;;ygi*?qx{RITAH<|i`# zW$b$8LN2dn0GmGD@O4z4rb~%2UFO@a8(x4ha8;QsA_yZtA)|!%JklLHME?^C(jBKh z<&Ve$3P?&$K5ℜ8aU56M$-o+9=0N z|1fk^xEs+?$R2WwxKALT!g)1Sk!OkI6sJW z8&1?T{4mTQE<29JM+54+HGh9_#kq;yI3djk`&CMyNs7&Uue6`^Pm~l1L`iX>d1jpx zk%AZVyqhb3b}I#@EmGF|Owv6tl7Fg$aIy?BJoh#(>_Jbf5r+*aM3txRNSHy+4UJ)HOV zM*Pt;SV=5H{LYl`cS8BZ%-_9C$X z?p4Vu<@V%?cT_}pF%mx@x4SHxycyj^a;hJ{oC7vx*bo-?k`c8!+}W(oF5^*lTdqYI z4jrxChqf!a{!R%Bv#-9`&PBi1S@&p+9qbn!=G~^W5dbg=XUN z!->X9ylzx*&fa*c4p%rZGJ0Wt{5YMzSg!OosWO|=cX!T%L8_0CA&wobs_fp(75p?7 ze!qQEZn^wP{~OaKEezOPU^oIsV_J=rx<1chF1yQGDB74g<^?a}F*W-TV^^KsH1AUi zi{yWz;sofsqbh1qqD3lB2{u(@M|${{D@yPU#p^&jS~#h9E)A zF*p#;KcnDCt$SYa?)4rA($dvZ{$9|bnV0uAqpd{zD?T?;DF;<>WuSgC_T4`w+p%d=+un}FNQUzj6pP<x zcmlBHfb=nLvDC4x##?A-t{Aa%B!Ea9T;DXS1z#uQt-*-D-%6-;mRcR>UFh5MRGqi0 z-;w9%JHV?G=?=WHcI8)(*Izs@6~E*!-PI!}-?&OdmFOBAQZPb_BgGu>gezyXCf;D+?PLO zq6P17?^!w%actN2yPVp+VSZ@>=`o`wS~N;EiQ=^@Mow-xKstl}ql7j=l+a{VmR`{@ z{}YKNE~p2&2K69bc{}fWRg+5xU>};sx>ZX%*c6DfgxUNkqGw1BK6qM*dm?3rUt7gE z)z!r2P52N^Q4S z>em?6L*-femYZzG?K>yr3e2oV92|403RB`=w;V;FKK;ywwzw2vi`xRWxRoJE^8+7# zOB_|%iZ(4SBP2S|G04ICL^b{M1aVCas70X!B;P|Me%W9?IVo*yp1G*$Y03sn1Ubcf zX^Udi)*1OJin!F-F%}!KKB&0~+!;isblR!5$)Jn`2j5ty>gYnxY%)SEx*9kIhs}BZ ze*c#pHv_WcLa8wT_TOy`+GhCiCtTYM_7PxAu13J15b#F6^MUt}nyvwNj?Jkd^X9(E zm%EX0-Ro+1DqzYn-zijKRH1K1#wXcnD)kTs(tN=_hjAqE=aXu>!N&Ke47?_j`8`S* ze#_nvz5ynX3`ooJi0!AQfW9yJn4jgPX(y=~gPEKvxhJF2DrH?ASd&j#0TKY%k|*;i zaFTt>dzUe8Bp4li8*PU!ETX+*TrcBaSxcnyIL?$|JU(K3uNu1PKCkLuY zEVFeIx0k@bTfs`v)x>0VorO@cBr?I&FUX^kZAAs0a%6+}`qA&b_ao&ZkE+5!AcyO~ zlw~gWKL>j+nuKj?fj#-9H3 z{ovW{Rwdq&l8WU>zn!_YdXOEfPeWAxY_=OOdKD3-{oY#5pr0|@#|x=VA$-_^Ai75? zge*?2xTw13t>+q?^fw*HskaoF!X%N0eCpj23x0K=|7y1j-SvZP{qa^`m8h(I5;z)N zI}oLpn4GX?Wr%^uR1;IVn@<}Wh)RFm_*!y{GjRAQQIg9gmirZ5ANnCAkHHwu!B(s) z6||jVjQ0Gq1!95WnkoO!(ug0xEc`&|eeo6RSTp=GwzSlpF^cCyn4CtLtX3ZHsG)I| z=Oo4m)poCQq40NE9_{=)s=9LhUrwuj<*W~A^&|z^S@;zxi;wyZzQp`_DnG}kv)RWqPL zpnM4g9t~@|xYM)>g4K*AvZY$f9#m!LHQg6bV5_d+t0F0|s7~Z}WoMTglVaU#1^ z0>5~BymY30a#`vj|mZq`WPhH0}S3}X5 z7Yu5io_*saNNDVel@S`(D0$OPQ`R#cbZv`P{Elpq0QIFJn_$sVo{`Lu)p^S1`mZM4 z8Titd0&V?sALk)fCPq^5QYWoh6B56G7*qgD<`3)aWx{knRfu2I=_Hzr^Gg zs9E}f@vc<5fJvyDBVBg#LNewa1{k_k3Tj&JA!?8|-K-c|{9e*M*o>p+cjM9&M<3Zf z&-Nbjx`iaN1+PxejvhpvB@a^h9%2cgE|+{WA-+flBxksI#*36FSIw@7Ro(e5>x7)L zJXw5h(GkESvq+iDyfn!uY{D{Qj9#=6s6Js3e^iv0`?DNTcK~O^e|(!&c#o=ly5a&| zXLx(vzsFjJNaQAyv1WdG@18*QckGy%#^1Arc`Bf2>ad#w7_sE}Et9pSg>H>p_QMe2 z=1$`@sU*GlKYSFzbSw|CU@?U!{4GQFPZgdE(K1WFaXS0eMPe%(JZ6(!g(8SJ+HIQ8=IJAX^cjdM`qgSI|||EeeU zXU@bY(X0&Rq*3b#vSIY&@TqWx4C_fLuY_Gz;UwloDCC!AIt&e{Zps@J690^&ge>ZvxkJsQ^a$({@1_9+KQNxt&zIbY&MY4*wawR=X$U)e|OO0WB6ew zs-O=jnBuoL@A~E3R=H{JT!hh5PSBnnN5xj$Kglb?J=WLU+)p$!>GVGj6l^dLq`0w8 z!6`nz0RG&IC6#2s`#Mzc{3{oUUWF_r$wH`-j0LPXARfg5L{#j8SKJwD>@Z+ z{-t`Q!Ml0_>h*vP3~;#wXjSul;m;WKUl~nkVPpv{jGl)-H20-{La+aUM`(unZ(Bm% z^@lY^oL?1J{$3Osjao3)4OXv`T3Yo8zZhcOe{a1~@zAIR-YFT?9Y%N;Kla^6#Dhr! zgU2COSN%%=m91c{X||s;Zy&D_K)oe0M<0GPirBDD{dFb4_{yBesU`EVUmNrZGxOf@`AusCZ+6O_e-$Wx^Ws`y zYu_f&H8smv;D~q4tl$86jum|dj%5#pwotQwd(DVW$-K?`qj$?=ilQ6r>JWW8FHXnp zRg#Sft|#;H>5?qwyuEJMHC&fBO$6o9&o2Q*d0cU(X zdD$?Xp@a=szZsgPoAOC(mJ8Q+EhFL%DRZH4Ye-D5VzzJe|EuZi?giB{1j87!Tfd@; zg2PH;${m!w&)D|mM(=)Y3e-mEo#ryON=j4q<*QsTsCOzl)U0@naQtXWlT8(!{z%d= zUBo!wLf~?&nau3p>3`dIKCqgNKrKCvt2$FHs)+xgKJbUWO!*P#P+oi5;ZHPcC+hO9 z6B#XkG=LC?X2V3w{hc>@4AZA)=U}}8LB@H9BaJ^2y?z(GVpLYBIZOn-Z2m|qqQWy# zQR1;<%YNd-h+oaV;fn0jf3e;yP^@>OH`Q^a+)z44>ZXkn@FSHFCC^Qo(nYD|{QPf9 z-O*nlU3Q>Rr{L-e+xfaCSOpW#SKW-_co{vE{(|i?-O-lLy!9S!|Ltm6XNha8Y*vq7 z&1$mdz3S1c#>i;RlABrIIgMRAMyX{Y$~6V(JzfR#(39|`fGXG=+^Q_SrtZgwE1w|L z?MKM_aZjMraR9I84Wpsou-2KQz^LeBPm(9&>s4z)Q?Nk&w;qlpLTzSnz^ zgSW?EHi!;Ji9OGRR<`W-0#&wrW3609V&7^kbri= z1J!;xw+3w`p;8osnorhk1ry$@+Akv<>g`J!(fLyE`)#ieIhNjx2tMC`hOdwkg*o5%g<7)zDD1sT(XOVY+cybkDv@Z8`V*bk@sW=A?mkAe>R5T&Cx}hf z?mzzK_CNrs2m2+8_2crRT3m*mse?Ojsj>s?1IRfigKkq1%i!YfXHqb6@WAKqI0M&$|BPw;EHg~v&cb9|~| zAFQ-9h`E<;Z!SI0VN$qx+qo!q1Y{STDJlhvD#ScYgRjD$5#m+(oP1k4ue3=ONk{tJ zCad!aP=$%q8uT#8T===1mrDEcha;chAlc|93T7X3Cb+M7#K1i6U)CU{g8Dpped?L* zW)9V}L1fLl(c=8BBObZ}eTMsBa{0+qvv;}V2e^eN) zO*;g%;j6o3A&|DQ{UtFSgA=M~5mU@+ zS;yICE#;+U!{!eejP-~~Ll4TJaKqbln; zhF(y@=pG9{K`fh8}1LPzTRN91B~3Apr8s5NCK7 z0ETzgI%V-*zhs5&97};&F#)paFC_^GHQwQZ=kHOvH{yI*kqH9J#|h7d620z=yv^|j ztRQ3#t7cJz?9^iEv%FGgq|Hl#`kX_h0LY&03W0086C)GXR7}3b9`HR3TygN!SbbWJ zNP2sOS4^wQ(XsE>utxF6v=;F}iOCadT}R}@OdsE`U5P&oD-!|h!hjufsV?Y|Ps_gHp6pXaV9*vw5#No7MMyX$$qo&1 zWRlq^B|hh>$#c9a{QihJ8p@u{0NJy620z)8ga-IOo}e4SBAEF-R-L0+ zYC^i#P2RIUh&g2JzFik^pEu-A+|d(?K51^YwZopN78aqk1x{FUOy8(5bE2W^hkt|i zk{&nI)5;^aZU=|9Y_y9oLuf*Htz%xv$dCfg7`K{*drvP;RK~L9H=G*@^Y3(-!6tXZ z?s$?AVcp4B12$er+41u5%X^>LrDd4gsZUm95wfIC*UoIvHs#44mpv`bBwovH{jei$ zKSkz$^l&Svm1C`0}Z9&`&u?JIHplFJ_|c+gpMOj$09 z#HEzue8aH4_^siN?WMe(zH~&7vu`Sx9H>0$T}j?n>32x_lv6nY}6GQz+Aa^gXUqpaT0bTyXV`k4GQK^OU2=)unuVTcb?WICh$`_0_Dd8SY6SP zdxRq?e!Nyv!pzi4`GbUsN9|=j9JL2F3Fn?H{1%9|UwtUcX4DMdfLP%8m&rJa5j#Yy zjEOg&33h4d`v}<~5Ycx2v5~}u;aisp?VGW zKRI;lY}SsD{7Ulb9C%=mc3a@hcqS>A0-I9VaMtBlM4QQegt0*kFSpA}C1Tf{la+mH znz&Tk+T&lY_1fg#w1UO@MYo1tr!LKBg_|EXO4aNBmQCo=zX%MlAjCTds7wIXvN`Qh zG!4iE1Hvz0GA_;wev?8(h9aNVK$x=@Uoif}$y1q(s*_!7e>Htaz};M*pg1Jq`q;P= z?U6KMW0IV}hH^VRT|qMP0H%gn`54o+sGYcE!?m>ZDN~D}W2Rm$cCv0-{_=5Y2 zX$y40rWPpSmEoKm$HF4}wP9*!3OEEk19V1O1~nGFroED_hlEnBz5AAT_k zKJHw1sbjTIkxcRg46tLi8w6%0r1#{ssp=oUx<*Y#GW`I-82ekQ+HVEKr)+z|e&y0X z;PehN+Iay!v!I>_1Pm4SJ&Jf=&oZdudB%{(o5s*x4i6Cf0D|G7e)w;f!6J zt+hcDL^z8jsRszlI1f{mnjc)?Pkd zWw>O9wJFu>$82Q@9#O`^$bir|B5IhQ_H6GH?L|Fqaw7l$6o$BX&lz}*O*|0izi}mG z0B#PI&=e?M7&{?2v`$HJ{v$zOsk0;bGsnp4+7$~jL#OE_zUk}ylWSX#?vhF1bEG|L zc71=l9cE=SmS`R_dO#+Tktnksr$nvblJ9xehf~HXfXK4_9R7x)B;IE!J;Ye`-Jc1{ zBk!LWTv?^pdEQ~q^Sd$&}>PhKcfmY?%$wm)S;*UZ(G zpNKvf^%xj4#=LXgtPt|O&mWGy?VozB2fYs9bS^t>Q7iD+93MANqLo&c}NS zwn+JRPRL3ASOMB-z|Dt;8K&3;hMZ1rDg@brPP_@mC4O*1Z7zpOfOhbPg^8Dz3eA(< ztwHGQ-^w6}yl)+eNc!!Q2GN#qw|OLj`d}aV`=Uo^>uIY;1_0_XNIo6{;JGiV!!o?RB zFA(&QDUUQJ>PLr|-E*qJ?2nm_p^pq@;}V0&j?|<{iOp^SvVRM-abH@0vZ4HE88QCZ zSFD}L)f05(O#g+}aSZe(sNYTNb$^na!e*tJqk#IOWwNU z!!{nU$zTaPjr0(!J00SP$atNkCWjwoXhCy9>V5SK$6LxX}?LHjZn63;P`MrXB^2I6ZbNay@gGwJ zZ3lrzL%lMuuBoYw6<_mDpKKWEqeas&{`k5;)K<+DXY*4-*%v6gmxo!gst)}Zd1Fgx zGRmMF8W|9o0?ZrTKoFm{y;<0-6@SwhcsI1Z@KdX-+PM+X?&&~r5fGntvsR1rBgyFx z<9b)!>#D}{!Bz0=X2#MZJnqYH6gr4X^hCz9{KRLqSc8=Cx;o=$-b73Gx_oo>cvX>i zqB!*->Mhag%Xp}s=Oa!>S@Q<7W)6j&*fg>VMG^$#JYN_-;7@+QDA;*LKNGu^E__3bB)06X ze674pNHTweRPYU~cVQdIMoF;Ix109!O3pks8B=aDU$JhEAr-$D&a3iv%6bh z1)5Hz{Se*flg2rlrypeu`V!7A?*DynY;%ESGX5%TY3qjc^EA9wj-XTv_kY9)%T(jLjh8VPq6U^;|{EeTLdSya(ZKVYDPzh}; z&$Ql&@=^Hxx*2(p4#umqeS9r`Q4o(Anno-t3w%qL{o{rA(D)&I2U!iuWaWzr)p%c38qZuFhARjkY2nqKL|*nrgDR7lw>krFLa(pw-QIg! zb*N=Zytq`TpZtIE-VJNkb%-Q#Z`_$$19eDs2pETUX59Yd1IZe8@k9LN4$BV}lIX#$ z+!{80BkzoOCwrvl+sTC&qGCC>dokLR-f`aj8dY~m(XE;{4z_6c3OUtW6Y!+Sgvo>* zz@3nBB~P{PVBA)FTFz?a`4tmPWGdMMk}0{8eqT9bP)?!qte%+z1X@ISPwBO?$0nV` z#~x*10lHWH+RJ+;9xHu+(qNsRUyf@!@AFwtY^*Hk)GJl=u1LCSB`tE#KXVBJ zxyi;N6pKcj=--CW1j#})Z!re39*5`(;GQ@MD{)D~xr|pMJ9cJkM=neVQNJjNA&sL|#H9DuS^L`xHz`pkun96b_+0Kl(=js(<*bw6}lB(0N@08XbZF%Dxe}&ACb#@4CN#hp^X0*lf;@4RO+-5UK$Brv#`4gD2KR~#YjTRP&Tdw+I*Y;Kxp>ngbUC!UyT(bl%ai}kRxW`R+sBN=TOB?wCo zEr$K_`{C5B{XbP*z zAlzAtevx-({S1V9;UUJCDYV>&4}R6obHS3I*>uM}=gsJu(Jz&UvH!RpC)leGOk{-e z*hSoV`t=1CWi#1Nwm__nkx8i6{1iq-E6cSM&`*>w7oWSRB59$|ArmwaYxYBD;UaVD z&Dyyw^1L9iaw+X5_33A4LD-9rXBRdmPzr^*$tddp3>h#R#YYycSt|n&=m}0C-}KM4 zsr>0UL4LBybkeB=CVp|9=z+3Jq1BDXaTP@Mqc7b0@--Q|JdZqQrLiJen!)*nxEJ&*k52rA z_AtuN`~k|trv~N{oUk!Zl5PjA+9z*UQ2*Mfod3v;$0(QdtzYJye!mo5XeW(u#k+Di zoZ+jCCvkIJeS?&|UvalfuSk9UtTNIH2M&=nV+|?S={1m~*^dXp&-;IMOtO2|=hVj3O_qWIL zbCoUX=Nzf6`V;b$8^NB4?)&3^IiL^rKn^H}L`%lW^%fmb+aBO1Ru&~=x)C5rQtv6& zs|(WFp%~D@5!q|<2Yc@6EWlrySMpot2VB!eo3w}Ps<&I$;JqJ323Hat`HTFdv_4yP zGET;3OSHL1E*d*~4y>it`O%I@tIbBfZZj!`#_#P~)xz()08Q=aE*T$yU37<`1dV3< zpo{(mbkT=_;r6Y-ZPrs-!mkE8;q~Xa7$OU&^ew*DyoEobAFbu>_i#?{4SF6f1`%mP&IUu=RIcQfpHFPS_qbjOx)iYFV z4L`_AIq?jgbo)XGXw-j9Ylyd%0E>kw2yt+D@P3J0W#7X(+@nI<_Zw}>^Y&^u*7FkL z@rC!mQkZmR+)SGoT3cH;paQ%@J1)J}o|V3%oYZ96UpntCQ3JT?@kT#Nl9g80NS5%g_~i|M zCnz9FujIdBn{~m3fO{{C&8~WqXAIVV_*$f{N%i+~#M~12##bKk1{c$k2&Yu}GyJPY z!Q_1o^AQUoyx7r@UjxL%QZ=77yrPGHowgz~x~p@w7nP?Wu|@YV94#mGkg;Ana~;rW zIvulHu)$W9EGfWRP)}5BLms(xZ^p9GE_`MmR+14FujUp~gY;F?fA}HWs2X2h$Ph#f08S1lT&teX_r={sQP8)oSGtIMZ`3!PT$>i^10Y@k{xQFsETH@)%V_LFpe>b$Ji3#NOD?$7R% zKgaFd>mW_7m3^gC@hcslC#UIl;UuoJL_SmXgACtK?XY_m<$Wvsm!0eouCWPhir0%3 zR2DWXU~?BtdGrLXEyx?dP^p1>QT!ubr4F5q(Kon9B; zSUa(TE+QlMG2#Ojmn;88C>NLBpnhX7eeK3A2&wq@&^)fK);^QE=mR$)kBV`+F#q!_ z-EOq?g%2)Gr+)17B-9S^gDP&?2c+S zMlrBC*nFw~P5Qt!ry3Y4VQZvhuV|qSPxy*DnezkWTrR$L0QEzA^2;VQM@mN}J>RDL zmZ|PMm7Dk@iZy%Ru?nT32?J-yGs}4QuUvx6-+cA1r7Wo=!1sEK)Mx9P_eqN|t8Yz3 zh_?yBC%;135ZeFDd4sxa&>m2}-D^Zl7nlcu+(cjA=xHeVq;K=anDLm?(0WeegdRB&=`@hW?R;ykC$|xgXg`AAG)#4oe zK@DYk2yAri9M>wM!+PL(npfVZz}qrEj3z>?=fh)2M{$|c(9G!T+RHu=*ZEVbXC+OB`z%V7~@v{J*N$k z#^#YORDIvfd)L`?=(6P(^a*{DyWG%duKlWE^ygE(bVwl54)yn0_G1F$HLTLQkE)-3 zMlg9SEwe0d1hICaU+*{y2$)=(24(ks0qjYB=+;x=za(=o%@;0PGv7U0;n}kc58Ez? zlzm-Un?n0rJgC-6>LIDPEaMnWK1=U~6qWvITRhMG>3nwnGVmz0+P!IALE~oK`A~* z|CX7H826h2gH9~cJ- zQx+x2NvoeInQAB*`yapG^Q(fkN{YlEIGJ1_Sl9yZH*ZC$cW+MjA32dd{O)z=$G=B1 z40AGv=kH&MfBv1nxLz)?7vTLTuN_gjMw}Sj)j=F|XM%xg&4|WF;MD{JQ>LPSt-X%& zO$+BN%m`6JSR+Y53IClZq1Oq*W1*=DFp-}Aw9lj}sxrWM5IReyE(?BL$x{^Od92-b zy`!%QBWm~FPyZ9~^BZ1Y)6K){v{fftWitxCQG%(Hs=YR1o|z(SYIxjRWbDS&z3R#0 z_ko)KE`IQ`f{XtFwp$H-qIJH3TfBEbi+AOunW>^IKD&|8o-2ES?<6n|^?ANV)q;~@ z@xcAQpsgV+s72xRiq#IiWfOKm^;IuNVr9+Nbc!TnJpD@EBx|l^W_a%GV^2vAo}Y%F zd*x1WPHhK(EdaA<^47^oAdGU5k?dWPfZb``n&#bavk$r@<9}g0${EXaSkLmRR=RF9 zu@z}%^EFk)mrEU_E9c2wZ}VzXUnQBt@KQBw@JMidZTO$Cp}$w=Yr(}!Qwa@3P*HV& zSZ5u&HcIxJUAm&dIJJ0Kf;C4SmDBG)|YTC6psCEAKv6ZLW5Gyx@(Xq z{|9AGHA;S$3GGl?pvg9odKkBAOy+EDjbZd%ij((prPXYc>eJrONU6=`U$H9h-f*y5 zH}gfGjIiQtuzXkVVI$s%`rafma&E-%OE>WS3ZRiFIbq~{JMem4Pi`?Jq1F2CwzXs_ zCtxO77ZJ5#`-g(`xc59Bkh$)lR;U@Dw>Kywogc@rXjM1>b!6pfwMVwV=s;)L82dtY z=y(1iSn&7FF|+(X^MpVmR%F)#I1QT38E0CV8 z=F;zD8`YW<{k zSg2I-w|z083SR`6K65Xed+h{iwosagSS%VTps;-~_IeR(5c-LPR6TGsKs6Pp?zdQbjlkRu$5pOei^fSz zLF{Ck&wfJkiy{#n3thd*3Nm!7cSE8}4m!`G6Xm6(d^yz7_2tKnsw#N(opcfQ|=}Czoadk`}%J zo2t-)*|EC5qKrx9iJG{Y*)x=bPtE@Qao4l=^QUS0tZ8<0Z?loZ%^7}gQkA90p%zNN z+qG_}q&Lxk=W1sW)ELu?Tb=)%4JCB=xkVN8QdosashKnbo-L&ZUw5uiy*z+w^^HCF zI+&?)~zOn+=ST$M;ApY7!~UxBi?^CM29naN-i#c0Y- zh0V}X;y>+QUKjOHl{{EGN!5@2#j9h3d^m4e=)stts$Ks@>e*fp z#OYu&j8LrXJZk&5(C*5U z(HL)~#Q82g4wFewAB#A`g+4uz3;?;i^cJ+Fh=5d&y>F(T9xY?OUZ|43EM7Uq+B2+o z3}163eu*O@KjrYJ41!v9YGnJ?OCpxIKp@L`$-m;*f6{AGbtnC{kgMDdv?8&mzXY_& z(B)JGDk;iUD8Mqb8syFy?0?5qs2~>I`=}?r${rPCP{CGWnTz6vQ&h7?1qhGxmB0%9 z)EU*HObuQ8?kwAtA8E$VNm;5)3fUp4F!J>ucRUfIAm|K!WP2e&li+~YW%^876Bpn%F1Sj_I}{^Tv#F zO+K!l*#4QIP|`pRDAUFFxMBHZR_ z4%Gh2AOv|F7lM57%l}OI>8tx0Ggbf;YHah_Vvy=EE+<45N4&wOo{{rEq{g3<4zv)! zUi~Kfbx9x1#^|Ux>7-BU5y|(!`cf6qBa#2d)t`q$`Ty_#xGihKSdyir8T+2Kl3f_P zG4`DjvPH6GPb0)Iwq#$&k|m1lltyJATOm@}A|z4xJ+A5bdVjvh@4xx0j>GY|uIq6> z@8@|t1>Q{C>V_-gcR$23ZkLSyo2Br5lvGMKYUMYJBMSeEMe zKTjw`vAyPnxwT6nQ?;-ZGN$oa&SvxxgQroC8o?t})4mYVN{r{=&lv7gK1YKbj;^$J zjlM_r`I3bxW0HxRLy>jGj(IIypVPL)`ddd(P~$&9{em8VgS5dJKO3aU7S=`@pT>g@ zoKnxKK#Rm>HQ@hN(TVf**=cYyY@>SaJV(Vf}D*a!4 zQPANB?Ox#1G>-0pxfmnx9nH-)bV#p%+oz{rum1a!eqyxz-#AsF;7DNXg6UQ+@|1{_ zc6?BQ)|hQkHdB{Ff1QasK>a}HFFR6M!S;L|@Yv*0%nE6jRd1agu>R`(OWY z8gw(Qq^j`ID0OdewUxt>Mg6*){rV4PC!s>~0bw_md)+|7>;q z-PD&v1nw%}{dYXwWy>X|CgGQ)OrpsLOb$~8Z|B_-sUKi_eq=g&W$-kPYT=cKfH~E$ zsoAJ#5XaTD^ep2SzbtzAM6Y3PK@#csx500*0N*|KFwVCf56xj2F9kD|BX!s{~Mdme_3*` zs%m+WSmCM(S(V$WF~US!ER1j%df$F4ZGdTgFm7{RlQ}6X z_kD)3qRC+|i`pvt+0Tl=BBqwfT)NkLe(E}RgG=9A;LLd&3vzO#!zrF!@!#Q01W9CY zI1fn+`~v7JFM|`(H9{baPZ+gMC^ztc{8eV%np%SiHBQc=vOvJZ$qoNbV*T2ad ziwp!-l)%!}s^(=JUE5FLN}M{|jMskkd!NAlT_g4QMUlQsBCp?sv{=P|g`1bI{V9O? z&A&7FsH)a?l>)~zKk1XDhmM!WV|(>MdHX8Iu9a$8-iAnO&365z$_l2nFbiG!A7y!6 zHR5Uitwp|yV#u!5L`3#^{7!lejrgPC8H z1b$c||M+F@dz@79WqWEyE2e@b4N1=?qOdKdvY>R*rfe~#-<}ATqHlq#UP8Y`$`x|e zt3yF{sfqu4gF1pYsKW&8)g39U{NA~{L~B?QS%QvT!-MY$&9Nq`QZE|Ra=*N2)|3*L zu{Dp0y7LSXwfJ>($R>7dP$<{f6OD*Fkgu2*`PVf^n+67-9wlG1UaHCvH2b`67V4~5 zca69JruyMF4Q<-zKh~@Qz}9$+VDk*IE1L2cj+!9Ps}-;0+vw*j=n0Yjy*xVu(;8sJ zbw}M47GJ?#X2}16Xdw?=w=rjG&WTSuO#k{QPC@Ti#Kj;D!RdI3>{C8Juq3x=r@L=^ zH*&Basc9Q)S-Ps4P$CU!q>2L2R6m8yJ9HLarZUfYJWpu@6J}W)o;O@IQKF@~$u7k; zJM!$$7q8}$a{(H^Gy&c6FK1u<)bh!O*p^Y&D(<%@dv44p*ZOA8CK=YSo-K!lq!-YT zMEdu80e{I^Y+r$7saFL;{^1XJo7D?sV3r|Ipk4U;wBfI1QJi0OO{$)9B2%>1hPZ{! zm-oJgO<|>`tAwxXT-aSx#USoro7>VtVyaYij{_m>S3}8l67%`pGmrCE+zgS>M5pV<*xXjRI#B1+Mm| zUG$boZ_uYYH}L*An|&5a|WZq-%5*H1q!WAdV z3reK(s<60V(aMh$^(aKnYU$@Jfm?+hj)s`jbz?!zQp>m zKCbNUumjMc-gy8Di9wSTv4>e8ZI9`K)R`zk`~rx9IBpC68|)#EvtX32|0TYlAdYp( z7pV>>1uAddq~dpk4~bH&nb^G)Y{Df}W4XLPDUgw|>4MZz7-)TWp7z1Rh)8jz zwkm`b!7XthOF?26c(Nz2SCe`k*j-Zf-^K?*=IpGz^t|$gh^+GbI!Q*;=%-KOFFffGM4noQM{pq zDdZdl$=v*ajEHXHz^8s*{o>dy{#W13k1YGct@ShHL$6s|JVSJzzdl7F*PCz^QMM)TwjT879e(A zM9-_Y%q;6Ye6paJu9+Ljdy4Hj*ZHH(fPei8TN!N?dn`^>{+GKMFTR?K%TWA`&a7MZ z2`IsI>;Xz>P}2ke9C=nFd>S&~Yg&I7oW9v0!x;J}LMqap_=r~yHu57^Webvjmp1*| z;EPvINWv+#6(ZJr>WSI*CAQAK(b55$W&T(R2^ zbE8AZ_pzqhaq`N7Xkq~9LFrVFBfI9+{*brS?w9JsvvIc#&^@Ql z^%cXYvz@I9LpPr_sH{ltxtf8kPj8E3n7SOE0oRp_b-RkBL15(H?G*5U ze>q?T&KQ6fD_E7rkS?da>5#d(Of$EJ8@1g!VA)ot=c-|hA2i@CnLZNDfQ_wQ_UPXH zz^rnWygXy?^QQ6pvoAMq>Zyywz6@rIq)%MSfmt|JKGBkm@UIfgJ{}^G*eEj+%fPd5 zotgW-D%n8enep)6O+DM^v=wV!b}Wd`7jm#bh$3Z`3j=o#%A?e z7gvK;01DIR)xC8(OQoeHnHx1>0>YJ_`V&P|uefVvl-z=Bkkj7`m*JoT1T6OCo@shR zzP&$MF{_N${*=#322_pE@PO}|a@wXcSgb-FY0MXhe2hX`Vi;id(@FlPz3AdSd70=u zBglitO{3?GLSI(?YzF`FGp_oCi2{$;%wG!1K_c-k%Y8BRn|nK_77%6&?6x=1s0*d+qJmgL`;L;fFyAzYD<$PgUGa zVAM(K{o>_I=D^NjZnx+e-4e)00vHJD? z>qi=(m)5z4v;l(z5TuTwIX??31i^^s3dLFLL$5U)x1{vtSxanslMmz0f#kWzZy@=(tmM5si}Q3AEf3GV zMi6QW?AfbiEi>dSaxH?o7Cwsxb2<C-*^{A?D{dt$MeX8Yu`UJ{RHMI7LmFn;AOR? z>HAVZ2a=zu#m_i^C@tIEH$k;oX2jYyfi3+vxe8&yPW(It=Q+aVaQI2}_j**VqtlmQ zRgq@$vJ(f=z@8oI3pPq$azCB*aOHaO^nTJuJ%{5Pd%l<5rP{i+2m7`0+G{>dW1O<& z?p|=03rd4z>v_te9#m4a${Lj-w_nhBlw5tM`CxtJ1E;U=9psZuuC7^{myb1b4U&X{ z(YN4pOqywclmn7VFz?j7Kf{X@~MC!G%9;6_yv<6W=7&rxgN z<6t7>lZxd&@dI^f>3p}bPISoR!Yhs5P7j^B+9AtZRo9xFhQ#?g-E~lZ&IjTrYNsD5 zbn!bc*)rCr?u+v6(1`M%T1;3Ws?3}cHC4%>YqbyvO5)b)I^Mm-@5t|HnF`P?6&Mz; zu}d-rHP&|)-r9NSNcl5-Mq$sc{UxU(j3|o>M}IL%ie8e^i5?l7zf>&CGGvkRgLt}re@PIfyTukBg>C ziXyUCT`Y}pW~@6PlIa*KR4B+8S)>#Yk6y`8P+ehKy$Td$N`dpGkq>Wh#S%`zxHGm(>qP#O1KY--At#MmX%ntuT$Jd}&C1G)G#4MO%64DVAv#7bAH2z!kBy%WF- zA!Acz#8#6Sb}!#uEkl+`{p12m-U8cYcZ|8{J0jJ@JaMUwf<9uw=pgudtCY})%T*s7 zK(m4F1aR;e3CNFJeiu)zSUHAg4}j=b*#48UgR0hefrB}wOc0CDkpQuH&{zf++4_oP zKJa`GEKV;l4R!}a?z?&ip(~dd`Nq>H^04-;&$qM7;j~SiU$tvd4#A+@9E83ISNGJx zY$OhDMWFT&O49ZJk9Jq$;jiV2Jo)W{Py5BRc?pBj~#7#7_k ziZ6=BflVajBe^dQ`AEzO6?#{p=^c;#&wfvu-c502a^KID(BrEov#<#r9SAE%F@(q{ zyA_66+f`2UYo=Ol|1E@LPoBp*I;n7TSq~Mh{%$3@rZ3P=*R7bi!zTXuh3YfyI|H;`ysRE74ZUgIGU+QMR2@jsRNSzz06rdeveoigt#& zk~wO1x#18a{FWkh#@$_~o9m~}(-oB?k$9KEE?Rc%9iekBicmx(gU{ozS(NHF*K0N0 zz=n1?5S7aE*EH}GuNx-*yeRzx6qS8Ha#bv5{NZ$8u8;QQu^`EI z-v)U|gikKMe`TsHr1j~^UAlwU8@anLPkqAvN{v#C-`nMmB&-etiiE&TU7bhyYmy3| zLH+JS$ER@f%a0{19k0EFf={S8p8Pm+P(}Uk< z9YHK|W)W0Zh+6~FYP3QV+~h0YFSD>*ej<6ki6iWOJ%aZYl7st+3>ytfbmQ~3!Oh(p zH1Uhza@1sfH`l`nAGZJ)Fz2*wWfrka#o&7RVrhtUz3hUnm$1yAX@j;AP6-c(<^1TZ ze%>Msbgt@99wxjf>Hp#=#fq1i_Vdw&d-_<+ddEfD(I8iE$cOT;PzZ&Da~NXF^uAZy z>q*V&{rLNml=TFcq30m$`R+cqhmf^h|J*~|gXyxl+c#Dl)))TSLm6?vlaq^x$G{S+ z*=DC?Q->@v`pUcpIb9W`GG0s|X~uj%r``TkgF;1oX{Fc2qv8gRzCAkG*k2*O+X@5k+v0!)sOdn6UnTqzSXA!u%w z^jXeH*ZH~kgD%~!r!K6Gu;WVD^UAXM1 z>#H*{6ht0p6NQM_3Wr7@&~%#77D*HNoO%YoOmofQ3VD+{v;L|&+J==FCymaVn};Jo zdYg_z?2)Gz!lxehvp0?#J;AT|q*k<0yqzekCZqsDKL?Xfi`8U0z1#Et0M{*e>|q%w zPdhi7*C!I{arLB7GVc3rm4Jru-`JHcYAK}^XbYc6J;mL%6xLNDBI3%<(ljoTL1G7j ztS2xs7au83F+t)>;?W^}+x%2A~C`?7+S&x`{Mjn2KH_Ql)TG0Jr zKradr-H12U?4LYaGOva+;}I@e_{3_%9kkL9MI|XEz=-*+XV3O}y)7H>Z$~!rGrR|@ zI?I;Ch@W>)+&r`d7P7CFGT8t%^C8U^$6z*^PZ9>*)GzhJfhsEG%RlHTAF3UU_k_LnHY zo=xM8vbp<|n0TepRJ zRB~Cx$F9Y*mWvOF^8PB$#!n_sS(+wU-QKlTzJR&_2wTHYV>~$$tgMPY3Dz0#zrv`l z%D^|dAVk{hR9X1x~ z@?LoBzq7I>5N;EzMZ& zQTza3(@U9l4K34v{hR_FR3^G{$<(?RrpCK5ONK$x^!+2#26Z{MpX*{8VC5cNMS9F^ z0tsVo2&a!bciwbY5iiT%zFt3~gvZ7n{5~^0F1%HMcLgTLwb*{rrM(th+W)_v`y=cB zkLg}hUb@v?{heHu1`tDkpp%3y1vhbIP)uqf@@)UG_+j&YuL9MwZxzKA-A`|!I-MQS zi&i18!WcTgTKnvCKz<9*WE)2p(29hJBYCJYK@m`|?NP6yAK!mmglzZN-An4!ofYc~ z33#9*6yf;y{eoRhFqY_-RK6r-5KXtyQfVbvleZyUX{6g2Eh&0#XfoaPGMW>XIO<#} zOgf!6^(fv~kLJiuqjRuRd9`ikwL|ZR2)l*4W$k~QrF7?3Lk@0w!@eZK0FC})H$2^2 zNaFPr)2FF_eRHyodq1s4;e{OMH%y)sNczU*KM)u}6{W+AnRjC}u6M;gQA3rUrz;+l zLnxOt-oS*0E5c|-l7MAoB)wwtPwYsh!J zG=S(g7C@)BD1u=g$Uc?*B=u56r1H2kIPi>eirfyqLeqr!0HqXCH0WodTQKfzHgbj~ zfJUtQ;2~dkgnQS1e%dQUsG&m_ z-Ug18)?&_aM%@Otr$P~+epcws6^SgU&fr7OFJ!tO(cxk9iLqQ4@LgBu&3mv!yV8b= z+NrDz9F$!OcN48Yxn!SM@w997Ml1Hx<9xAp^R^*IAbyeqKNBeMBIQBlNImh~%X)mm zq0Uc#DM&6~`d5{3;+IiAyR-8;C*K*dvZbH}(MoIn!hFNq0-uXJU~Co7rB15OqQK)( zq484rOk`2WmP-d@zX7Gt#X+x$?5RYc zNmDw>)cn@cIa;qmUwL*1WHBjCRyRvd2c_TRL{=9kpQ*TH7ZOv~SAA{5_lFzRP+nc1 z=VqZYReAZ3bxtHKHFPkN>%>Qolgsm0NbP+Vz~h_w`^4|BY=QfgZ&NBde2y3=)f`^G zAOEAN?|d#R@^+X*ryS*t<2L}K$0=HQ`;5A%&&am<*sh-&#%p=_haT;dTCtV$!R*%S zM7&yoWGLmm-&6#rS>`$RRRmb0#UzkmhjznpwbI>fat z0|rh|l+w~S{~3bbqyX>T-WRdTTzWUP2EEL+LD+-D*k&U%$0!JZ3OS;_k$&GED zuB)S@5!!VM3^a8M993nCefEeMNq0J`$L{U=CGbuw))I)35d#Sfh&L>@nQDf_vmdp# z4V$SD&A)ie%1o%nXr@4{?jIrJCc&lzfkoFih+H8y1Rs*!48R%EH8+_)4ajO z_!Jml8Os)biX{)bf6cn`Xd9vNC#lgj{Gi$~s``_bQPQLI`r$u1LEBDx&#^b$Nu@i8*7cht@SIT<|a9B0d|vpk$Q z(+Gb+TVG=HQtw^DP?31>I3ytfi_IKjF`H^=HFQ>WHkKQxrpK`PT(_0wFpDj2yY18s=P}+w^&v_aN0CAbZ z-UdPXFW#v=Cpo#BmoC;L^bdFAT`l5r!vf9@7To(Rv7fYa;~Z-RMP4FaSBH^7el#=^ zsZSe%4vL6h@mo$>uF)|`66V7PZ8uKCUgw=p7T_cPu0QJnu|;|^e!7~B9!NLcu2-aO zgS3&F0y9zl8nEIlIfPc6_S;gw0OPLnUydHU1Lk>I1X|-9IVZoQx98MU{emTXIL>ON zW9)w3pYE6RYaJOq=yH?X8!of&X^}rQBLdfk9RMN(oIq-&uRulJ(26OP+9L zpFqmh%?=|7>F+4>3wwIFark=R?i!}=J{zDrqI^HgQuzMLoL_27l7N!*7Il}ZfoA{8 zrilK9n{z4W@bnz)sV4cLWBTcf3xiu3@TF1<| zW~xy6IlyHCdM6s_odqPlvw7)y&8YH6NiH<_EALFh5}TW??UcnoufTgrM@iv))NJF8 z6LbNr6((A;!6K3e-TAa)PCj5cmv(D~gQk4ZmMo0!n>%WD%5XB{=MJTW?X51S_UGZ zc_oNE3TpgyV=Gla2hYJ>5+VK=?>zsM5Alf@Sa4S*Eh)^_3R_q~WSUT+xZ7`Odh533 zlgnN_L`HjZz15-wB7cbGngRaYn2lD~XU#6nE;59RSg?HxkUDd6%{TTwJN~}vgh{>S z!O6d{;Ck){4&vMifOyE1-YHf@)?$xMoFIB?W(71vmozXHQ}7IB;+W06*gFSJe73C7 zqn;6V38EI5ENizr5$5~!^}%I8pd}30%pU#y({czz`74BgpqR8=f91n?qku>5UlSM& zx3f_Q9&F&5(yl(iy~b-OfbF0ZNQ4qMCkq%kbL1_M>0K{5qg(>ZZpdBr;92S~NP1hm z(9IWaRLlDo^oorB$tLgd$gbncaES>9hn74|idt~8R=Q=TAx1kBycRgXJ=8c$-N}&@ ztxE%njMJ6>8|}*={iy(Z>9qNHorCs7Ag6-{V%HCDE70=Gal{4wB~X>?x62| zvKgY*+xUGm1}{dI;-%p~NBvz}Y-W29lq3EeASXYZ0$kieNKcyi{uu z5@*{&pI7ur-D1#VfZIR(Z+OBP@Aix9T!a-LKWY!MNru~u?%1D@-=p8zQ43nqS)$cQ zph@Lo^mgaDsfj7lF}|2PHujcx){Z+qkx0%rdJ^|zrv2sHYP6=CEA>R@mC4ijAM{WF z7+YvD@atHf8=~%H=wtR~-(af@DxTLl{Vz{SG4tWC6aTJJO%^Hp2r+lQH!{(Gx^5nq zQeP3@x^GaSM=_E(B8*+EsO-a(WJn2gdB8$0**Qr5 zO!-@0s6N%NevG^xQAT zH;+$Nl&wt9@gdsCtyqP#Dhf8;!&&LfXIeB=?vWQ|rot{xYFmHAQU3N{;2=7bS5@!;s)xTLBj zDOB`L(~F(vH5$VAnyX6gnXb<3yK@n~z15o%Qy^&Syl$$32)tnnB3lWE?+Uy-1i|6= z8M>>Ny?Qt)zxrucXlot!j#@zT>5{-HD~X~ z3bhMrhOo9`OMHD(sA?PZ9%0lzRXP}7SVj9dAFOC%T-igM)uQHVx**%WTKSZoX;pMk z{ej7~_c7GCb{9iC#Bw$4Iu5u^sSblE^j@R<70RIZb9+97kEjM`{r(Hs2Sr&b@hVN? zPlhsBWr(a&O6#Ox*|!sBn3VFHYeK!C{I}gs_+$pmzNx-_I_7{~bnE0|4uuI(=DQ3U z%Xd8`{B=lWfOy4`&p81|;THvevbS0{ecK1`4{4oLJq`Q79@bSaEqs1F@rHW$IjCIC zW0vejpk1*@a6Z287Q@Ir(L;JX4AtCMQJ1k}j?Q+N`Qjc+0FNIZ?On5f^X7T@GT_~7L zvPzwSx<#~bmL-9gS-Wz!#+In-V6LgEwB{AaD)l{P@hH1N<`^p#Q3;(JE(qLaL(zBdg#1z!@zaSC^63d)ac_N9@!_r1My zop+JhE1$1`9-A5)Q1sJ6f^yBSxDu1oql9%Ixr9SllbaVHV<0glq`H=mO^t%={@IGdN zemR6?nu^+U%HfLER}yB3LU#&B%|M@$12+R*c8rzDY}|F6#^h)^06{^(GPFz%cCxx@ z^3gM{Yck7to85-}Bz_;cS9fl^QMe5n1$2Vuo4`MhiMXVSCFLU6=gPeaXnyA?w1SN>2$##xD+w2`?Pp@7Gl;KH~>_pMNf;{qrP1*VwQSu7r|#G^bG zx;C;8ey-jAobrH%tGw4 z^}9bT$+RplI8YROQ1v$@F0pFq3wAu(rV@4W5!O-SV-vChUbyivCmuo3q}FPr3bm=i z=`$?kL26f@?z+r?K+*2ij$rMDoo4J*BEQIen+?B?T1^%&Gavf&DYr^d5;1;7uVKSz zsa#-_zm~yHw+B>=H1&kb0}5%pNH*8zV>N_@Xhz37Lly&t`Y64QjM~0GYl*(T^7Zw& z#F{1E$ga8B#z!>6=$*C}BG#5_jiZId3ZrR`mM}kKeE5~q4136a9>MxBwP&T>r_YqO z^l|XTQR$e0Bk3Dth`&EyiA{u#JCyd+@7)9+sE1Tfw0gu~)PkT0%v}}NUfUM6f zsanw-XayRU81Id!cPk#cQNG>g{M;A0BdI2??Q@a+b<^ah_rL3p^GfY+fw}N4iqOTR z1U?eI>D+Pdt&T?3^s$VEX;8A(-Y3k-^fvU#EBcVl+yaP477X`f=Zz6jBdF}F6I3)c zwU-)`pVB=a`~`|f7R+kwptf%-LZ~a^5M>t*cPc55L3|zI|Ds=hu(#igtq$>bdHUh} zu)DD6N}-DOzyKOHfLq%gw4AjNwb8Zva8BFalbh`ZH&9&bu)^E_s=(qvS`VrMi>@xY zygM<__Q(D~!=oBkFT^I;Pg;V@7s2rQ;}h{e~72Ul(qACdlG3m5R3CPZL*1 zLJnW`y^h)vJDIM-sa#kH&)TKNORFb4O5SQP`=4&M4%w=Gsy$tMR5IGzR>pjR;#5#Iu%c+;9`HhzK1=&K%;?_2s+ z&A`xU$_*YTjPzAy^bPjL&d;Pj>ga%lx^_x#V0?X_sU>uMngo>wP#&Ve72oDVoneb4 zd*$H%)O{v_(Q+FXi^xfi{!Y2<#+Y31nuyrsqq^ghAFduLKUrYx_{rNtBbusDSjzq42LMJF(xO5M45cqlfFp+{P$-FmIoW?S4FoYOhVq-G$^i) zzv}4~i8v$PPTRndyhF!jtu`g=3_4sR+=A&9>%xdnOO*URl)bs#w)nyP;df* znngjrR)1YK~rk%P^iTTuygczCHSum@cBH%WS-#cwl#pY?Z^#r6JF3~m`&p-=c|c!! zA+jQ&JS1}}b!|-Lb;kPH+pEC2pF!8C&)MJ_edG<g#$`oql=nD4I$kNCt)4`jIh~?x}I8V+x3cs+QRZG=y{eI3{3X0;R zRHJ2Zhgu8bTBw$|6$Tbq*qko4unPmv;8$lY_-(=$i?VqZzx~mh@j$C2c$E>_K$F<; zxeJ*c*olI>ciC^76kBw_rt_HE(QQLV-}(UG1k~hQ%vOlYCdW@6fUnZWBz(#7 z_{RTrDGHloCcmjWZCGoS+b+iy&!(_hYr(Q~yHBGKhP%iYgN2xdcymz8M&5VX@$rMb zw_dtBhhM7CPcVtZ*t;T4rwdnK8Q!m}QVzSp^dI%LdWl^rvW=XA#ZzX#zYBKbyP>e$ zdoH4_*k8IjP)ZFlC5x@VjdsceQ^4mvEnkNh9OA%H-BQa<*Kz+As%LH5oL$OFxe786 z8hhFub?MBU7t0@zKjlF@16A}j#`(O%bi3#E%3grmJn2v-P>+D0^g6l^tvu<3mdf~t zn#r|IWD>BH!##7~8=dtEwp%#9ZqVNEkh*yUs(y>i#oRSxWKys3B1AqWH8sjfqL7@I zc}4vKPMk*4vv#@A_8+O0J0hAsQVa*+%DJ|tJZDsv-!(P zPza1LD*77+8;nLWl|h#kb>C}Qx|O;8;W(2%w+L@5yiS#U?eO&l$t*qDx;68M2k)z> zDJIN1gB^(^E|OPRPw+x&!CdZDViR~wV>%N3ed z6s8&t=jZQ7^Llzfy%`3#Y0yq4I zPIc*&e9@;Tu2Vzss%bYbP0{!KXLxmpPtlYsP>qh`g$w6{n?9aitjYXl*i>=-u;4z` znwZI0wH5v2--2TBcq0JRLQl$ZVfWm1bgV?nO6HK?#^m6wQ5L^SQA&X)nCNs5g|6Q0 zdS*e6WYK+eC>?NX922SuCw5?S-65cB&IaNG2`@0F=pfqHsY zg*z87+o|*C9Gb%?`g_x}@0}F2;t8a;aQ6OGKp#lx|NVGY!_bcZ${@@sgBExh2j-KY zqNx1Wy7xFv=&nLo2w-~*dESftF`;GYjFT}fJ-KF+QR22=M##ACPJ35PO}%T8&6*N! zvd(8fVMFiaX~5q$*j=xYhFa92rJLZ@9LT&0i>%{gV}>VqhN~US&ytis0~r|B;ssf# zK{{tvfii1&qCowGG}(=DeVf}^5k=+ip{c%%)E!s>MNQ}$#g~FTy(P7g%S*X zZXp^so8;acytWi^4Vq zQ=WP|a2pId(oBZs2fG(S;ue(#LRP`*1L&E3RM^~rtI%{)i9zwcc!ra`x)Yx|3EC=| z0STfDv{i0uXZ1BfM^lEhhT5S^v#%tAZagIG4IV4P8@=Qhicp22s{+R7Bri4Y7Pp;} z`!Y{v6K%ej6Gy8tyK?6pD*7EA*RD#<;?#;)Ly3)_*~Br|SGvp8ZJ=v5M$`mdbFXAbDlYSH|C&8L|dmOt| z#N^|Z+)+h|6}=Z%PgQfF{!~8r75V0b1E#Q8#0n;R$G>gg;IK3!ivyn08lUE>634r8 zpFG)*MX)mSn)xnjIH;P2(`8J`wPJf^>BvP7&6QDj2pWo zy4&s~xDBVgF?QqfOtAXxt-WWRiSFjc>-x z**dQJ28s|6vc~PTK&#{q-o;BrJMY1_N46t4q@cuZwCQfu^EJ!0$Dk(NPVmY*sAd&t z2ICQed9H9o&;{Y(giaC*s!&hhueYA*sO7Z)mx+u-T08%AX7f@Fck|u}l{?)grgGkz zDuiSTa>-tQap8rZm!t|lW2b)QeA4kfoE=WkDSV5?iolB1VLhwF?WO{3L^bp!2lHj| z5;3Hcex;76sga`izdvsX(2 zNQ@6BD&JJS_V2{GrgUEquaPGk1xFm#)}#*awubb(MUO+wr?)3=Y^$4@1}!ueS|seL zTM`}N#aH~bVA-N0|;=@ab*qUXtV8q)9CTfcvlhQOsTs+L^}{5HwkP$J@l zAJ*M@Cfo7xsB8}D{BtA2+RDHpA?kSH9&~JnvA_&Lp=7M^!+W|qw@0#_KAr%BPj1L- zd{@xUG)jRQ(p~Anu?2Z%TiqSzz>7hd3jXzD;fR)|krs7R#7dY0U9?X7UAcPr0zhqn za!?Ub7dkc9Mt;Ki7SoJjXmBqIzXN4QM6!|Z&xvvYA(1Z#@n2kdY;OX-#qJj>qIP5&b1muQoYOgqeP^l#?*={ z9gRX9%YwOV)s)(Om*a|yZ(#aTeyh|v!iUwp^~q`viw*50m#a@-ZwI?7_)(BLQrpg7 zVHIeh$6KWLD_I(Nzw+y^S0a)sTFeJ39Z;DScVmYot2@7OcWFM)$g`vpZfS?5RR39U z#7U$$%N1L^-q=FSZ0K#?R}yv($(CHXuN>HGu7~X+gH}n_G4v}J@ku0_A+(zmdK2tA$p0MBXkovSMZvS)GaKYbXB5Ac5`<< zniZINjQ8%SgO$wwiWi1PRmPGS<{oCu)BvtegE0OmZ$7HsJJNmilg}cCv)5uH{Qqg7 z7LW$|8W_XExPS(lh&Wzyf*M00+_Q~Lh9yxN?G}R%owUeIM0gK>_ZY8Ul?+wdI>L*o z;Z;scpI7@7^s}chhx%pPL$*Q%r=OxG&UEc7vC*fZeA4$jodriq3Y~)9olmO5sgS}= zG3g*#NNjykri(OqTYXd3e8pt|R(P*LKh(M6=8&Aqf*7RSUC#Ydn$)x;Wv=cRYpT$6 zre%A20#;&Bag|TNe7MRI=~ZRa37gQlXJqXhdci>niTzWFbPcf+>h#OF!W~@Wn-2iG zdjOz206-<_`#wI{7R~HT#JP0qupHcDs%d{l*vDt&C5zVY8A1B!nZ!&_XL-4EzW=gF<@ z!i9M+#FEK0CFdF1?PlI&FLV0c&#bWtyITvFKfz99;)_HuB}u}e-+ej9$zLel`@w5R zr^%jeo|o#(J~-(Szhe{#`T9OVPrYq=2>4dDUXT+yEd~HRJPJkS)Wfr->leTKo&sw9 zDWKMaY`~+Znopi>e0j#9nEzc=_oHT1(^1jR%Y_1&$@Lkwit?oy3+S?+6y6Z4P-iX2 z9H7tC2#2k&tryzes)R|hdDBg z59OLE(>dw1W1pWNoxgJQSJZfP6o;4SWlpTUPB_lrAa#KYfvc714|Vq#V~cWG`XMHA zL~Z-~IR|XqU>&{p$Qi zXUt45L~y_Gh@j;5#N~VM9y41mz`H&)7ETWAPXEcZJ(uN5-{zz{m~GVgPCgY#pia6S z=@sz4$TtTjTxOvY31DJ#5m74W0ez%sMYqR8SBMy17A3P@Z|glio>w~give1+sLdMWuARj zCUrm0=*iUyns_sXSX9_SN43CfqdEkTnLy2m{WRF1xro-|1r!pz_Bft+ixwtPjmL1+ zUYWOW-p%w9=Gu+fmydNgAbt89wsFH|WySR_Vu4_kKNnmRkk2@x3pu9VL5?YCt(0_s z3j7S~(NsN9;$%3j$MTP$uP9a*@n^l4qFrjw+wwa$d4oalwGmYoRqSOymEUD7Q8eUf z8*uonGEHNC^<`eE`fAgY2BsH99wyh5N=WI-U-DJnwyU?$dUR)5t0wuZ6Mt+&gWNNDm5pw^$V?0H;yS?0t8lwH~BYQ(c(qQm){a^D;1 zW=?q0sYi2Z)zM(jZAQoNv*|}Zh|SF;z^W2bQ>geXnI(|OOYdfE)Q1=`suMd%F^8rPnpudGQQrYqsnHyQN;}?*Ar0G=jgy0%I%Ib7XCFEg~#g*pnuc`Q1&`t7W^|zJIoX`FCHhixV}Sdk|XbI=7;M<%HLMA zKvcoh-g!rh5=27qWJMVb7277Yu`VyiV&%~$5=DU`|S7S2^SiN9!f~>y+TJJtv58B5D<$4l0swHoYBQ{Mk z1CBFm240|Kla!b%Cxi#ovIoE{P|JGu#uFf*bOR#PCaHAPD2-G)n!g53{=NXW^Sngd zFBuh^<0K;bx1kxXx=i=w0@-O6zNa_jIrr!hgmuaxI^{w68DBX-7zAnHNp!ph)9Z3us&@*2lH@;Wvelf8TH zfXc%EuPubocKa+2{L#z-mosa6nqisy0Y19p@wfP%e7$o|gU6ArwuuZqkTYUx^H0Xh z;EdxozAg34S+A@4+l8m>-y2U{8o8=ZU<;ldOu0@}$_z))iB%Z*o3@X7((N3->V;B`#t>t(1zyu~o0aiYs^eC{Z z=wbeEcKaOgM%@@bU52&3rD~q`nqx~lV!H?B41K6(P8^E zxr^y5ujfNiJ2ngBl_Qw$R-@GIdg!FGnPIdVt{E^mvnL1FDGzO^-Kb?f`f^y!di@D- z*lQlnW&sDp_RS*TN5%bpOIk54=zkSqjbzdMvif8C0aH)=h%B>lbJ0gS&=PH&LJZ*!m+oE$d=5^ z%2wHxLI}Ud^YnUue&0*~ocgEBQP0Q36xlorSbAySAn_6FW=1! zI^WG2ja6XS4dS@1cxwLoCVb^CI#rbzTtS*J>1O7`OJgJR_dw$$ zJ-5KDzSY!X(h1T~g(teP7m@X`zdQMmhK)(~91Wo2;~AVxrjB-tF{w3^$u$okK2>#a zoPk%yu<+AH%|{4i^yYpqsQouWK<%#w>M5Cv=rTmmfw7fSfm2#Ez3z5?z+7Q2O7GRq zhih^W7q*$o49X*ik$!afsWBt)@zM+HxbzS-L&Q4aa&fcX%?T$;UNe|p$GN70`pna~ zsMM&3yO-S6dnk}#BNB=}crn8L^|+Dc^y=LSFsU-RpW4+f@-Kx^>oyRrY;_xfkb7co z6G$-pGs9q+V_vAkY?fyo!<*uU$k1`N=`+MV>1JT%MR0d z_mafQi)me-MS&@7M}gToG(v5}?yaB?u0GyG35Io;K)d`*m+T}}Sl z_bkBEzcSm)+K4T@HrOXEWMmKP*>#)8c^_E~sR77t2tt3B-qd?EJb*S`Re243lrwFo zA@uR(x!FrX_}k{lvs14h0?5|qJetR5N(KHN?{F`d1lrMSW(*yC2Vn_jiftNQp9$*i z*WNF?m4rXfxH?R>CfzdpP>ed^2q+-Xy>uPxW4xpBDj+_m7&N9rHwMu*M$j04YAzny zvJ%Jm=6$Ub&=&OH5j9$Lro@_kDO%}&RK`^W_tM>($cLd=9S}Q3dzXIh^bond)3d+#p$YEx&TIK=VtM@vFoTQ;on=ow={<~Xzir2son0+{@TqKWmXk*>xqXhjb< zP;#fP&2}aYk(GT~X77vmZbJdyd}d=kTxh5l=VN%y{oZ+jMYKVYz#+0sK~LwY7lP0} z-Q^{!d}Hu?sIsMNp2})K+%N9UD)6f>3$GY_KgD^k!ac<9P6{z{+?R#6+606WEFNDE z1=rkbb_-O#YC?0aFx ziSOhr^`7x~(yHkkE_|K;{1%fon&&PgJgD~IoVe~+5*))C)w!}SZhvKnYo^C3U`)d- zH$OXT!cC?a6j9IFgMkNL!Z<$RzOtk}4=Ep1Xu)^djrC{4)M%QeUki_2?+c73H`cjy z&%AL=m>MI6td__*5qnl!nb&>H<|6EXYHE&{F^lLBc0{>@AwMXiu7fh_2Uyd;VYkYe zgkJo9r~54nn6ly1k&mzPHr8fei;aiELZB?$PmU2O4ZA_H=o|*bH_>@S?eCRuc@{OR zk#=_-+gfHRxEaql41HLICj=?>cA{9Ed$OS~uckw>t>(bFe2A;~{LS4QurBc_`r+~2 zu}A_#j2ZvlZ3e>CwV{~t+T}Sp;-<+6Y??3=@X~Fj>5G??EGa*GOu&oVV{s9L^QCk9 zr|!Z+T$%mDEsD?nvbl@;z4V)B5u3nw@40XhaQ5by#tVjj;Wb{2NfO1{3p|>3+ZkiK zp8=tY%Rne^dFc97BRT82yvQ#(s@pMgh5LTRC&FWH0z2!Bw}JRcGzpUC)h$aHDfQ0p zSnkTCHIQ7ABZyYgj~gzaV!Q#Xbi}(@I^-o=St;VCugCHwLLRf&?c#(0*EKG49mLeD z6|Rfrzo-th0U;N}I4H#{~)%<{3HvS!Vj}$?n((h%{8Vjn8t2 zfh2dklVJF*#XK)|byQkq+MN_R-wH;puxpj$4^4!0Pvp~u&gM(dmH4x-i(j9BIeHBX zsi1=W(sI%azMtpZ7zCTCqV@1dQvonUjRJDV@8QYd0J#VLU%zh+`hCrPG*bWe%k*9y z8IG1^VaYpc-qdHQ6KUVTM`;*6o>S}dUf9Fwm=*8F2d~yuED3uN$cCFlwKb1YDzQ#w z6vcFl$dI-ANgC2jD9g44{L;!N@^)MgBNAQ9BReAtu{xg!AGt^4@P8M3ilEn?{8aJ! zd7_xaekzfFtl0Oeg^`}TNFLoxt@>aAdQFVPqlpb+dJu+uSqdo~)o{K1VPw|l-wDwY zR9sf7lZ?^3^63`;IK^#;?k0V;Z^RF)&8Ni9Yj{*(U4OT~1>RxJ$!2bAFu`Eq?Bvb5`q@^z*@@^G;Ni@Gfd)BZ1s&Y0JO0WYgNMFLVoH`Fk1% zMG~&%vz~51573I@)CTOzut57L#(aTzMuEd*^Lw`;Xcx61gh}!8kS{cof)FO=-Z% zQG8oJBzJ;N9ZAevrpJ6{8F^joX6 zI5X+P3*9O^B#^R_vUY_8fKS>Q0Ox|e3ay4R+wk`4eN3xd*mfN_RdwiOo+qO9y;iq8)UhzB{ ztHqlZD0%C7b;E$lFQ+Zw%ZWnjUqii03(`*h+CUSjm46ediQli^>R*&FkYqk#v#@1j z7ZeJQNjnf&fsSd|sdZWa+jtmsjnPcOaKzIcH<>fy$F5`b{TuS6s2f3p?E2ms1vsikA?TBpn-JbRjGRV8(kNCBS z5f?#R3{x$ScK1nU$5LH2EhEFRg`Y|E!`NCP#&M((*^4|nWal&#-lEoI8E>WwNtR~a z)Vo4B6BSKVI6F!pj_gCmtr%9{W%sP4LqB__3wK^h&_dB;>#dAXa%z(<{zlh7Zxlf5 z2g&4%jA%%Gm<^$m9K{S`-JafAS3r&`{SB+?iDNkBga~MKhel%ZZK?GIi7w%ua)fjseVc@eM(sw@Q z)aO;GS#@ZT7evcbb1r)Z4M z#aGyegNhV{kJN zv(j9njW89YGXO!EnT3E_@j1SFc~$#6QT!wdlqWn5DovFF+2=OHi@I8FJ~~+Hf0+Ai}0k>yhG}T)+LU~l7Px;zRwY5 zsuL$%{|Zyc(KjAo#arie)k9N1tefjH{=VqThp6{FGx_i3fu%gKthUnL&jIKnQz|-@ zuXx~JkdCYFx7oi zF2`+EvtC=3bL%sBK%PSE@L!Cy1%QudQgqJ4em%n^PWc1^z|hsxG?36ao5bCaKkpK) zsv%9xJzCFbepKqtCmq${w_@Viq}mBfgqS`})+D)`82n(pTqOGbFSh&a|XYk#*T z`lp~orM7<*Tb61;u%~bwb46i}x4pcry{&rziLKd;&2@sqU|p}kGe7anl@70WrLSId ztLqpYQka5uv}=X^1DlJYSLyH!0VH<~b`oHj;-af~-yG&VrnPSv4B1(k^@ImUW4WTB zJcTBGrkJ?G;@2g9P1l49P# zP2eJ-EfQz?I@yS6dCD+g9}%wVe96UsM=3%hq5)RbDH6}njDAcfHwC*jzMlDu@0rBV z2lIu*J?E?I11w)}Owd5psWnX3==s!B>Q{gIZ>g||yMR6P|MpP;&~f-3Kv3$J-J22G z@{`0yzXNLYlk52!`5qTyFKJq85n3N$4&G9q;ZyX&*WZtJm%eCTFX~rjCJtJC2{yIy z-aGTJ9(qRW8{RgXzG%q2<=bf^&3)G@=)0SRFu5;OOHEp@&~5chsxXxbN`{-Z}jRh*Bx=+(MMC*qMu-w@2u1k!AV@}zgEL! znCZhNG)($^lsTdA66jHy{D)#hQH9!#Ygb9D3E$jvFP;`WRk_F5<4czkQu5aAouA;O z`C@^8XVICiFv4z`_A7LX9laOmn)104c&gdpitt{=r|-Z`FbR8Ft(EH64*a@1{<$I` zg`wajnD3izbN&DIsUDspTS3qu-n6>$6T$(-TxJ+sCBMns@;lwApLf7$f}tM4Lm zA}A-*z(&gQtd?t~4e0pww^dHir390>;TLx&iMr?3Gq8<0kdsTHs-Z%^W{plUpGP;u zkVT7?VZ&EbIVjjTD&Nt#nOBW2@+6+YA__~)92oP|j&4YC23Qp54q9Up3td!KC>1PI zf-Arp9el&I*VZFU#ev-+`XqB693>#71Y^{bH^jET6Kea}7+1?+(nq!!6!!|5ZM$e0 z6|4Q-kR=4=_N~_YnhZ5^KaTI2c3Ew`5QRM>Sq-AsM#JsrM({O{=NJq5AF1B|#r?bd zvC^=fG%0-De-a%Ui3&x9@@Rk!zDvtfn;oU;5@w9Mcov1+EMvjfc2O+nNbPlepFr9o zhx}xkq^ae{RLrcMm?Y~*sjAAX4_%06lv99pOvZOnnDyOz6iGT#_(kl6!*v}CXAE7l zdl@&D*)pf@$mVmc_oFMy0+Ybud*HtEPu?d=P z`tkd=AHy|`C=P}m<0&G}g@^Jy>yQl3rGfp_*+mK)aB6*j(y^muTypQkq`Z~->8c`h zX4$oH5mTiDp=*=v22ifi{oFz)&`8(;D@MpoqTKh84k2b~8tt4~Rv?VF$EYqG#2U@9 z2Ak;ehOt*i8Z;70m=QpyuVaoz^Y-2rRp*>c!-5?O)7FVIbO}TXJ?Hhv{&!HN&Z~ou z#R`aSAb~Pns~f^fn@qlN#MLtP>j`00(TjEH_-P9%`hN8m$w%zr zJujLIlxXgXEw<8jMAmUNc`|oimBZ1JjZjJ`{5?TJS!k^G*e9RuPw@Bu*88{mgaa>{ zKVzC%%Qe*8mwdr@@d|~E*Qt`OH^CVK;H5&KE1JtBlQ~|YqPW%TNLVFms_7A7>8SJQ zQ)~NA;1o-rk-4Eacv&S-2y^-w_Wd}-O9GSY%_K6yR;4uxDVp^Z==+8yzAYbAK>9v3 zvG1>oc1nw)M{norz;(%}S;x18ewHXMP@~m!yhBW&5&ut;kMENdP#HXYDEBZyRAS=} z;;i5wZXLasJyV(9YwkZ(D}U?0$Qvvpy**A-v7mUX53KZl@L6Ux!wfS`JPaxvh6P;{BwmK9FR8rpKxLE=^Ye8Ng;_-J9_d_p1Otd!`64PCaCZ+VPExKiAJNjp_9K7 zLf{W%khc6V(GWqU=c(+S!{z$u88v(Lcus2jB}4g89EaRER>;I^$_YK2D+D|JNkQ5o zRC_XcYC;=kFkM7)@04~ljfd#Ry31!R9ul>vZ?==i2yZ`{4WNELVSU7dEQ0--9(MI> z-5;uE2j~gkzOe^$^y054%0`0UL|Ej$-Juk}$|bI=xQdSutTpmZ`8S|Kf&mranAcf1 z6c-Dg%=)0_k_ijYJTJO$8i+XgLexz`95nN7T(*gPycmj( z#TR8fdb`z#H1;N?t~gk0NK$*t&_1|{6R(5^T?=a=2>DcSmH<(oz1I*)xysC2Gy}Q* z@H(^QI%f1_TlTEPS@4xMpe@DJEgtk2M80z2HB9`&U=UVYHk*8CQ>BBkIhHltPVgzc zizpUmH-}oPAXH~}P7=(wu~en5O?KdS1sFtx>CoG*se`h!v^@4Aa}e)3jsJ zP#*qIHQ@;vAodi|ui61tA4sNt(rz+pUaI6lzhTgAhcD{3_Kt6u*2fZsjOsGaGQnUv zSz+Si^XH?JA$tlPih5q_l7u3j^Gmci*F@gT*H_&E9G z>$y@IKh(EvWgu2mxY485!8M7Tyu}WLw1Dk5m$3 zF~$o_#aF5E1~8uFh0mGF+C6B( zidV9|&jZ-Lcp-Mi4(#|E8QllY`W@3<-R0lp@>yG(_cEfYzQ&eq4?lM2Kr9LFU&d@# z%c75qwIFi|3Nn{)!#p;m5AS%os+)P9;Q5N4hMmGS^cyKu)?={;g&AojlkWuXRvgJ< zcw1onAJ4-RWSoDqWAyrZNc(zce13cHvGAzmY5r@2XkqQ>yI{g1{K@f~Pt_wGAW3<` zchynuNa~^Ljnd91eEWJYusTw?omR^lDaFu5eXsYh(T%ZK+2aXEN&X z7^C_oo7U)2u+r7nYl{TEKfc+WkCDb72p9C}t_vc8REpA|-B4}E|8<$j$jUotM-2>m zVS~0dEVO{DFdHmT1;knYcE|^fOaJUo;*j@5AI0ZQcos%z&6p0pgj0XSZ~H=2`ls;) z_@iqugIcpMI;_oxjiWY=e7W@<2S4o!gL^C)Ij-=W)wk2A?k8ZaC7HAqR4<{ zH6UJ!1(0YNuRr4ZM6K9iN5dq3zu>#(^N8jhQ2n#sj~;IdJuj5az0)o71MV95yJy(- zWp1$aruKRh;P3T$rNaznWwqz7uOol-G<&A%u}t8 zc6CWV4~H?#JR2ZzzlK>vPh8hIiK&@C4%klRq#&f8D*J*yWODtfR8Zgkl0@~Ab$e8z z-F!coqm&@{Mzv{llJUHi+5gQ`+Lnas1~nMy(Y%AaNf|a8Dy1R)W<}obxWsTzcGkoQ|G72f6_8z zdJr$nW6e52Q>;GTKY?}n)jGCWAh>hr>V*hCl7*hF+n`aj6pcms#Tv=j1XC^jku1ym z7BQ^0spndD6kfahr)c7K9}xMP0sIutBHhOu62jZD%9I@R8UwAhb&22QoX=#2wrMUQJMu-^_+N!}D$?sW3%%o%Wd|qm z4aPSqldMr}RM!{4N(E&;y6gNyY;Ag{qRxJ=P@k3dOtlGE^aDZ9-W*H>L_^FtVS@H| zt^XfCiW{xt;~zm&uZ5E8L!#Xa4Li-`Nx$==SZfeGd0%axua*Tdh`*)JcU6}3TDd9I z8kZ7&XJEgjQU#{Sx{961XVYtWqmj#c(@~rL--@ac>@Lgkhj$uj1%4>D3gI0%8tXa#TX z6k3iRgh16{TStftbD|o>Xf!M-=)6f%aZP|~*xxl_F9u0MVWGA}Cny54*9`p1ZEM%A zS1r|H2|4ZK)47DA-hSEW*E}xrPHz{Yf|3NhF0G}Fa<(j_2eJs)4W*yo!}tput8ku2I39wF z^*%IS5g=Zy*Hxu7yC?yW$h`fGqd4#NPgY#%F~PnPItZolp&?4a)m)iVatq zTRymXJEO$e_BE>c=Go{%{R}bTqA*qJrq?p}J`LKbfI;e|Y#*h7R~=uML|jC^X4eHve_$MgFn<4usN7NZ!`e65E`I4h%Cs6jJ*^^v;~9_c z0#o)E1YV<+fm`HbOKvE`2#b{m-i6JB{al%z6sZQ4-l^4LJKh2uT7{QkUS9RChKhjp z0dbI>00F>~anfFzUMXOlR+{`#oQNXR^jMzJ;qyBee!>i)?aDN?T_G&rB)|zWULV3K z8p8-Gbup{R8{}m~KGc%MbB@wiL!MPWb!Xj+Nr_aBlIQefw{!}$!;ynWT>K_*73Zy0{; z&47B!!x&}wUe1Qmh*^|8>vT$Z6|XtP_4HB6276%1orlNrJ6FXNvDD62%E^M^%59;) zM@J=0ck(6f1CHMek>lrFMdbJ$b^O080Hbl*+`{v3@=gWwT)Y1ai7=^tS3##YOxuVp z(WoK}ujP8ws3g?4N>ZM2G*GeL@UZ%ArvdmOd{JcBx2f`>$)E*D6EwA)BEqLsm^BP)b$L&{5ECBh zj#L-Ujvw^L)rh-c+UG>GhBYAHg zf?|_uCXknfz5C0?9g1apa@J{QmM3F&uUobFqgJT(<#CX#<28IvB>L6sDA$M{OH#M;~)&$s$H(nWj6PkA2b zb9%qkhcld?DWZg*oeNF&tjXqUBYZv;#aXBlkzHl5E^a)@S=dQ7+R}fw^x=xx3vBQ( zG?xm68ts6Vth%HINdaf6M^QqsIL%rbD)?{pk*AlSyYvpEnZjfJP0{eX$}g=ES7-F} zc;2hL{F?m@gU)l$7O*f8g|qnzff@>IsnB}Zq}pwgUxaTuMtGuAQm?LK<1vu*8w1%y z2toitMA===1|PpYMsTzViSX8ny#MxaF;@trUwro_I|Lh4!H^x&+{WsM^>MIP(khD2 zZMp0ok!r!J_o-7!F1d1dJ&Zif3)(sfpU3hU_=%^VI6?N?<3R-s^6O0mTg#&l0g3Hk zO}(ZEEcrCm25b!g*6V&5kt?)+*?7C!39Ufv_>qvLzc5_p6s<;Q+9<*jxgQ*Q&8eD3 zp6QN&&81}-1K8bjhA&OC-qItEJonPif9KwUg{o96Atn9tk?e;*zo~qQHHnV3kbAA_Q3jN&su)uy&x-4O; zWN@}PT}J&JcfmTk84DNI94XGeHfM}Y{Mb^52qke45`n(ewRa|Z{bvega`6mg%~Jmc zsf&-UC<)9)iafveJv|gLne*dk{|3wIZ$|ctW#LeSN+G_?W-aM5G4PQJ4zy@S1~7f} zqlsE9>o-+@TvinFtj)54Z+8IKGMhmTw?S>C*;wThuPX+C@4KL1UNG|_vFA@baPWnq zN!GnBdRL27|0ovPgL!{HMW#c$2qPu(dEtqc0T?H4f$4Fw;vQo#V{iu^jQjDGdA4MN zRXk@tG`NX`77!wf|Mo4%cEh*1B`L}7`(bM#@w1$NudP%<^|cbO-1qE;TOr1jGJ#0aN~eRg=l~1v^-* zG5&m4I>ljgd1u{*<+=;+w{4^l6AC3aecY;`+|P-M+PL8r%1dK7g21#m#SX~*Zg|0-0^`=^|T%$ z7TlWxKmN&04IsHGF+A2ed)ZMJ_k(zA0>}}NMk8T~yZsnmB<)XxF=~lJSD%5>oW4%x z25d69`)#7Nn4j8>5+tgt0AHwe;ca8bGg+z0{xPvg*FRrQ%aZb#h2CMA{nKA-`_F57 z9{?1Z$tdCV4BDkx>d(^Xn|T(%sPvxA`QsDR#SV*SRwkx-J2&@ zytR{m?b~P317!~&9dsVvQ9#{xmcxnH=w3eRq!ip&Qi!%MeK*p;a)~mHdr1gzoUT)c z9vy-N^6cX(O~Q?+jTe)RPSFR9l8AFNv{7rNZO38or;>;ywBg9LQy6(w|10{ui)tA* zVT12_ND=_fNC#0uYWVIJuMY*M+dD`t^}lUYD&Y2gr@+M(ExT*{pIXWZaz|An8vSC! zgSOt%eb$`ILG0+}<%JkYzdFyiO(~i*K)6$idQMlV_4{-U#fumDPq!FdmqBub>iX}O zRt2vfTWy6ceYUudoTwP=p8OaB&x9msHTDL8ckq|3(sY8av-@-yml7t1ry`GAVA z;^84=;P)czk=t&Fk$JLLVJRI*-tuN3xj`7Q&a;8)hyGc!q>)Wr_dV~B-8wqtZm*PG zV=_nK%*3RP4LD^V+0hqxYML(G(wKfz4D%m*_H01uj$k4a7Tz z7w6v=V)IgNAo#Zuck`kzYrLKpDk}E8rw`8F8+)dd%P5K>!F!jX3LP3O)Q=Du)8E+F4f%QAeSk%fiw*I&7Y%S@gu!=691D=e`` zM2)m~!<)A%a3vJ=kdS|?n*7>=oO)mK1Lfq5OIbnB2YA!nZIzP)g>qDMr(dvq>tq>R zf?|Rn7Dmh*I&z)OrssiGSKeb%S~OQcbFgJpBHk@-1TY9i^WhH^&r0OqXg$$vx@aL= zcoUfu6bCi-G=_^VeWub!;@Zc3l7P$(?#@%0U)n`jK?Xk(q-r?H#is-a}- zMns4C88C!yH?}=bA&sHNKnboHB{s|Os&KiK98AdF^L>sY6^@;>#Z zlOyZR2Z@cDpjt^N^^x;WJLHO>ndfI2y=hHQn1<-h>ys}#?c<8z7r@C7vg%6Fn6 zSo;uyAE4Z z%a{ACzdu}hQqb6Ur<^Z9HgZUww>M5i#669&?Jr$Bk0H)N^+{SpdPjC^%&KFSplW<| zGq=dkCwgZ0o*JG(UFbx4Dl_@gzof}g|J5&xsyV!nE0gRZxT>1L*QKaelD^{W^`MmQ zekw#r$pEw*ah5JKu5mWD_n*%4eqTa`-HlD_6*&vW^Yj|JrZy?(BM>_*t}4KU3`kWB6Hf81@?^e0DAe~wkXTM0 z{*il3%d!3*uzY}_>WFBl(&_6wGR^@{r?*TMw&%~oREQ=CaudG4vPax4@kd*eFEk4Z zE9M(G!_TIUiua;9-jjVGi_1J}CcKpU&)@;wKw7+9q=c?Q&{kI=Q6TW@h?cn`p zF-0lwGdHLw+-ocjuSdqTtr>r%8Ss=nRqOt^@wq}=3h1p4`qj<)(2cZKUA1IIMc-iE=MTuSXzi9OaoTk2bc z^Kxxe+4Ce*dz+3wq4U@{_s5LXCgY@DfRd~i5p}yEp~z$Dw~_2(Lez$de8z!&K*{QR zu%Pxu%oN?=u@JeiinD8nq?KjN7l1v@71C92eOEID;wZ?Tm|6;u%F~1=$<}uoJ!BrxR!u35E3+02)#zVVS~^z9AGor zO`?*u+zM4T>bf$CGMm0%TDJh8)DVwG2g-IJrFU|hxm7aJy~6Y4yKAUjs=0cTrzo8H zh+Nb&AXiSqz2En;mpx(i*qjbEvdpFRyv$9P^Ah4#@B6M!$iz-)Qex zJJgX&7uF>yx~3ufe7*ixDyKbYwG5(3Dr5;csSKQlufSE(XfRClQA3qNIj9sWm)+xt zD3$TD#o$cnu-bug;b}j9tcC4K$qS7%y6rU4ky#O`m7`*&vu7T4;io7&4X0r+>5K3l z_C=NT&G=~Ui}!}H)PB{zilxDS$V~Mdi406hTJUtuXS5t_NVsdWoO zq2Q%Ii99B=V0A+sbx%yqywsk;79KLLT~Vbh|Kt{mSCOYb-t2XCIP9=LvMzevy)g z`uZ!%)vY-*tBAr$8EQk`tayBOgz?phwPmripKRt;0NU3;@p{muS2sVIl#{6lP$hk8 ze_WrtQ!7y%@fO4X7hd!Ew13HBVO6DR@kQmB1-uD zcs`2vwi={?xO>tfjg1iAnp|NUzhu6>cht?FDFjm4wVg{|sF&}i{`SAXPr^^~L+KsaW@nP_f0W0Dco%4lJ$vwKMgbJ6ii?45<3n+C03#{l}u)u2k zx4@bU&bgTb4ysC@053X~PPs@r>?Qn06?7$q(nIRi5t7e(O+DyV`H9Uu4^lzN&7LRk zF6>x22gM2KmfPY#SJRl0J^Anei|hzV;@XU%zF#u=9t^FV;5jRfxlya!Kbw_q2Nps% z0MnLZzJzeZOB57mss(6|O-Z;L-E$qC0PU{;DFIY)WB#HB;~Kux&&7umvf571Vxv1X z1!bjmEJG1h(P=S0rxqHaxIa^cJ-F^hzPbA`c?)9-kW>y%pbm4xjp?+m>YUmooYk6nuN`R_ zE4qK?KUpBt|73yq|C0rV#1yK9TJR?wz?7Dc+YI^s|iS@@c!Py(t@9 zz@WYWHY~sZ^adD!{yF|7%(io<(8rI7yDJlDcXd13&Ct-$0ZFRk(D6&AsMQBCPeF*9 zan)B8|AXeM@RyJdVYG@f*=Zw%w*}3SFfd!bR)0f_5g~b9h2|+iX7qQ%sooMK*BM^{ zaHZ1x$-k5oYtS@Xmc=N`6r0986SZAnv)rb7Yc8Axk9g>O94aOYhAxyCFm!uEb~HqmxRShm+S^9O-e(P>}t>S=!0`-o!8bA5*fs3UJ5+*saGFt5=~Po{cD zWyq@vBl;-~?px6zUz-*EcHWJ)O^X4N>!LWs)X*0c*|5?Jb;fQJu&5;O#`LZK zM2&NY4$m$$z#LJnZWhK4ZHAePl4q#y2R|~gicn`D%ZyL1P?Ndt_YyBF|DB`hq|)dX zkXxwC32*hi_}z>Kv+<_?|8fP6ibE*Ir)n865swO55AQ;IfnI2irG#<&g=A~6QoqIY zEifLxLTQm;l%yMKlU*Kf`h6w9kLfiAI;bT)$W~A+Hp8 zR{cTmhSqVaVZ!Sbq<^Uuj9i*~iU(UP ztgwmYBrxcF74e!T%b4OUpBK?BqGR&H{Yqb;Hr#e^Gs?2Sw_`8-`6WwY2E*``w8kGa zleP)lpdQ_Agj-YmeiPw#>&1sG1hQXmXBxeaUX<;CPT9L)ap{<+~Y~ zmhHQu52(_Qg^Tjf73M1#>bQ7sTmt{#llgK9{=HdyU>*P{H2QeA;1ve96 z|Je}o=RSOvVp@Zg{6?qM65r9bWMVjby{CS(B>b`>N_|_t0Dsr@Wlh zC*^g7H+yYKTahuQy7&ZfukP__ZOgdh>9#@k_d9yMMyBKJJnjv|UR4c`Y z^;-qN-d4I=P^G%a>1Y)nA_Gt1oc5*+Iexv!V@ z9roRGN|mq+pYZQ~y36hu8?TPu)%rAU8*7(J#T#KzI`T~mw$)|BmN|P?9 z4zhe9pc3^nU`4RPo$ujSnTeOJWH~+%YvYR|NL4Vc3TU)*k!S!CdLN5Oxoa}QLl;f!oDz>K?7r$;S zm@Q&G<{(}jfXf5Gg6gW8B2S#w=KTNxV{C);%H$KvE#nlqHb^7X$UF#og4^Dy!aTce zZ&I2b*LAHf!e`>fnp|b8EEI1qGRqL`@z!;___1Ltwb6IxqnBOgUU2m03427lgD30% zK~(@0GSCHFeyDnNpzDe7zQ%1>o_5$!idt?Yl#HR8NU6E zONny+!b$mif7wi11DJuKdSD)5CkpC;^8Dkk@`~O^bl+dQ97|Y^X1DE5ofPwbeDQ1L zD6o%xuy!jB*jfNh;_dB4!7y{&^+K_Gb&V=cvDt)GHbnWn*jdR>jqx)}7l``SRwy72@o3oN7%v&J2mvfa)Nh}u61awyM#gpW1=$^aq^n#m&n&ejv#fhq7}HC5u{YL`1q`kvZ~?zbEgV}C$$ zwEH_V-omH&GZ^k4m4u%YXY@Sly8BCHzybmcd6bpyU;dG7+0gYX0i5H7Kr{&~CCoe7VNCEh$mI)}Bq z3p?0W8czIqqyvHbox{FF#MeN!z}w7xciVCIU=JE=_+-i{w*Jsxn*+fu&95VMTbJtb z|9<;uGwdU6X9xL_M)rh>@uPbywT%mZXE(xtGQbEZ19Tu|K$}DW* z=@}%pO<_dMOllNc?@8MP@zfq%KAaGF2UblnKd2VbaZS4Sm<;S=P(v|-BDpkj?k|}&Z~7Afa&(nrGdF3lH-2p4RoD!3&(Hm>4{73W z4OZ2PcIIN5AE%_IA`?69VyF9WdbPjje)%A{*j%ujnT!~pav%H;mh&7=D@(bMuk9Rg7Z{7`pK9McXg4f<{sETFxR6u#Lv z?7H85Y<=arKqJ+K=QLZzBNrz>o;i4IK6U2&d5@K(U#m$$A6ktrQ7GK*&*Kp2lSL)i zkpMNd+$jZf|Hu5XBpQXVm6R;iwvhj!;~h@kaC7I{dK$7`8Me_ zA$^!&_S>WtxYHaHFIu!c_RD8m@nmo`1t_X(3*e2f)?#x9(S7dWd>8rlC8V}o%ar(1{SZnow_(f(37^Mm2pk{aBN5#_`zvT&i< zjt};Is{dd;e-m+C4a-+jC8a7^N;4qb)y5T+4h^vF?c?~wS@PM^)d=3dhRXhk-RV@R zH#;q}OBu7lwc_pb*8uav`hmsL#jEd=6GOb+G_qm$8lUnZ^*(DbkPqELv#lLZPazAc z7XuWNe@uYkwHztBM6Nu__?7FMDqyrj(bWl%m*du|_pcVoUG7WvCw~TwT=AN`;j3&{ zB>D>Uk`$WsXQUFx9aQ)(@X>B z{tvML3Y2e$CHdsLH~fqDx`u6l&bOuE_k^^9PM#Dl+%fzD!)`5mcXL zovrS>x^Q#2m&r>xo@kE359{0qAGPD7?2ZJ~IV$#I{W$oRxUZfj-I$ZZc=BCt7s`DO zGaHA=pO(cc3Av3vWf@svlf_=hGnwN_M3}wMOi<_BedxLK!DeLM4vrny1!pazmxRS- z>N`urg#!aex%UfP zCTvC%R~r<#MiBtx(DinYhgo-Va!n>G_)9ZyrW90TQZfgncSzSun3?#QI|E<)Xc|1< zd`n|Xq4+;91nDg`t0YBSY&`KIcs;xqGnO7s-XhYOoneJoTz zU5(&l&Z>(J^D%r@P^_Mpdu{#O<^wsi_9L3530~*o&bs`VyvSoIJtHw}kNXW3t)z1A zqvvsX=6ipowp|wF4zn4zfQl|@ud4yuFdBFukmXeD6c9mgy#3sbFM~$fl+uK4Jis}S zZ|UL#THrH30Y3Awp-(QBC93t-M}-3(?dpp#!SZ^t4 zKc3vWD6;CwQhj6IEu&!5u8?fces z)*^A%_R0uk1O41b)2VZno1L9FI1m3;p5yDB)(g72e@35tV zP&3lc_K{+EZTSlQ(LrgN^8q&sGDJ?-L%Imyi82OfRAg2(5i!t2Dm8um`do-dn$WrA zjs40AnaH6X;Wyav{P=PRD$#B~>IGIpfF)BBWQ;6W?{T0FUzur#W5Y{v_D}qTFg7S? zZSvk&Z;zj%VzMH|Z`;Qz+D%){a-i$4Bk0ms0rkk>0qD~ETn7rW?vzs(R8J`&vS^Sm zDR>c!Psm-4t`Il*r_MrvJ_A%hD_6*S7&6Y6u^mp&aO$jUUQ)E)5q~7bO>#a zh>oLU3)@LMIL2xY40gLw?Pb_VZPY zkL0cNVr@3IF4exLJmj|W-ni}h#{PlxlIVljdKF%ueud(S4V!=)LJPnZ8_a>|L5k|FO<`oI+YiH3q3P z8t;~~kBw$?tW9jRcxf`6(NZP#F=1nXRCygO9b4mlK5K2$3vZKFVJg`?rcRlO>7j+K2#oujybr> zjnsDQ&!TA}lxeBQ;2kRVr?Za8;$49%N|;jP>lfSYdJ+XB4;~TUq3@vK5ou*~gSEtx zT#@#JmJhN?=;--6;RdUX3o?`|H{JS^Pk$d~`J0bVta%Ow6pJQs+I`qT##&#UTZ~rn- zS};ECZEALL9M^7u+`p$WCD&T(um)_U9%`&@yT)cUV&-V0rs)Lv7dxVnw&rLzu=fls zlqkX6H=%aBRUOLuckm4`M1ie`ceXV!@4o}RWJg#NWGjB@W6K}S%UU0;0F!r66MrgV zqK)UBjIObcrOYQ>T{tvCDhsIU`_#>7=v^@S&Q^~LClK25;~vWu-XDRH{Ya4=W^9&> z+r5iMhnI5?qjHjt48sAD*SB$=3PD-*;5EsiokHd`c#VpF8HB9+WB_{sPWN61^3+<#4@nMd={%qiAG%|UzjW$TQDl5R?C%3FIRc@9eW zvK4iKdfpxC*vgM*Ui@W@INO2yiTyEqTsZJY<=2TF#5<$5YpIUQl%KP@Ro5(u5 z`5GpOwBOh#cPJOD`o22kKGpr#4RY>)LTFO32qRMXCAo~irI=iY{R#!M&~(p}{w_0_ zW}LzW(qJ%I3WjqtD>ny!%kuaAtuNOu9V1r|S{|Rd%VFklTxLkZ&)nH}Z5h?wiad%f zQIb8o2zf}~;YU~V(E+Zc=lc>Gtigt^(IntmVVgMX1M#$)7H;dF6_hd^{*Q1Yu4M!| z-l#!$=tJRoiOvYbq&aN;omg(s6&dZ&n+&C%=Zlg z>NMwerM~?+W&Xe9GYEY5;h)54dn6zKHu&Rbi}YMMp1V$MG`No+r8j6vJcS?gtb+PU z-X;zDA?6v#g`f4JEs-AAb!;Aq9;^d^lm`Gvx%~r3sq}-6Co=$$QpSV)>-e&p+A-sd zH$-#|B;$un5oDzJ*W14x*a{-jKI!|tTOCkdFqcG`70#vh7?b-QrncQ zXe_`!BJ6X{4gE%YJ)hUAhg%;=^D@`agZPG%TS|;gHhVZIN2?#2*IOcjI1Xqy+lg8;|>7&Sjx|9O0iJO%4 z(Ws5W+Jdf4h7e~7*XIeec^VR@{ccEhvbhO1?zZz(*c$CB3)XzCQrdF7qmPD>OHI z|HD5ehoE7=8Z?TZQj2#4`?mqy;Z$kbm~A^JU)K`=mAYBM68h_wDa|XRe}+BCSAB57 zY&-uFI@DJMs?9S2A$;@7|2v|G*$m9@{A*bqUSLq-osOKoC%_ z5LY|xq!l{)crl^6qpdrLN5h=ax&!s+=Qn!lS9WEIKIJR{&!Azw4Adk`6_a%8x8^SC zJ~-Sz3YzEWd7zs7>`(5sOFGwUq5yzj4%Cocp~jqzsqxu!_rLiIj?Xxz$-k-No%uu5 z(D^e9!3f^{q_7N-%wM< zwCg8J%(s~I(Nz&h<)>xvcO9q9^(M859qGZ%v?wYnK&RQ2ynI`PiAATw>}dD8?k3VRN(G>J)D4(x zjuvCi?I+vanQjwuNvdBKnfG`Sote~U^WEflL&Ol6a&*f_f0h218#R#0!Yw}<;rlG} zR!F6E7I{06-9X);G3`7*oN8;qy3qCpYR0<)VFFBM0EQ2|s(kb}i*qTmzl&5|%();S zo&NlXh~b=va{jgxjZn8>mc3auZsKsxjA91d~luibROG@T>0jP0VA*fRd1%!ph=~_+` z!vJQh*mS3QJMR!$e~X9m2m%IOPdd7dsK^mno%sCht!+p9O(_R)c|?=W^8yiR0lJs7 zr-U?>2_jQh|9?A8Z)m6Kf&#y&eZ`{AaPpsWqHKhLWFv!Ej|E+(U5(O1LT~zn0kv0| zxN~n0Rm|YoT@}y@sA^n@NRH_HLs8a|AIEn6Wp(t^y-Hk{!!ty;n9&VNF;|ID;nOkW zH|ve){?vIflQEOs$Abde)AFa}PaB%zPi0XMeyoQY|MKWBa+vPwFgzfe_5vID@1h`} zQ;%YhMZnpdxySB#rGd`T0AHaJ#&7Y!%@{waV1IX`+oqR@HoRE@gQa@23Yc0ppj?8L zwz2P&gL9ZSz;`wpw3ZWP`11>^a6mL)cDqN$&tQP&E(!eD1tGKQ3}}PFJ$g=6K2emM z+SuF%EN%R zHiOmhz;c#);`y8aLIqL=H%A`#7#6av>gTj1ik9HNYuC2o`(jK|&e%b09#CLlcCi{) z*NqZgbnsPpAl8ivSbs9!tNco=9=M>}lc}f+L5u4Hz&2YFeK&V#R9zHJvxzSyuRAaJ z@|(loAFt?@dX&w3fbjNMtYJKKIIJc5I1ehzQ_L272WK4Ma|#@3a)g-M2N+Ge9#X>z0VhlnDbM;EX(S6qyl~G^hhgGX8jO@#7k1aba?#NMLPQnA zs9=PNo8~X&wq7W(RbS+CJz- zndH3#EthgZ$DysphG6Ti{eEmD|R{a^GQZTc8qXRsLJTr z70epD0kLgZfsx4K;#akt=5fOhZWb_jGk^Dr6tC$&N1f|T`FR$erct-LxA|h#@1m)| z`!c#~!xE}r&G(^&oGQY~j0cXnt;QNp*-}D`jcne8_)!rMKMDp}{sGMVe*%0Z^sOgP zom|3Pa?$4VNndfV*Q{d;w-YkeN*Vamcsju|fvX~kx;Le39(T>GzwC&-uXTS8R+3(? zE#lVVYF9g(Lopi&?N00ealYQbDm#XJmGCog5}|RFpGj_H^p#l{bO<`5Wp? zG`!wW9FI#9=xz0=nFUc->z<+tanPH*Ae3?UgLkE7Lu7e@wG%g&oD0pHntx-H2g2ws zMxPQR&OO;{3Z0cecXLa2IVcp0O3~8NGoM=Ybrv?OP z!A{y{*p1^@MW)0j?B^|LK*QUHmHZ>s%BdSg9al3h&x_`ct*{1e%Lc&wD|%%suCK>T zxUu~`Gb=)ZVmiZa5Z7BepL{Ww{R4TYrlylLzI>Rq9JgT z!h?8OJI!N3MY(y4xi*r);dV2qw;c4yi9Dq8m%KrY-pN|TP7v}x`%kp-`yj-AeqZf& zKYg?jcCGS^fz0kS`&VQ|EqdonwsWd3xH-4r*|F#CN3p$r?$pvZ&S5|pXoi$>M>3MS9>){HRqN1M zIOoGsZ^4%;i8<(W+>Uo2VdU8?pNKu}oqRrxkndwBk|Ks{{3d~Jzj@3K%JU0?XHCkj zerhHMN*I4tnYm7`M^1Mg@FCdy!hf4G^Cpp^`qd64vkKzk%WY4i2GiNT3C2Nuh!F?} ztdVCE2EUSG1!3{j+~(WK&vJH}yd+qI zF-6KxlPCB5RzP{{e~bv4|1lz@h`#(^xvJ}8?ME1N?hj=E+a8+(b zT3XGf2O7UAiecoMIu8#S3MAJ#`KoUx*P8~ab1e4nXa5kU9`raoJM|??(SR)h2LcX0 z$#8GfF@jFyNmny?*l$WQ|6Tno&g`59aL{lNYeKy@*tXAG)R9j9^YIvpHElr#{rgY1 zOruL=g&x+DEyAvks8B`_CfB+isqTzMGu!x_2^-gi> zYDf2|w=EqH7md zN!~J8eD*cYpeI~V>c(L{l<2GR0KOVAA3;aK25#>E6Vb0io2B2nE${m@F)6OFmH@-U z^8p&KC0j&;VaB>5l9RfvIkxe-W+@l0G9=ch7^*9oG=3OhxFQhQYkJyAJvztgflh&P zGapw8MO$qY{DfHfq>&i^Wbw4hsj9&88xfzgEPo9(tuVcs9B~VIbc?AZZRc1H($S*f zfLhAmV;KeaaL7=9QkuOf`13K2M4xr=A-cx5mK05(iYB*Q(>*8q`)H^z0e;9t&>Abj zNUWoDV9??-ZZ9mYA2`4w&_x~Gjl{+0#rOx~;PPIhrm&zTeQqCJR=yzT*&d&3ASyD| zMZT6wW&C)o6z=4(W2uT(|44R~rH@WNF%Q9yIbX=n(X)^dpn_E&9bh)lj=U0=vT@cA zhHX=u<5$Nm>Ga^<-UmsMKQqTI%9iKgQ&6P9pvG8W@!|Rz z%>HK9mOn zZ(rHtng>fZxj{zPl-;lAHR+XqbIk*U+XPL^$t@_k{Y+sGGQ#d$$y+|tQnsHTlNELn z3ej6_#xj61HvEdR#Mi0i<;1N3$+)bkYTJ*+ZY=GBlCfEaM~Qr^ReVZ+E3%5IA1U}~ zA{%~H*gQ||A+Rv4u#-x)lu~Yd4-nGsDBWc^IVMt@-l4R2{@k|cduTt&u0`> zWk{6ZzCg~xKwhz)eq?oMr*+yhc5~$6o)g>}bk#$2Owg_TvzZHXyZpR!*jDVFxTWdk zT$Hl$viC(sgP7t@zC?;3xgjqS^1aQhkSjUaLu1vMM^`Vzwq~qwZ<0s&e+{-yCp)*n zJ>%7e;Y1hN4JInR@T$*9=fh|f_0e?M#95y8r!j)*#?eh0)ru}9MoK~&twDWubelK} zg-BZE2}-X=H|K{o>G6N3MBs7)r$q8}F*9*y=}1v&No3ACvyzELuRP&zmsq7HVYHl3 z%sTw!Ao7ewB!Y=rfRj~Z<2Q=@K-S#B`P`R3XyMcAT9qA`a>)qmVZCqIHk=AEb;ys_ zHs8Ky$W?5)m#^%My7BaG5<eVoKTZo$jxe8L_y= zp3>nQlNm0;woHqzj%;zT>Cqzm#nPaIHl!ffSWl?(W{%PR3XR0OFIn*g?r-f*9=>7) zDGv`&*8nJk^7%7uSs=homTIMDA#mJCx963eGe)>YOmLIEgO)?DNO#?nQ!ueHrXn{t z>-Z&l7~Q2_SYF^|QJY|2n*LZ^jz~i-lL=X|GP)e;z^%XW*;R*)iiF4N(Ap3(*TcG` zIRGW^|3XNhkMVy)juUan(I5^v6_D?x68K&WzLYHqTw{MbWSMzXez#bKj68P69)t>p z`7oNT<0)Ie>Jr)Qko^xZvqGY$(A9<;9O&wZ!1W0_H_>~n7AoB6!?8c8nqDjPWB1kV zla>eU-`tF9DJC> zkd7TWa4pps+Sf3o3EKr~ro_5QK)zqCn-i=$y0|fgQk8z&Yah?mk$MXO?2hSG_BvY&oJ}MhxDn})p>%_6P==#yF=-zFSs|h`ROfV{RT+tn? zoqFqut=+(6Jnv61Ih#Dq#uom0A+{?f1=xj#1UI=T;Hqk|9+XKbkTxOlr>D+BqR-8q z3fBW}MqGT-V*6Hiq67TkDN&nHgY?~pJoEkPNqjQ=R&BQVf;A03RL^Mhxs+!yhf}(d zdbeJC%@%C^N(<~sfb*XoRomp)%|3ev&QD*dOwb`+zHtXm7-Hya45Qn|W?T>3zQ7Ou zR=R)dW=SF4*}iw(iBb)(_;ZaH8N_;efJ0s^oq2^a3J#9hp8l^ItEF)~!29@#aBn)s zB`e#0z!0$ErICga$*FgxMcr@I?H_Bd9j`0-?x47r`zoI}; zXiM{>)E+SrH1_+t%CPqE4n23-qO5;|{ZGDyae>hweH~pHb)3Q};*pS7HABt3T9wo+ zDO_Rr>w6>pj*+#jFv$;A@(m>Ala6|?v6Wk^2C>2=>8{wE;IWXd^e+%fyyr1fj7-A> zivIbTJV~f2Q0xn_R~c+fzCg-E4g79C;(r3~f6BzFLK42Mr1wGSeuQm1qS``ke)hSU z;T79l5J9X;k5o%Jb?`^)oBu0r*K<)%gvGv$-btS(e}+MtJHj}RZ@PrbN=Q%FN6 zhqTMth2b4}6;c-|Br``(_U|=!OtQQ-8FU$S6p>toLY#rs+QQae)3B~Lppj_c{aAHN3J{g!JW)phl#_>|`{ zOFvUod{a}ezTBJG2fQZKoR6>W^4aB8O!Mm0H`AIZpS|#N%Tv?D-}m{9egcUQ`29%q z8tF^8?eW&A2lwbMK#gHo;Cq3{EUl0syA1fv4lY=3=bNMd>JbwI;!s|@QuDQ_{>S^J z&czNNwvF1k{k@%E;&jeVOZvYdsVWxXJWJ>+PgaUgu_KLjkrDBMS!<&ON(Q6^EF6ti zNU=uDV&k?yrf&G+(+BU4UyF}WIl1_c&=7zIKWjs1PVE;8pE#8DkcTB%-3ZJwu6^k48_|ZQ;Jf@PX zdzA#c-LqAoxxT-Yz=Qza1K0ca)2PM!OeR7M@&UPd6cafhYHtd4u#SWwvOx6g?pLu@ zmE%W5$Djrt`ozD~JdCESP{)7f$O91!65NJBaye}kZ5xcKJjRdq#hqxLef$nLyVYH) zF_zStuVU2Ud}|64WvH*okD76Zj{VNp-Y7>|m3#dLeMAsV$OOn&?CT+SzN9~XiZ$8K zF#ah?(0rosz~k>J8NokfK-&ThS1h36>K?*t8((0$N{LnM2|R6!Bqhy@2?X&rL%vUk zOoH4ll7J@$3K^Jfsm;R2KE2b+WOSSta~)Xd0q@i!kEm;nXDnneGdT$2JJnUL3|yLR z;q6e37LB7^(}vpSGHtl8#jSt8zsf`RU1cU*=?=KEDHM-jwJpII>^h=S9Qi2g!|x+* zQxtasJp3KIZ=x1!M@7s1582$FfYaeS^oVe`zI>_jFULmEAOJ*)zuTYxgI+itgSO!J zrrY+_i#k>KKi`O_Lt?Ylef(CDRWybsqKSIWy)5eDQv+mqqd<)NURWo7XOj_Ipw7zc zU(K$u_YSvd^D+2VE>`Py2>a#NFxfT&M-zpKf9E5+STQQ5V0>JN_8Zgx>g~({pk?`y zQ9$-{_^tM$pHCWY8M5$J5;2#2;Z;d*CbVJluXOrHazg3P#!_2G^W?=RE?ynJ8l1^I zt?^ioUSX(Pk$KHFB(FTjmHXmQ_svM{FZpa=pDfs2Kc^j^=yVUR(^zcP9K2U|BIeo; ziye);^v<(Q7e{z6$sy7$|CM*sr=5;V(+(f{f)?4a-eii>v|C|&t`q!aER8hStneMy@4yPIq7lClzRIOz=uGgSI3 zP4XqkPP68fSvS9Dgv8JBq!`BYm$%hyNb`CZ{34Vjh1vFo_Nx&EBJ)Ekd@OyAE)Z_< z3Bnqu#f9Blj|_?z2NjE>{-CK2YN>L9umO|0@3pt>i@}YT^rx1nJQzXBgJ!JB@X!y^ z{opeO@`N`+fLBFkWKeXd6D>KVD{eV!`}LDnWDt)?EQRXOb5?2Z7d`ajB|Y^r&lGoL zjBp(To-~q%2j1oE=|5WZsr0DmjAbNosf*EbDQ%34Zmqv1i#jTAu&upq8%XEcIo1WI z>SG$i>UyZ3k^)r;!-<`LMx3E>p!P(5~8c+576)r%DiWgJx1y^?93%nVLtdU?wnxN&G0~b&n zhM+bp^z47xs)~BV_q*@k;5m>Ar=HqPS3ToL;aFDvsFGCgoHi3e=fwZA{A@lt_14YP zFqsg}5JCVCZ!7#$5mJ>xp`($OA*2Wlmle!@M62a9V6_w*hUbekaO(pekXN%l(+^9Tq`Nr6t2()0fi1#9&Gm&PDgbS#kl>dhW{z4r_er#<!l-G(peL8bB@l0}at4;){T3<pRj3pe$K{>gxsAjaeRE^}?u6u#;&_L^Y)_s| zVP3278GXK|fEhf+TvPq@s-BcFV%jukgzYWL`E3ee(d(QLnGhM6(lI@>F&b|3v^mq< z4ML^S26w0a3fX_%)86&Kx^Hx%Z?w#SRCPm&-n;EykVt+eriK=rfg#jnuchJ_wHJm- zVpf4zcDQJX8+~h1mn?Pb-=fz#wjgM%t0T(&pIb}f%S~=#MWx73Am!XtKmL5Z#u%V2 zAme`4V^pf3CL*Ez}2K1o{B*|5g7j zzNlS;oCbgt4x9!lliKe&oU}Q5vR)2@{;lWjGW}8E-*8n?C2pAVAPKsWRr(O`f@}So znAJS4AFYUUw;JQwmS4Sf8UICV1)FDK#qoK{|HX0}gq1!edrGE}3IbcFug*26xjT%G z%(~b9m2A|!T9?kq+Q}?919DVXhoO1sBNZ?bivJYNxX8}xsAy}R$2}UzoW8mED;i;Q zcf@o)ahKa;0eIgMkCjc#+F-jC1`+ES^C0(A2i(9cYbUoZs+}KwD4Mpt`N1CHSI)W1 zcIVN9k6?7CX@uqJ)==$XO)V3ksxvP&@;hMVp?OLHJ-QcUa#goBvxVLwwz#ggQ1U}e zVk3%h5!t%AYCVfwI;enoW$2A59~a2|${{%I=lJR&He7~&P#T3;x>D*Rh#QZ9XT*(% zSXx;Nw{g!il(HUB$%=3%MNo+DBHv@S z9V|A+tEtw{hj1i_{%A+WUp#+mnQB1x)Ii~=4c(g zH6HnlUp|(x%%@);OH1q@z=}6C-o}S$imD3zH5i#}iXfSKt!?-b& zQ&PX|p);6u6+;e|rykUH-yt&&ROB;5GZl=O3*R#Va$#i0P)A(J3x>=aM(CuD0C+BXfZX z>c{(xdGph4RTnRr$V)#x_@|E#o;pusnOj3&FqaVjYfCjJfzG&hV}d z-2@(vCDHk?dqfGBLM$!kKc5zKb2j2me25;HD^O9z9Q@6VV<))CXyd3}SpU-@(~}d% z6d@CT;UeoTeC6%BVdOTQ`sMDYtts|a{?fUAwxfN|Hx)8%{6Uu8aLm_c4{wq{6`?*3`YzM}mHY8G}5jPA{)QpoYVPI`k zo*S!62OsuH zRJTB>K>lIMm9Ni;5T*(0eWKTZ5Ak%zHdX{QV1c5~b7A(xaTJ*1K&eWNrh<=#00BqA ztO!_t1c&>YXtH2V;gh8oOhpT=7Rw}pzrNa0o9bes`#!OKFsqy!v_pP-mS;4Y_VzEV zfhwi`ZpK*ItE$Ri8?n=VP)Aiaz}Q@|O@6Q~L>iO)j_%11X8bcR+_l;chJ5ysDTO#F zHrc}hww#(^%ZV=t@NnQ$Q-vzOK7{JgxRTy$E`}@K^M#c;m^-eD{5!3q+|0*9-=)WZ zrq2%Ow2nLGb>{ZNh17%Fl|r0&TBpzF-vyVv;`6F9h<3(dEK@=WAjo>z5!c4Lr`DHC z?Wh(uNS@uJHHozxtIO$Nv#2fMEkIe7(v&uuw{tG~5ei!72=z~K`qJM)=RBYSRcPkA z%iLZaM+R(Y5Wh%UKn(|9BnugfFhKW6!r$W%Juq}aoa8`nP{3IRHPnUz$2QWY({{y= z{W{$93huI4*0{P#2-@$`R^XyUuZZf!6w(L|wfB1&L^ie=Ut=zKKBMpedY2J^cZ(a&0w*7flQqS??S`RiBakH2mpmatXP47;;Zv&jMTqN}+r@r(Gf z0$vMJbchFoZ4W9SSY{i`I-@cG6{)ErjK7A+7cF9)yTbm+S!yYP5535NRW*!x*bYJV z>P13y5)>S0QrvMmzKyigxGjOx1Ttxw4iRm{mBiFE9*`nDqaY;34ly;o z0;Q(g&>s0u(ytH8yPJk==ATH4`)nwT35pKEfBK*2nKo}s)!)zI7ffjPMt&7wJ(V2K z#BkXUiLt^kWti|kD_%^!JP$X($EP8wGR@EtM?|MPw2Q}mJ2eyI##LTe5|i6PD^+sf zegFOOnz(#6O-r^Uz|{MTWr9b)dQra@XY!>(tiR)r-wmWEYs9XCiRr7%3UQy}sOhyG z`XqtXHj9PhZgwz}hJdz*mv_oVa|k9(SVeS!P~U9An{noWu=HujOAN&anitE!M}IG# zViH{5Yu21Pdi)HVMX6?#@Cq37Ks_aRx1LVT0bFOkcOsL3RH;_nYeKC#Rlo|TT!qP& z#pTaht{Z*xtA}&&Sa771;BMCLM@mBEB^#K~+T6(Bn_uq_0F8IRO8C#OI%0xg4%c(o zP9^p3-A- z%UbHQR^I?;}&ro(~>(jt=trMrt=&zIA;V$2Hu>7zmq9QD;GxqxlF@pjtboSe59tXBMt7wg8qT!y;cJ`|bm&>K$t1?$0h=CfY&T{`9% zo|Ip-6XTKRFBP2b=otJFt)Z6awd!N?y$|(Jc?^&#ieUofZ6B|yX$UgBCIS}!C-p)d zssK7DDAB;}+Vl1J=)zBhp9%m9I(uukgX!fby*9BbO{v!%YQ=}Fh7HoH%d~#T7TL#` z`f-DjUR{Qdh>9#Vi!fQ0yM<9IpX^z=`BPTOQ7T5p zJkg;iBzbYv;rX9neZ^80G>_)6ALdqPVEyDj{a8$#u8xO0`;Usuz}A(-CeNlZF67ih zl(0v@BO~7DU{Ccl7HsTO;~N&+y@7lrd62CH*UcFv^%YqAGUlH^M^FZwX|~KwT=5?#OWm`UW;66{q4jRPz*j z25PG~IQ6MJBbVwnpe)g6d?R-af}RCRd_nQATzAd5YcxO4xay)n*Rr+00tqD0(=08M*NlF`>!TYn?C+j z+U7o@n%{*CVIu!P7o+^L8#YyTc|kWi%dOkj)Tcxo2{+x_eEkHkUHy^q#ehW^ncC>p z2yorS9adG=I86Q<+lzZhoLNCFwFQ`2X?{2$_3k}QuNL4_eM$_tjo}2snQ(8+bk#>! zsPfEZo=d`$;@sr&HU=gH;eprv22GbW_bUaB+v%^|9*+k+e|3$o;8OWY9*3P31xR)X>OC6L^cs@gy5z>?f8);uhd1jM7d|eS^7X(4}u0it7jGNNUGeXpYq3oJQ zvcyYs=pNG@l=jiQcU8B?9 z1^r;Mhz6{T_K+;oARE(V8JDS|U6hWx=kS>cWDhMRqb5Zh|AKU15 zBWpSsb=T#VgkbQlLc}e#)so7iR?#j74DS7KenGF!=Uh~^iu!X}i7K}o_s-UnqgxQE zqZkzVfpNa=vIwlMe0)BWaiQptDQ0n{q~g=c(mD;&=#toV)zel&X&Kfprvw4~INMpU`-Ru!DZy+S4(W<9I*xkLsi#?NUj;dNdzj$%%X?F^ ze_!lc=OE3x|msoWBEmnR#kQZ}+54V*n<*t~0Te!!I%va=!B{e?@8Pcf^ z9#T|>$)+~CRd~@Z@JUxgk;&7~p(V!yj+)e@vHVA1qCQQ#0N2zo$Ov zU;L84q7Y-eMI7lpK0@6Nqg*S$>^4#LlcP|A-Er2HD4df!MrOtrcN6iVy-dC<($Kx$+3Xf<7) zq$W6#?=uxrTX&lx6w082&Qsa@gsXH^1coGTY21a%+o}o&Wiq=tNgdV%H{2mZH`01w z34O_`{+hn0gpqRhZSXFnu^su#cys&l!PdSms~~80@q6}%q`~~0L%KPE5^vA&c<+!20pmj`RkpbH*p^=K-kAkOgMzDCG=9+GK)~kPV=Q}?)wsLs#>w+Tqv+AKj+Srlha+lJ&6kj5L zODjg*uisE~p`=Ih`Npw1w1W@Zcx(L>{`q#wfmyHL%L^xe>&eNvxQ0$~Jt^{hPeIZi zPR&IhPu_BFU#V%oA~C);zgnQ5@aoTw;-}A}VtFz$`QuGjCYTeYA|k)}0}SW|lSmQ_ zz2`YS5}(&EI|%7|X0q~f%C_b>*DC4~Y0_Ut=0ra27T&2kT7VOSY}m#xf@;6=+aY@@ z=O-Qtr_p$Y<9vliTaGokwLZ#tgyROulSkdIylL4JqJxbyK?J<3nmwDxuLdbiK2_Tn z)*)pICatN^np0yXl5zSBdEAT)dBa7_7vi)O(%hpFWtvkc&KoR>!jKHYlA}p_ACW}3 z*iQJC$Pd^ZK#9LHfCbVqc;Y|(%LtwU7MQq}A4JLojBC zB+sG9P_97kElm%<=h^R4NGS43%)j@fCg-Q?1O%vj!1_f%v;+;(dTDAS zxk&yI^snGk=rTiJ*XH-J+r=dvETtT@w4*^PfBX!oOXS}Q&QZUkWJ&<1qB&G|x>=79 z?pa^LTSG{)6T0^=FeI8*5XJ#Hm7(~6I04u4@qfh=n@9|<41+7u!N{WS8Demla85?w z2lhxym^GFE3&@}V7PB@kRWXCldEcsT)7jDvlFCyi5;%D`Cfi| z4y?I&p7DUMU+RdRUPB>zn8dhK3*}ol~7{7QydfWJeE+fKEw%YVQ zW0?nBzruv-gv0Gf3HC|&EYc!e!#h_n7wde5JFs*kOJbmBGeWy4j@$X)4wRzPj}BZL zuj4$aFP;Q%|$OIqA|V~wm(R8hyU;Sq&`kr%1Cf!jEKO4>JJb;?Ldgl)ltES^f%9dA0<>u&5>f zaCF^Ii?Pf}?I#%PG5tii zJX9}J3ht`zhrV%fLeGW^2_aF~QMhg{Qr7QrX=3M*9)mhFqMvVG_NxYV5MWK@uoE~{ zmu~n2sGTt&R_)1$kais|_1*uC5I8H=V~(6r1HHYLy5hmp={qfUh$N*U zv32sR)qjsKn2bczywVbskbm(Z0bgKL(?s?pT4eQxNbLB$M?9ZV^ip|i&~IhV3!E1W zdUTOC<+_F0QN7q}k94VQDDmXKw2OxLsDu@*2a}=9g$PJkfi_8I#Bn@{asSb~l(H9> z(Z$Lht2#dee$uaw_2S~rO}`0^H0xLc#yRN8W=t8ijc&X>+Hr_n!TSRQpNN>(CKE7h{tWzsMC10m78P8L@8`~#D zPLO*;nF&|yi(Pl)i}_>sG-ECfrzDhvLI^5Nn?}+Y@Lp4A4`QwA@_^opRrt{;1NJ^t zvM8>LBNa?gpa*S$*n{>IdQoOMSs~56b(G~uFG)w>WE0W#cmEE7R#-KE*#d7-tr~JetfN%lic6SRl}cHI!rdDoXt| zy`XYens&OYwi-6uEpIN?HK>CwXqzozqn%1|uB3>lt z@Q}#0Bzj(A={UZc$Emty*xXSadh5a|^QYT>*0GfwrOt?yn=PwdzabFs7Gi;6xbO z>LXlJ<7ZA8gfpzh6%?--Te`Tib#47KD((EwrxbPEPOPv1KBb#U_?53vhy}7K-9?Dm zOTz-!G(U1IXjxLL-a(IVYP3t;O%7c_8KjH57CIFj_*hfFD&fy-idEZOL0KV-Pp9gM zuci7UY(y5me|Xp@%IbwDojngwW)$L_wml!@O9YieIx+5!Y;=RA^|<0!qb3vdkDp#q z86jW(U6P{uZoBcl`R!*S?{a#(tZJ$l_;<&cg z4QxfwUxq}4-?XsVh@cyve|#zEWjfc-wxS8G<`?LVZx2MdWxKp^0Z3D@=X?gcmVB|& z;=_3ukpq9%o=BnKSIwx3!YVxV2#)=t1K8NrstCfBxuN^T=`& zi(F`_qj-!VdU3Sq&(JH0vWEjqm&YYtfS=nvx{LUr7ejmbC_IS0+ZRBk5+M-G6eQVN zF6W^=m}>~tPh@gcO6t60Jt8^^(q|gA_Z7TjJ@YbC!ud3Je{{{m&?L1np6se>sq^3_ zRE_Q{?(LF$LzO!EVKCY5G-XLx3+CVHy%pMVMxW8tFLXw-3?0XOS8}C}Q~03sJ*#u4 zbHb)Y3Y45zAh2M`>65~k`Y`G& z^ZCRYrvlK$bsFk9GCAeWHr9dF4Ziyk`Igo_)}Q$YL18jV-R8>Q%2sr|!E;M1fi>jq zAY&f#Garkzghv<-iM`Q?Yx{C9-M^2&j!)^*BldENfqi&e8S4Y7w3;wtF}-qp${+D@ z*aS3glAwilH+!(89n%x9@VuGq!SkmYQQGvuDA^W1i6ojXlun20QqEm-G!~B&arne` za@IAL<{|oVJfC!U_;aFOwOd+fD3fdCF4?G>p+XotyAMoU>x* zdC)eVDDjiB9<*^Do~>~5xsflS7>~EB!32DD7Fl;xw67t9`9`#WA>% ztnuZ+2$uMHf09yQ>f|nv|D?PC(EzHWN?o$Af{aiuRC8Jk&7i&>EVZO=SjZP&Qh?|f zo^9ZG{c1Puq6-u{P~_``K1K)OahCSgy`E^#x_N63bcgj=8b>VEZw@0^|8M3(e?ia3 zm#i%WoU!k?tfCpudJF=}K8roF1^tAB!D?(l`cW_FI-H<%Wz*6M)mZ95V?0&xLZ7aja5t_Uej+o8I(C;^C+WofpQydmCko{hZS-?1W1uYwW{RmzxDHrGg}yy-GmPkVi%l6-iz4K`gZdhJyWe)$kj<#)3Xm5KA~{< zCu(cAT!v+y&(e3%>rq?5HGkU40?f?7YP2zLsP$`Cyz%bSKLBZdX{`_6#v7J_Z!2S= zkW8x#iw<><_~C7OoAS{Qjz^{g6DyH|6#y@jj7udGl^U0q3LyEJdaOykjB-o7~dVKUlb$Z zzduCKJ!_#cTZyQ4&B2ts_YmGO5~;RBV}ab$`lwNxK@mhi;O_5ue(zoP{x|Eab=EnKd!D_Y zPu`AumT^zT0;_%ZbB$WU^Jh&%8{zYBD5u}`8?!!#4Sqiy;T%5x5Z1zeBrUWoy!^rv zkVh-@Fzt3P37+lzJj``l6-#MkfVRB7CGf0u1F6L~f44G1!)b(p-sB&pTw`V<7^N{)WJSyLWh&iDE9GDg|e0~1^M$J^moVrSgb zYtP@(>34Ab(v|&j&}(o`{~YL#UB%38I$`dIU46>4JId7a+LlP@VRcy93{FXHMetTh8!4cm55OzvYuC&{FK!tjM9^L1KN3~2nh%V12hz9#-k-n9;ezUv zZO;i6oi3AQL-?O*31TQNn9=e!t#U#Z_y-%B8u%0dK70y*3Aj!YGq8IfPGj+MCy+Au zXDPdNhzK{iLC%g_4y|rDa!vLhdyFa8RW!mnnENs05kFfs z{J;iUIF?7iQKjWGt_RW;^XIVT#}9hY?Qe9!H~?_dDvdS7H_=_z9ozsPRjp(EQrFJi zy;Jm6#O6X7`1fZ)Q4CXnk;xq|62^?AgFbAKX_2*l5D$As&TxN>q+^zOw!@tn=DctP zo0tuX=jou_q%UoM9XNx@uE&(IP?^B1T)BOc!5#LhZJ;pxA^rg%1w8=lK_8OpIq(2@ zhEWT_k60)m`f)hiOH;`Fl2P`oj!p64+Ic$UhsE4o&&dPDg~ zRcrm-T35LI?-xe4qsYfYPF1hX&a6g}9HNK5Yjw>L3hJOPMifXJYZ%}iN9|iYktNql z*2O?uggYj?Y|dmn)N#DcG6k<7Iqw6l`ESUK>e{ngau#zDZK}QZSd^+q^iC{o)mU%S zi~IyxsWE-m=MBFiHm0f{=rnryHN4w$AT9tXUJ;@z@y<*F(>X&rd9E-6KtBOyqyW#1 z2!V<|q!Q?N=LSm-)7=lkfa?UkeLuRzEf8*(`NB9&y3qr0i)6ELXQPHzDw@U)gMgr# zF5)8C1Iba_{m#0w_hLb0a*8n+U`-ee4UEdRLhC(Ss684sH;LU)&Jv->@Ee{!)B3Lk z%?D~h8`)2u-OZicL~H#(`%rfKKcD?ouwO7XR|>VDU2ezB#Dn5Pm3Ej$Q(YkSDv;zf z8LLLKI^B9!6Q{9BZe*_`CVd@m`JlEyJY^JmM}U0hh_>rQ`h=W-d1aN_}Tr#^s2 zCE(>wiRVi-_QA(O2?jkmx8dzYPUQ;lHw;pLe)~O+&?D%EwPH_#)x5L9qL4(f?~_J0 zs%Y<=6GQAImekNYyH1k!1;Ww%#fu*@mAlx7Wn-Yw3NwVZ|9h+3u+O-)2gY#d3)TIj z>K$h#a1MRp(wR&eCNL|UP2|vu3x1{tS(8AB>~uFzzP19ZtjRShQl>Ce=8p;vCrbJB zB^+1x#4%Ye#<=vg*3!3q-fJT5y_=) zc|#glXiMIHqAWttL^@gyF0pFP_d^LXC0-f*hOm=7hlI=S(;U!3EOg6+xgJIcT((3kM$+J;JBP z1-&$U_urFWQ;xwfWc4J{Lsc<+go|wA#RfVy(HZIoeObBY-v_5ZoTp0Rc0s-}&g}6c zy=JTOH_&&J)7;RxZfLBsef0%z+OV;E0Nmwdkx=`Qy}?g5DTz?jM4;vmG4>-Ji-AwX&NPf#Rvy@=nY(jZ9f9 z#gR8&230S5n|un_L3j)$RPD3O)f|+ULRo5Z<_Q>uD~!a1Js-90DUjjEzC~XE#RTT+ z-2dikzEN)k7^|Lq&{*Y1X3-O0u`M_EQ%bLC{(nOiFCY=ON>1)Ts|py*e8$uYmU^AP<;h3!oYW#& z)N(k}B@zDO=NCohI^VEYybVc9ACXhw;%o~*|Rg^0+6(E_TG}yi7&zJ zK%vlAXh;|Fe;6uA_B~F---aOB_YJ@Jp)g+SWCXNM*GG}O(FIp<%E4U{;?Y!89(0sN zz2S|25^G5o3z{~%%-XMPixKj-;ZmO>v2%I5H8t9AG3Vd}+Plp-ZCP27&G;K5%d=k- z1y%|pA5!E}fc9@dI>~%BnCB`fNBY#>VK#|*)mj{HI_@B{9jIcun|4z&1RATgYci%L zKZoR<4^C^~1>;!(6K)Ch*(Hj`*)B7t#pL;_N>IvNPtWl<_0L1dIUS7kQg~CAGfn>~~=V9z<;hAZpqKCbB{$yPGP3S|BwyUOlXCNEjZ^&wO> zbGTIJb{a|ViF3M+hGoHp>Ld`put`G;>;%il@koNoWp%4noQ>9Fw8cBl#xo#se157N z^jShfXU+P0Kpn@O&l3I6XpKJxJtm!W_F%8dA=dw4esM#^hl8d`F=~_sglE#G6Hcg{ zj=ZqC@EY@}wr_i>r3^Jxl0r-*NDga&Y0E@9N_M4xr#E%X=_Qt2h0t4W8`qP#0~t zFJ1sPDtv9HH7qWbc09$cwicL1@L-iB6ljC2=mSO(AjZFYrhTkEW#yBLF&xU$%O$6u z!7Xmu2jcTF$a$cAj8BBE@MzNJ(p<*H_-4UJGwRdEGgh=0;OqOUMx^#G7W$RqC_&A> zK0bK&%RjR2Af?VU7qfL0rFp!r_99jqimVa`U@DXVmIU?g#X(9u$E^Q>seKTb%8&9P zCR%wVHZC%}k7?F6j=nJ7Ge+Im;gRe&{|0fBqls9gxAZ*i?akMM0%nGbn~7Rf00B(k z0}8yvm>Nr$hPzZtwN#VI(aF(x{as35()FQN{X8*ega#@v420u{&SV z>iPm$oJu7E_h*AL%rMW_0gLC@UsW_cb5L#}j!H!+-rS*GsX#e{qRrs4+AKJF#4$U{ zcKx)gHoRJ*`1`^Lr|Yp=;Zw+e1F4$3AXPIrP&Lnik7Sa8xisve-jbFmg%)Eh_|qzh z0=HeQQM3y~dFAZeye668zuMu5%nL4NI)>)HQRIi1&^JR(JEYjO>?xboq_Avpw8E#R z*B3977fewJ%V=&WXVf+PaS$&!0n!J7@Iv{4S_W|cWZh-MTbWV;V_$)}TaVX3K|*{w zLzA1v{u(;jF2x(H+VCDn-WtytU-_*4n5rT5>_?lK!mQ!s29poi>8;h83ra4_AjEF+ zv;Y34=vdL)I;lbHSX>{S<4h-p5ezUnh8NkxgH=lCV6!p~MP0fCqjzV;RU;6(AQ?EKC`}r!!y^M^}P+phHLy8PsuO91gxU(smA$hKLxBi-j-RI4pv`S+U zMb%w_Fi91q(wEb@|PssZr}Zc zL?aftT3(JX*5MOnXs#_MWt{c@gETR5JOmGY3{)Av0DQ|-{u7uMx9PBX84h#sk#n!F z$?xR7{S!Hf%`5oK)UWu=RDFVu^zGp9%ZAaSYY}^We3Me=yqH((Z@i)~ne&ULUyis* zz)GXl@IBg@uV`SYP*3B*Q{b|#4C)IEgqiaqi7r*Nfrk{(l)m|=DFqP8v|1`;M&WS- zWWMQWp&EX88iV0!SOIIyM-(dU=~F%_6RS3fd7K;U?e*w$Oifu#+El;D^&`=!Pt7nh zjc>waJ-6#_C1I3Hb=B-AzJ7W1TJiaWWYF~H4HCb$oxQJkHYx#16RmNTCG|C*vN960 zvi)$Bpg0{==T*vb%o7N8Kv|=kdm4?ixpa{-<;~uk{$E54S+iZ#VujPo0-sfsoetVC z-!qq$Zko=5a;K%`;QXt&vHe?RbywgjdmR}aDNv@bB~O9NSa*YN=Hi(9+1+qx#phR$ z8+G3Z$y{n8dE#fPFUSqKmY4gpI1(-+&+Jm$k;=bWXz&H&a=^#H_RwGGh2 z84v6!MS=HqunPHjb>|o|ghl~F=U0?+jhWNLc^!0eQ~+ ze(Hm6EAn|pMD{}bF{JDP8y&x-uK~$2qNc7hl zWK492J_46Aj>Um~-Az5aC&*X0_Ox2&B!LE7c)JRa?Fet4&1}VkeaRM-K21PL4hN0S za-HC#qDQPLwY*-A!*0Nxc1{_{X!Q44h||GutB-IdJ}#?lz40TBAfPcxrZ;bMG<@b< z7JG-X8=W^wqPVi9U$(z^e?k&TRQyhh8AwI% zgERi7!;=V!PcVf#7P}2W(5eL3zNg30$UDxo=f0`%I`&SSRH#y{;BNHrA*jmk3}#J} z4@->^vdByQy+d5!VXc$=s7CjTO4X1aBmZbiipQNPp|7mFWlyoe%0W!Xmz?E&y{8IL zh>H=3EVAR{BD673g&`|ak^tk}6Iod*ba_a4u;fQOw4En`|_#8NPXFvnP_rCC(r}EoG6v2?EmB62vy*)10Ok8;0$6ujtmLs1SElzBJ2;IjAHJy##&qw*}tlj zHL*h|jkrzm_2`rKZ=yV_6*wAEqhB< z*|Od*FWv6}3F@+lDQ|3sPJ#Vyig)vnhLQgL#lg7SBd}fd5y6rY=Ml(&@mwiYaVPHrMK^QO^{ZHq*)rQ-O|!cN>1v=yPh~1!46O7@DA1{! z%l)n|-Xg8((YD0fqpT~NS`FU@e?5Y0nlsiG9c3!HtV^sn0loxj1skI&by_O+5_bja zGTmh=vBQ@OM2}o}s$7r&d&*k$(mYX9dwcEe9t(JqUJM>Us5{_-gX^f53cq_lfZpBY z^wFRMnunpNPIQOLj@6fT={t8Ltl=`ZzFvtJZ=17O@{>f%sLpuxkp(lvOQWh~-&OjU z*hwIXs04N`ZCYXdAG4QvyyIHH0QkSBXf9+50cZ99!|UUZDh6v7!r`)Xqcs?_hn4&& z$ydW)*INsl6kdnW#lByw`;wwd%(!4Ao}!N1ro0>=d3(iK7Z(`!)v^mEJs z8%ich@wJM3$)-5-`yf{TzGA?r6|^d`)6{{FG%K(`1HR!%OI5ysx>TcEBzLo;+$Z2`)h!_8C3v} zGoN9*#cn#hfbu_~^GtSNZe({G3nvsW9=DQ##Xi2(9`)cRoHv+2M_VyX{{2T}R7SHH z$0uB+v|C|I zi3}28HT;g;9q;_wj#2*XPYm~G-fTI$)TF9C%5ctahobFPG1nwn?E9$r7pIs?|$c7==vhl`7Kpj#nc?01IS47@9O?-Lz=3Oa5?qok}tv!#lD zR~Z-fZNjyrEHacv)3wI<)a~jvT`cU?CbN06RZg+ur}eY(Ptcbt1Ide@2*%g2!Co)Q zH80T7UrVM7dK&WL3HBUeYRZ~5GbKa7Qk`ynU{{K^Sk?Xg>Kcao_ttPf9^ZdWxx;t0 zBJTLcw~_I&;;?xJ4i^83j9+gve)CqL6c5|d-DQ4P>FMh19-Aiv-aej?WOnk()A!bw zUufnWGG+)6Srf8opdGAM`xO*5?S@U7$*QE^o!D;jX_!BkYiJf-olk7J`-**@LV0NF z{GfJfz&Tgwt;zqf;n_I9BMW8hef`&Jsj)6xf;>XHku2n+_Wu@jX=qXJOlplSQR`j9U(>t?AbD-7? zfS^nkLKwBGm(!bTtrs#ze!o}mnfVoPSH8P2a|`H4lkxh|4WP3JRXh?t0T{!$ObP*h zgd(#7HdQtwHF@oPIG$M|pWx~8O6lO2St&=ax(_1;lJu;VQ(e$@=b19(LDXV`)}K;)7r6eqWqAfXEQx#DKXX+` zg!`Q&(*PKlBn?g+{0OvxbSeBgR&*x0q;RB@+3L(c7`lfhFd*8>Iy=(t`VA-em6kr=+Eq)mlh^xOO3r83bULPsExb(gbbM8WgD9JPo)%b1<3o((=P!$VnOu*U+zw*FSNyjui&I> zfveqXUHh;O>Gaa^Iz35B7Ao!GI159zvve#y=2jY;@RKI{bZo9V|J6{Z%X)jAtwS}_ z!9_oUXdXOORZP`fUJAgqM!%fI_`Gn}n{nP24XI)KbAYeMO^^T&l!rR&Ul`o|VhZH7KvT;h1O`~V1N!Vv?-G&fFT+bbG$+tA#R5@iu z6zA@eTM;=j*KbAV?Tg(ifP3xpo8}o@n2@@Rnzj)TO+zb&PS%;M1pbS92*ma{k}D+V zM;{T2$?VrmV5$JW?8`4$M)hEG6k6a-patH3G%OsRx^AN;RGDzAt6@mt+OXOkW4{&g zb$?bxn)?KbwnzAS4%RLhRf8+(tO(qC9rSa6=lrNDXYs$<@q^D37(7G|Do-GeJ~mS6 zIjtRahu<%EF@-j*8Ep8>?chwnjNU9{_rs6;! z&eYb>9aYAYqdS7@+B`A8G|vD6`b8xJ2N(nkQsW6}iKGGb`NZrG-3Q=XM8$(l%PpSg zP%jmoGdQ3PYij?r_-4@d{tOmGSb`gA#)-G1p21uu{7@U7==is!*OOC(g)F4lgsXTG zk#WqePcVA43y#Q`TFJv)L|-7gXtEFjRLx9&cWZlthdnir;C4&7nN}I;IH?>@pXmL5 z#puY~v{tunS5`GD3-Cx?q_20Ip$sRH>e2UYUm`x&fA|q(mF#Ezb7Hqa<{9AUV-y%a zQ#lO%*U+&p7C7}=^=gCKY&`~&<8FZ)ydT`)poKK6;9~Ja*h{-y^EY_+@^fW*OB}Lgf?i-VQ}zl{Ogn45+O0c(bM^zlsj4;5Hlykt zB0T>&mDoI;H3K!2mn>k+Lr$96*qYh(xg+GpVpB$xb_d)ZI+qOsu5gJAmzs6HM^-^_ ziR$1He*ypD`=>7!Xdd0+>46M+FLKuEAY@6N!@>U#28>}Dt47KIO(9Zld@Pm*HDnNJ zRcF4j-Ii*eO7S^ge@DX-2A)6Z6%=_y=NYkaZst~t-GNzRCsU%F3xd$b@>gTPJeXP#AR7cl8}&oxYF`h%iPH-6V5<9 zx0DgIV7^)<5qvL*c@g9wux7crnvma5=|X#Nb_7+W9i=$*jpb!t+9_cL z@HfutjqVPBTOwDacudLn5}R9x*JbdXa%%16M6L3DJw{>*Ita?Y7+%jAz;Jh8-ivP% zG+U$}qOi4ij+uXazXzRsQ0PVsQpVCTqPqzqmpnD0c5zvQ;RKOGeQnB%#sYl^sy5`^ zS!<^(5cPbC0Q6w1iI)jx&cKbB-}#IjL}leBV_LgOqvq4rHTu{X7vKNZ9CCCQfZNKq z%z6({Y<_+DKo)W-6JpabHt6Ik`V8`Yd}XQFH@rpJay0J5VigINWtlNdr8LjKFud!K zJ>*WZ-&6bCkM$G5H1;OgbYYBHM1Nc;p>&3%;P@r^n4dEWZ)2pQB{wTek~2zlmA5D0 zBW$momUa?Eq^IEYYsNDdZw2dy)-c^EfUI;~K>jApS{`s-f8g~)j`UDDrmH-~pe@E5 z0qdhc)H7qMW>h$%U+3OxPmc?9zS=RSaSa6c%YRgP_=s^?>w!Ll;-3*{tJ$KYc>q4r z#0hc)U!EPJj7jIiyE6mnK@PuFlfV*+)N+%av3`F~rBNW+ym$pYL54;%znEuGT>tu( z{?)*Yq5qvh*I_04B(c@z^J3oV0Q|$&fNKZIpGW@`^?M^gQLl&4 zMeaHV07X6K?>=5q?;wyFdZ2ax$8(>o`3RRQZ+{XgnDCIA5zb~^@ic!j=--)fRAs5P zC?8*7@*q~ApXl0>8q-wf>IEgIvh45*rhO9~se`V-r0uw0WtNnyHW8*rb037sg~-Xc z*d<46;seJ^Z46WdF_WfZ_6wW3ZElP9D;-5i5B9n)h1a*`g62#Cp-cK$dJ!$ZFX&PD z$bu8Bw#?@wI6HzcvMaZ&*KvCMTzQP@D^@4ia_H8B;%7VXCYp?lY8IL{g7o;Dp#bQX zSqF`|ncE=t{n5d2A1sE`pz7?9(U;X=r;!k)nwc~7%Je>aaW6^nTTCi;O$Bhlz@9`o zp-Zs6)fT?PB7K_=9wJte{)&P~C#_n6w}&S-7I%$5TGMdn<{+h;dLF;^AqcYsS}-c> zNqZ8byO(iZ%*QWAAba<9(JQZ!j^H(BDR%7M{D#q6%S8Z1F+cmsiD(?pQj!s4Tx`yb+e?EK%`(2@{|MtBA3(6m;7K5uA zdf@dABR{6m?(332A;NO__-@|PL^2e3^XgyV4fn14_`n;MRg69CLa&r`u+vr^_BPlHpZd{$6qI;f8+3HJ{7lejEN%J!e-B-7+^= zcjIuXa`l9|=F+RW>_1NM`?oq2JKrX9EB<~rqK!{MEy_Xzp4L3GN9n@ATM(jR2Ey6A zt6V}H1wib4)`Y2ypE)0Meo$9qoS0dGIQMz`swP7i)`;1bvvqaLp#KjLED%dS2>U3O zA;#B_(DHB<%lO_A*pafg@Vh{dC*6gryWx4pN*(l|RLhz&@lnDU4rd6KSxcV9nT`5h zfqxPWp{s7ZeY|B#*jny!P;72oSN`PadVpZ`!udvct2s0Bg%YN$Qkr23eV^uYQ2J2k z##X5D_D>||69U9C%GCMK1W0<%&}*HnHC0G%S}dUWGDVYYjD$u{mY_X?&DSJ(Pkoc-E2ayXo8(a#N5 zb@_}83#;9@TD3I3k6_?3jRAKxsdTj>u6OLd+>glik8F03!hUApL-@m)fm#+a>Aar* z*h_r?6IEb(|H(h0o`lFom}jI~O={C~1(7{T^U2BMRucLSmaP^$qwFLLC74-+JD+AE z=L&&rpg|nR>$>p8kI32sdFH0EZ&CMX0vxDooUE9SHR(T^2b_Ee$2Y(=0^?)D@8!ax zXuaLvu!kh}a|D%coplUP{dBP=Q{TyzSD;NjNz6P4kQ#$Nl+m$MLPR2wT{YSYOvwbu z*PS3;#kRu?{)*XRvKhniL`I&G_NL}tLMiABkKh`ihfn5HGbGyymC57RtTeJk}g z2nH}sMp?N2{iPU=^qce#8bB8JPBAg~(KDN1PDszoaORsu2e3YaP00~&>B>&N89J|p zG}F3NlO#`d5dY*evMlaOb#Ka0U!;dr+7k`1k4IM~<;04&rdw^eu z(u2}L0BgfgR%+9lV=CP6_TmB5JkAFybGOGViT2r(g(+stY)Loa?Sk};<0sMedeQ=) zS=}j}>0=HWL1M3jBxOiw3O~u8YOO8T&Y_Jq*|n+X?6c>C8KeUB8;+&)b2qaTk0;H9 zDTFArQryjJ7VJnl);7M@p~&6*bqq5E3F|)} z-F*mLp8LS%ImFq6C)sbMCd`44WMi;%yXOSMZV+;RPrTUrK?G7Mr%21X*V0*Qbj9m0 z$2Y|p$luQ@dry3R2z4Z`$sEpaf@>`Xvi`-e;FbHQ>7Cp+W5FpMwlqQK6|oC>e7N!= z4zK()`=g4Aw-P&Nd%^Jx_!NF-hvx!0T{!&A03S*(29%$|_LLWNgo>?aud=d6!1KQF zC;3%#MfO{i=q*TGJrO2)u!+pA38z{S)V-_acM9sr|32f7F-iI~o(U4IkXQ7?wlx{!z!?Y&|@YEWxAJ2i|_7 zF!9UniRy7-<|dF?h8)a9VQj3vpB=j|z%_I0Av9B{C@z$aEA|LQmyq-`JGEi$pGJ8V zG2KR1xtV#+`RCA}AxW0UCs`j3C$pJ1#`ej{tv};QE=2Ex{$BcG!CO2Jr(^Gj07A%F z{4MTW4VA$sxvbb`P#E<;G*byeGgqN#RGE5cl%BnZ_)8;*zl_VC7otj+oEA(H^ILX; zHEJABY=bZbWK*KYcWED^HB_8!kih-O$JwYc)pu zmcWCm!UhLL>S(--9@M#;0vUZn{ljEe#Z4!BPoErv0H%;Bha&4WUCr@na2-ofp~`fB8N5j$SF7II9Rzn;F+5u>MRZ_MF41U_S@<$ zTw*EqV)5t>K`(XJP!G~p@4NdHOo?t1{`P~VQvd~O=r9OjLgIbvYXEC;tjG%xYxM`l zLPqWG+OWE{a_^lQB>4Og`AkWJlhStmR&Nj$=bEmM0V_(LRB3A~r0 z#C;Lhms$YFey~6e7RzQATn{1Yb)M1LHlr_#fpZghi!xf4w5e+T>a&)}xZeg^k#~sj zZzt_2FUtE9oJws92f!*{JG4nBGxsp+Gzb-5qZ6h{3{;NuO%gmMWe zK}KZKM3Oc!sS)k<7yD^#{r}1+0`X-OjZm~*23$ZYge`E?^lwBVR80!yHPsKnKT74- zjn&5Whri7EmcWtDufaqm6xwe)X%a?QoorR-l|HxuV=CR2zdm}=7S zySa0Sv;N>3;|;7$|Nh-d=nEZ$I^#d*%XQi7HEWEUO6f(Ow;RZR7)YkuzM`qd4`yWkf6LmdOnK0C?>AfWq*r7 z-Z^KNQE)&6&fcC?6^lak)ddIgDMKWmS zFTOHWI=bW>aWCr!8573$;a-wLOpgZg4x>;?fCWGGn@>vtyzuenCI+UFmqy9nHhNh~ zJ z;=6HT(8|;btxS@a^t0*B7mW@6o@jFvEYV&SGvAnE_%$Im>BlP>_;Fl(r9Y2V`V3)h z0?gfASgc;Bh{fNBCuyx~__zI58~}}N0BBqU#~bClDJpyCi{OPwYt0iCg_+3&ZyhZF zKW1Qi$Uzf8gt-smhHe+b+}M@t+cA=|>*qz81#37^t86w-ltm}uL1^dG=9~#pAIf_~ zr0AuSz5NQFSS9crEoV;SG*V=-n59treGMPLTGV0$Lac#{8#L!iuse?qvZ0RrKjJ6o z3N%4oC92s~5LGBMxAu-Z$v*p{=2*wjnak)CTYBKwnK1G`js3)2+FXVbi#JnOVleZ4 zVM)|iF=p20yz1I6*w4qiQ1j)Q@KZF|m1|`BWI#<0IQF$DM8_J=>fEJT^V7LvctxkE z^=+7^*_(Z056Fs84QXaUw9f!c;rTd!RWGCJb<0&7SEX*WWO?|rM#0UkhgA+bvT;(` zczWIT5=yf=>|4e~huShAN8}6<4`6Z=0Kmrx6g9$SeWT^ZIyboBXE|tJa}~$FQO#TZ7AEC5`N`#MFW9qEa$2H_3lmLg*$UCQH(u8Ii>&nB59r;p#(~GK+PHkYunq`>F z*6i=HvlxWGv%t%{r?RJg;}fh7>FR9kz99?n;RD+(q#&Km2&A(OLTKlt@H_E*eAZ&c zH6`_RrxNQ;uh3b0ryzK#_@xPN*PY)B!XzGyd-Qbf@>lKTq7L({KdL_^MkKRQmRz;@ z$V-mav1QH^KBlq$Xn8^bZw$3!c`?mZcHoo?CgeUt^482n4$c~rhF+bI@T-6XF%FdKe zT~oR~z=duh5yxGepuJww$6&0(`>^u8YuC{HbprV?!9|IGNz30b#@HU)EbCn%GjS^; z1>+u=)XyPE@_7DIsgV-cu7_;+4@gm;ML`~fmg z{0o%j4x+C5_%h9|nLgl6z<)+7XKPUfO2SFlu0_A#BfIk>Vgkt?vWFrTJo2Z3K}RLCpJ>K(wy=roq8E&mJ}H?VC1E6ccIQr z1FMeuwRzR_gc6(e$WF;zEqBv@swOKk)ABCcrwH~DtC{kO5BfIzAB6rTI{1#(lt-SJ z+;ieAvZS`3;=z4fU^&0aqn6#g1RlN1#&@ znV8ihzxD88TXPvymt8F3ST#eU`_!Slyg3fPeQtpR+7BI2ps2zV>Jj|;&!I3-p$hG~ zb$bn}n)u=&5hzBJR@DJyxxFC1;X6=_V0lXS#v%ir(%w{x zL~7j#RRN`Z`kQu2FJA(Fs5=~vruDO@jhM_I!-r>Q7gr@5dv z;<5%7s_x1nn2|&FpEJ3dPkyYD4wrvPoo}+V9zJaz)2A+hm}er#71Cfu#Ae5ys~1zb zsm)S@c0*AIN|@Q9C*Q=$E!rEpI{7a`EV##Xz=tVlt+1*v#nxBtiy6 zH4q_Rye?W2EHj7n|zkE2>Dw5n&S?; zXrO%HFpgM~;L{Dpf~t!FNf+6}(77FB(K}E@jf_KwmvJH*JL)x z+BMe6N$Q#P(TR{!qG4MC60b`f)Tb<_A7ydNO1sllC#X=(>wBX-^7UamjP6Ap4`xf4 z+t*VZN>{g^M^1acC45KLT$Wv@-Qh0a&+@&67(gTXP_`vBJ&&O2`PeBoE1bV9ZZuoZ zdr4^8W)=USzf2Lm^xnOYzE9F116^ZjWU$tm$f6W1%A2zMo0+UhtBPdLNOqirPhaW0 z*!r8e+zZ+CCWrGctvR)N_8#;WssP*?eB=&Ap)H;i=`y)~9c2{ILI;<$^{jPkS9=a%fL(PFc};7yzof^&n3qTuFJeopz=YZx7;i6 z@vDH)T7gKFwYYaf_D8&7oz~J}XWNQFDBbHFjR~4r;}16lBl82X5c<*U*=e3AbBKP)khJHP!i?`o95fsLEH$qKO0a=%3ahUqG7rcQSw0u5mA_SapS+q}8XSm3+ZRT>LvW^_ zklJCMEXKD8j)w|VeLa?g-#8}{$VXni%$7I*=*<_zd`31e(JPtNm5Dr1O(}w!0TLsJWVd_G1%D8+L#@ON7{Ea}3Y!hy2zuqD-3uLFi9HL}u#DGp>9-4=y$Q zL-sPK{isbioB{W6+MmSC_VKp(&$=+>l_P4CHwW429B4Zf(hN*pdql&@lWFq-OxK0B z!e@FG{n?G$9hKt#+1sFZb{+K2wnDwL!B?LqUN0@)CzE;t3hcOLjAvjqUUxm6oS}z1 zCq=^2OLrcYzPf5mEv-hF!H2YE#K`&KE+?WL{%{CV^A>5M2Pjdl(EU znpYs?{{~hs#t~O#@m2lCTdSCTca{9CVej!xPo>uKOGd=Jh}Xs7JH1u`Q@NPRKli!#Hmdhl_KK=b~)LX;~HSMt%x_+FoKk|67f z-t@z;=3VmzmG=diOT=mV)CUWl88%BsXWwS7O{g88eNRv>9ZcXn)LYbxW6v!izWUA* z@f^7;;z7kHm!=YVdYi$x4%FiqlO?t5Y{+=#$l)n@cZIevhNDmGPr_O6iIe|R! z8koEc$|W|92}Puwd1?=ye3+oAeq6c4=dwnUj%%1;SZMB|GyOQ0_1GXIb=R@1#Q5aUT{kzL87C;Y_u+ho1cxlh-H!sIF1kA1pYxR}LPl7R3VV z?_d=avgam8HjN?eh!g3{19=lnhaTcWC!=($>YFRqk(h__#2eo_SFrV1jM?@(H?ui) z`JtB~ijDj4mdf9h31UYD_4Xg^B9Tt-r?M5te)r1%CkD-g#GtnUNB<9-`t=b;6>c^{ z*s3t$Tp;*K@DeT6mYvFR5rLoHwGpzDwK|zyl|~?bNsuV|dZvT}_WN{Mmxxxxguvgs=U$6LTDB}r8LE)n!h~@j( zzhX#CZ#mwf1P$Lz%+QkyI1Y{`^?P4or##m%1WYJTQ9;aMd5GdS4EyUgoHhTaPcD=< zgKt;8{iK}ik|pR~nEXy9gD> znxiw(ls+oxEfr!vz%3^;^jRY6TQQv&e>PlyXjhiDqS43ay5R+a7Vv|u8`o~oDPB-n z6du%N^9$BGk+IRZFx5{6PThQxh#B)Tvt!&UBQ6a3o(>~O91eCDgb6%uI%KXrUW-Wz zXI<3Oq_eL6LjH7lnJ=m$Eyc@GNl>QdChqx`kDoSoWv@}Nfuxs$%JcgW){N9#udRz$ zV7;v@6aXkuucjhX3H>IS#gVlWccQD$QM)pBnA54=U#?Kf*pCb#oZrRhktn0D3Ool@ zyr>utJ<7CvJCsxiqV~vK7B^Az@bF%X#CisqX;yhY?)3dl_X-2cCBinwY^mC#YY#e6 z{7Lblg`4KGKk&M`Zy7%^Jwv$7&!Or<9!mo34SSfqDQB*8W9A*&VGnXZuJtU)TifyO z5d`{nnQ7_%1a*^;G50XUxef)!SQ5siJ`_JkE2RUx>4{{U;L{-g&lklJzjKwV-bXi2 zjv+DcGqXFIF@GO{ux1w!)@?_W1~a;7qZ(qk$|Ja}Irt0XA|Sn0 z|2X0rfg;(x4f11@a>K@z?P#fWhS{WKA`a_E8yez>wu_o0=#I{rpOp`K_$>HIUV8PG z=3|{tAK(dlBW52KYyW5U=QEuHz$^bEDqijMLis=z6>*OKJGIm-momW}5F^Sg3c{A~ zN@RS7Q<V{wb)XK$ffx%@}!{cp_sCo1_XegV$O( zT>j*jD$CpCcQm8+Y#9gcoT7ebIYDo#4@l%LD4~WJdvSKr(R%}y8JiaQ)t7>@W(3!m zBa58&E3R+4c}Q9eYyy#8rS(ntOGSlA3}}wm;ku*aC*$Q*;3rvK*lwnD%X0V7HrdFY zd|XuLx7nlejr2ucj?v>zD!&ofwBVEN{-mI5&8Kt@QK2 zoG5FBEzsT&on7qz4leHH8jHIhwoVcA!YjyT@h!nXb()i!Y`5GrwG8 zPq8~)?Qsf*pY|eUGTzK#Jw*IJ+WoZmd^(NYn=BM(H4SWxZ0f&0(>?r!)Gj8tJG^;U z_w8wA3nzH@-x&?MGx+cNrYR--ogjUwP?}HoN73oE-Z7471_-jHDe61@+(Jb9jy5&R z$5Gq!4lpBGMvw?Sok1J31n9pYgt6x#Sv>7dey)FkWn=|8XE@XqPRpW3R;w=lfouNZuj@lk=;*&u+moRQN%tWDuCW& zIAHD0P5wxSOE$bU5mCF3UJ-p9l?Z9K@!skaF_ z?iRL)>p#&!oyj~dpsQ31-3C5@@B8Y#cl^o`nzaE|R!}$|we5pNJ;5ue>^*@fikmxE z-inTirMmWC%&J!%J*`s!Mp-ZwdD;kwbk7z&FxBt8nL2mu`T76C8}^)^qm!LWS#6V* z>|V`LBUl|^YSG6$lw5e<&zPAY0B)(9aMxV=w=2$LCtcJHH!qGwsxwJ81j~$eYfO|3 z>1UCAI3BAs@r@~oUC;bNbMkA)HyDm8K6?T{Phy+SDoV>#WS;pR&NC+3RFFIJ-chmGzFx6Q zpE@0Zaej?c$wlH!HZ_1>zsWYgo$`6ahYAX(9yQ#)x;)s2(9btuJb z>E3V`Ylg2E3mc{&rd7-1Bc&m~dN|CJ;0E;_oq#%}w{|)7-EmDJnaXE7H_LB)!nc@0 zTZ?eY4PsQX8VE&Y?&bPS(Gm7(UVu9ui5h0yfzLSh;%Hl$l9k&c@w= zb^%I>2CZL*Hhke_bb!9E3DA+`}ejbw$hl|JqXpmOl_f z#w2-*CCw4uD7*UzjutnMG&5Dc_Y{Q)T_z{`_far-dur<3`iI#r!5K_bpE3u$02!%& zISsVdEJN38rk-%tfRAZY+w?w9@3!7lxibFf*eZKF$mC7&(ZcIVQ zUB>M>GIsW!FsOevTISXBp^UazjUnF}{r3j@e{j?P-mA6$a8t<%?e~19-P1pA8E_aI zixqu~oe4$kT(XRU>8IHyt(2ceXo|}kiUo4tKGAPkQ@1413a>3qbfg}(u_QTKpJXXj z%5-bsQu9-&#gL7~v|Zg!dts^X&&o3G{dYCrOnK$D`34gINi* z(Ql8kDo^;X3|m#J$ahS97_{u9EC#yVmf(IJ$&|se_dbu)uL3%UBor}Xn?t9Y&mV$T zrb8VBRcc*QY}|(UrP^mll`S+A z{l`-5i*gpvQH9MD*XeglB45edz9UcN6{2UOnV38qfXwMZPctv9SP9fV1NEm0+=|0T zO!TlKUFmN&R{bdq>5{o%-?O_io#z=?Om8MJXzcw_rQs?J$h$Kg8EndRdwEdWKvn?rn}?FTPcpDa&1v_~I5<+4JmJjJrLOA%n{~ zC<%M-2@TnDfC>;+5{9y=(ipzF6=6B%nW0jJr5^aMsT8RB&>9xT3!dcNm%WKDo?+5a zcQothYp6LoRwfQ(Be0bH8Zhw9$wHp-tn>-h882C#Relm^H)iAPp*s(#x~8EbZEFq8 zFb)~H+FU9b!b3ywbGwHM3bzi0%ag#hcY(e3hM<8`D^9%inBtbN(wms9-*KNN+U&}b zY%^<#Of}-fXWvAmq(lgf)oI1CvGY>r2{X)7io@^>OTiE z+*1|dadAFR_|l%W8jO|dM5#kndsxqK;ZhJn3>TIEtxRtwvn?Y&+FA$hf`@u?J;z@I zr`bA%ys~A$!r<5S0KvvS1p6J99EQ6JZa+_=kmokVinH{qC2y5>zu#Be*nZhGQUpB1 z0E-KQSL2ioJ$JzKQ&9q*;S3P4^h>mN95Uk1^BC<^d^azF;FL=9~4PL0uCy`{<0i*?r$c2ezt~5uYB^D!oi9qA! z5{X&HL018Hp~1PA%iT*>OWR3b0uLU`S;&F%neR#M<-HA{-RBYbo_@G;^8NeagDCmf9nhU z^Z2)#q*JdyMkvQc8_m#{@*65Ut6yH?o3kSV-KXj3t$jk?J6|KtsH7)uPsWll7H)7- zG_#A1lkd0)(AhvrYae{3+GnS`y|^a+k+RHTvkAm9L#8Utnl|q$UlCz~<5ipAylN_a zrV$d^kmbz3CB(rAd(bM$2a@RlAoq};25!~oK=ndkEi~mV_{ym88f`w6GGTe3uKV8$ zJ^1VQZ737e0%$)?Pmx1A9@Vq#QRRv;AFQLCD4AAvI~3%u|3aoS-mBZ7##~WI#?%m8 z>mVp%OB~gw61%`bg}lF4FR_(v+nY9wKHkv($h0N*u$cq(H&2aJg%lXuJJDYsl(jxK ztO{|rrz$z#@k4ky<}N+BLabO}ynQze(lGRqrtUk%B3wjM;S+=dDo$e5sRTd8g1OYh z4lo4k5qJz7@{Xp;g6FMh{Z7~|&h0I4%p!jyffs~i80THs13Ab7kb_)?Wk1KxQj{9m>&VR0=)^PcvPq??+6m{5&;h=-wOsHC;bpy#mHuULU6(GJ|U( zgdY@X3ctQb2n5fe)noRC(7{azSZI)vw;xW4_f zYdj4_bVb)}uwwm0*dcO*Uvrj?R&mv{5tuc!$={Y@vS<20bu5nkgO_;Xb&gCY7zx^937Ga(6GyAz!>YKt4PiaGK25OzyRCaafb^=N%?7_D@DtY*DREoAiL>OnBcC7)bB8y$GKyx+nQp7+jKu!O3@Ba z?=|_d;MT@kC7X6X`}<^iP+vcER%mQX2cSfXwh6sXbZcz?3Qytig_sa7FWm0BD^22? z(^|46I_X-LPyqNi2mo)`u*`yrIXAFaCCCp5IQcAKxl%hF(6%VKi*D@8y0^muHxfF| z2L)7bDx4{%b_9+IACVB*dA-Kq3VvF4OZE0Xic6&){XGV&4~WN)`}D)s{#^R>KbmB5 zl-X{lv1t%2FW9R_%XC5EGMvc9l);JG%;uFt=Y6BY2#=~aA)k!_s%u<{h@LN0Bjq@p zUx4k~UBz$6Pu%otCs_O5uRlU|wXy?&UCkdy9q}IrY2Xh z*MU(3dpIB6>|Q!ReEosoaGF)ui$=zfV%hff!tTA926fVDp`@52nfDH#>lebGF+}0h zj7=vfe_fyW5bCWQaxE05ok)B^`@Zn+$-R+-g|*{C|;O=fVgr$*a8Lmo_;Qt7|e~OWneFeYw|| zbrQ+OU~V!BPI>-bE!~4|b3zcq+z)q9o^&y(%h!1U`j z)S+Ym!eGtl24Jr)Wj>IW4=;D8*UL|RVTU<2DDiFm_~UjtRBk+i{>%t@-ipJN=8CR= z^*r8qj{(?*^-iwo74_GqRRWLdJvnvCQL8hfZ9tM}cIJVo$wK5`>tU1RBGw;6pM3%| zIata6RmVarc`>w-OaGR6@!*DuQ#v|fLx7!q(@23yW>;BP5)y?lSr9y_$)VRnYw7yf z$~&U&?LWL6uS{$Z6L%>yKs*AY!%BvCe$SM7_=$wR+@8!#_0YP-gg7zeed#v>AIK;t zTSaSQ1&gz_9(av}^IidMXx$eouEX9amI32KhQNQ0HxbB~IUwK6vYlZ;`X$0AN|;1b zsl1x5TT{uLhO55rb`|l+idM-TDY}xq^J04WBk6AYyV`E$v?-;jF|F6EmolR7&ph0= zQnKwRY59-62#0H3(8nyL?Z=n=YWvu#j~(tjl=G>HTA05DoEwr0kd3hC(%YxEXVJ|D zsMu{qyqPDO@N8$nynutMfJGHapsyJ|BMa%*er_WuSXLyhKQdyZg_;()B`2Uii@yE! zkBLu()5X^YR*8A3py8k1_tUXJx?q9qafI-l9AWnRw)zh^Qn(wqu&&8vqDxP%XVaVD z}e>;(%>ZkLn`U4rG6Qmv&&;}=#xk)y1RfAug#Jrc!6@dkElBKPo!VW3WY zo4keK-EVf9Zyh(bhgT!bpb$0wa2IV}l_FtR@Fev|J_#biPwLSZvGUN{xSYbN4tzIr5-Lwam-WPb7L$9aP0ii8QRRglh)Hq3h_YRk z-ZGYoYxWd1l)|*vwGo$(o<{2ICx3eUBTj3@+%E%i#*|w(B7_{Y3gSZ3r~BW>Rp%q& zHjeEH^MaZ;&wb1PH6nsfyMXT1AV~`AFMcd2E4^rhHqH_25C2R!g{Iz3GuyfzBO;YB zg*H0#_zWr|F2|O|#iDBp5p9x&J{5oW%tt9V;lG6u?^peA7xuY8SSRBHk%E3g-cyZG z*NJkzeQpb4T?(u*vzZ$2_lI#Q(4}CV7$GW*Xjr^t08^=^oB2u1$-?v<@6k1b9LrmS zrH$SM8w-}=vLr|P1etBjeMlhx6eZ`5ExRP9`dlWT)mIZJ=>;!)Mu+oWj^N8^xll0x zS_u}g(0Pmf5{2G+SMb&|Pa<9wMc6di@B|HgzjnQhm59rtmW@ZpO*8*bIGx=v z8DW9hWBU56;0fXZ%oxmesoYf^PP$`xd*2nC1$G^5bsdpdt|=rb92M>l(k!6y34_q}PS@LQj)d!tprK#?%$2MQcW|pNfLoR2zqhIl z_Er^Rh^9SX;U#FKlumyrAhoo3vSNPapS-V-%!0$E? z(hk<~#(xuyVbdd^e$K?{}Uyt z|FxwNUJ+Tp`$}V>DIu*67P(s`-rRP3lvn5>LQ{z9!FEEOn6%66x!()uoK}KX2%$Uf z@m1R%CP=gV}llM=RAmMiuUFGte5{gnAF-_$^BUx^wTrPk2qmfn@ z*GzsUZrQ}?EV|NS8J$sn31Mu6^4(e*u>+XIH+_MbLOxKca8!2wZp3&v{o>&$~d1eDMqe)-*8zP$G!^Z&x_ zioCKHp~Hyt8=Y@&MDqJ?HmVAJTDBw1QBTo8w<&(q&=5|*g(lOUWQJy+%tlt#!O+CF zU^{;sR)gK4?hS(-3J<9O!s^>VZRfwi6d2`}ncWa=y5_ULO$(!(TrDrhPg8|xPuHKG zw$XPCAgzBmOfhu~&dibR3}y1O>syvNZnzn3Q|dollx)kJC1b~F9m05v&8KrMoae7s zJ(R&7RQ{``!#~QW1#dTcx;|19+C)~upP7()s0VRXUSE?m8D8}e%nSK^7sLY@dm?DI zjdi}-6uJQ~u~^OX%G*hFM?fr+*H&G1J$UG+ykRba-lgfu(=FOpVP}qJ(K_SPE0>Jr zfUM8Zr@-?<|Nl!<`gb_~bO~&|yFKBHpZ3XuL(RIvXoiFW-#ZN85O?CSyih5QbD8oC zyGEEY^Yas#G@TgBU5sTFJ6o~VY+z}QLdA_N)inDy<(JFPyYH<3GsBu{Q5bgY zOZdN9FS?#>b;I{Em{fWFULY;`!dL%iEPh&mom9=SlPXX@_@4A)hA!iNt5j5^#C`S- zb6`u@L%EyB#Fo|qOG(s;(5V9n8N~C7`(G z%bzvp;qRP+cX5JU;z1?1{$WTbb=7M_+ZQjHCO%74%L|J=C3Y2|o$@Bq2bNgJYU`sh zVBlonXLtYRa~;Q!w-_jK8aagd)Sp!@p7y}P+d^(TMZUWFJ%q39gC^=k?=OW;h$M$8 zD@lr;&qpa8E!KGm?#%ZG*7pTqeZLG@-yyiOgvYLWP8h)&#j6QVyIv+zkTVEaZvdOg z34*o%cM#4=U%G?>xk&xj?oYTnA&>1wD_=#v|J91}$P-Ym8ju&xAE9?!ZOB6_()F^3 zGd<6dTtB}1XW(<_SP-&YEXCRb^Cj_+Apk_HPs_=cXr#+Q9*{HOwEvf}n9#JH_CF5@ z$`Czc{D$+DEZq}LLo{J|dcI+q+oVB$d5V0l9IyK)gPv&%63^t(;MW7_%7YzM1XqIo zxNo3XD*TaW4DY+NArSq$17Y$(5JZJbDf6L|9X+w!BcRGjW-a02xO5jE#t>Wm6++{p z=l~iAG)MXTWxsw#NcVK_dYURbMd2kRkP7G2@XK<1oQ^}tc~(J7Y1%eYUu$)|bb7o8 zQSFInlr#lXKu#RURsZyFVf&;LUI5pyQk=0E~Oxar($zhhb4 zPaHfN8os}7zI{kMS_2nI+I!Y*c&$D!)IKVb?=FAc@zaQs0z}fr@e?(MAns{qkjH4n_tvP6al|KADq_;*78U-Xe5qLf8|s|x<+Yp8v|FC=xS z>5beB%0TCei_qm%9x^>get($i=iVOJ=DSzIp_kF{ew)g^7dsm&;oXgmF$T)=Uh`LH z?aE6#XYPAnoMF`?uFy9%OX<;NQWk$ap!ALVHdWKk9c&69fv^Oc-wXc6uxmyooyo5) zzVFp?NM=DmTLEV)M~hvIN653>q^*^RtI+y>;g=hid-dAw(IKLT3UiAK*?gP!@t|#) zfpz!=anRz9T@Frem7T_GVI4t=?x!Lf1G-UtRYt43X7lwAnDa zTY*q=noPUrr=-B}q_$vd$PptW?=WK_eCp?Cg?X0i&l`&1npFw2R6Htp<0YtViBdA8 z^88J|GWZro`Z8Wk3LG7hyJFI{hC%?CNpFc~;o=?0rE3Jabb?EE1tZ=KIF<)!b{_W zCd(L^w~w+(w7j^rJLF034KO-FgZu?5`>*RanFFu$EC#BA7EVFhPE|-31MWYdrsKTI zIQ%*TZQieN=z?``J_j0(*Y!zk#3IMBSKa(Xy1%@RCoqcJ%2ILm+QE=Xz?3Of71#E> zauk=06sNxGsGn?`eezIO#G~wi9U*b$uJ0(d=gf{NE=d2eJskB@A?)8{e;d!S3xiTtH(apT3#Ob}f(edi?lFNI>shYg$qoI&09~ z+rIYHhGeBcZwk+lMuqASQENkNEpt3KLH5E~`WKASRWFA0We9P9gCPv9Jl`WZg7cT@K9n56ZO1I$$iigvO#w%^S=AZbqoNBcK$v(B2kNI&MO&`~8-V z9HX`O=K2pD1F--=txpWFC@>i|yA61=-!{}lq^dfhk9rSQtJBJ0h`O6J%?_;W2ON6& z=LaFhWcS>2-74sQVZ##*fQ-#7rJwG_5jE_68@}%%B_M#vw^IeKYc=0fun{Jsg4fjRz6%W%rvBj%@m zYE5>4cbnDwiUZMrw%*luMF@9|21Qmoja+cdAKD)}Q zHz1i6lPtk;bq>8j$ez)8mTi+qiW`*)2{$jX?o+<`oB@&QFkX1$!h-Wl@gGjr1XXQ& zXLlLO_j^^O6kNe5AI@*Nu{SJlGJPw--vBH~RhK)}nuKQ{bH_;zFn2f(gBBl__5J^s zxkIqbn2d`8;9-gXAC_iWlp<5#Y0$fzMj9j&lCodXWHyef|e`BK;Ee z>9U@#|6$W9RtEYy7rIo(Ni@`R6||qsI((Jc$GI-s9dKiINQxtoX&r%6HrU6dCEhJ3 z?29nbl4dTuEHo4L!vD_Ca`4*^P9?TiUnnEPsjyzR$^l>$nC4xu)4TL(Hx}P?p7NQp3AcnoY zRLStR_FDEOJ+T`x72fE!r!$O~lXBbMAy6h%DF7!$rCXeiDS*+FCyylz|Ao#_bo;fl zLjdAG#-)bSkObgcDtCm&m6fy^Zve}O%kd!6kOb9e-U5GA{}pL4Mf)G1H?%7K%|>3goH=&Sol*E7Km(rSeDh4+WGS0uXZg#~=KTZe5Kyz9y9P ztuFlA$4+G7K;Cw(+LMLDG}_5hiE(W{SEfzt;z-7^zBi_eu$F#4uu zj5qIc6t@-lR|2Z@@CHKm><@)|b-`pOQeCgP%)-+M)-O|D)htn#Z_WPS60M6olt3f?RBIwqF&0S<`D&&K6SinMtzPYrOU;&}nY{ zxhgyJ_c*BUoCNiqp5CGV)q-Hl=7XyVSi3wQN#Ja=GDDL3cn`eS`s2}pxP$g5VRgZ7 zpTeudcJN&vHx|;+-Ye#3pJ`jV`Uyu{tr^3ho8wfjTx zy2R4c1vg$UH@8rxey*?h zB+qTZAfV^Yz|F2#p7Z>~!@h=@Wj__;{WSS{NM-id*U0i@4CzB+lx-Slm<76}YoKf> z#2QB;vbD+t(F(m=RP*NVmcGjWo`6H(Q~HLUI_pImRioz3_kDz1r%J*k#>KA!b(5k7 zbVQ5M%@eqH^~a@{x*2JTW4Nqug{nM#gBEYD3=a-gp;YUXy;Zz&MnM1R+*38mH?PHhA9`^CoYD_|T*SujnvdSOix_?uCl@Fb~M}*8Cqt3e_TU zmiN7KxOt)f;Qf%)x5urV;L~W>RC42s#kV@jK_AF+ zmsmarO~=HecL#-x3gw()#XkC;31LS7ondjLS>0@-r8-T_&+_vv31t$z0fm96)KmtA z{cfmn?j1&8dBGdGw8m>G_vzXz3t@>xZLLRY31JcJ!>9SMDn1MCFUVgby9Uun-)|>H z?KQls6#Ty#8_H}w-(YY)lpK5HeTnNi|L7EoWkys1{b{!d>bb}5l|X%_voOJ+HjCjp zc}>rP^-V+p?;SPLU3015r8rU}s##j%ud|3cV%Wv6%^UbP!5$k+DWnD6wepV$GCW5U zzLX;1RuS}F({ODU=D!}X0-mW*Avy^uTTgRl}UZcuO!fEa3|u4-8@=hTq^X9Gn&*3EPXN)Z-qEd_qrwE zOi+^@^p_IT;&x&%Bbq-OcwH1R6RSJoj`n`Z=iKz|r+JqOjtUOBHaHSjx@G$Gbf2_o zP(4uSBIT{~ZQ$9^HM7I?#_azINaKMWv(4*S~bvI9a)F@ne(?k#+OW|AfR`3T+wu@iLe7 z6?&VG0XIsn0B@y-n#u9A|9RuBH^PT(dQ;I!H__X~I|$)?Q$m!$lt{~9T=9+H59$jA z(YTU{jauUydcV~#zU>rt7j`E?|3ys(t8}jEfiEmbR7TIG6mWAi8~Jp z2y5gKjprtc8O>9P=gq*?ZNXY=eXX7SpCmK@YeN2HK@cBhuVt+uZXfj{m8|5;QAxH-m*S z1e{LZ`>qCIY`NEN9Lq=?L%gUV&z}<^Z61|8VGEEu&})xW(8j+0lPP~rY9J{xNJ0QU z5RD$=ep-&?!F3sRKZ=y;gglpVtvE*aqk1=49XZOGDNS*WCRU}vC|HHDv2Zb`LgIcY zk{FZNa!paK@NGG|Snv{)X_M5nvF6dm`9}yJ!GSVVHR)pp^~AI`2fGDKHu&=8XKt@n zzA#;MH@@L6sR^QSc2>oJ7PKDSD^GIkixOkASE!PD8s?r- zs7C8J9@H{!MYcpP1V>18A&=~;L4zMwtV9Tkk=d|#em+!;c(G5kz?1pt;r$f9(s{9k z_hA{sq6b?{|7v`O4qkNZ*im3Kyuz~Q)&2864el~N59pjrc`v@1UeTv4Pu!ba!Qb%S z6!A&UF0Ar{wX9;vD3H<->P+=Ic3ks*U^jVizvdBeabSR6iLO`+lG;ee$(m(`Ttwl8aT2qo-BVZK;7!yi(9hT3)LPZ=7F7xvh&Wh5an|+SPcg%(d4#v^uV_e z#HMc}M2omXjMFQ0^BfYMavLT3Hu3#qMC%=%NYRp)(!@l}b#nYg0YW-5py|;)i*ByN zSI>#$xH6sX{ofu{jr}(Uc-Phv*8R)j>_b)qFl(w=7BTIM`!VVu%ue~pYSR)g9g_JRWGkHGW!RoJnD2-kH{}V{> z`+|qI-fp0U%akFgmoW)W+tQ$??%*qH@Q`n2eb$szCM7`&;dR4vSjvS%K^pA`k}+x> z9`OZd?6}l{M@uhfXr;VxMs&#uZ|vfaD3nOA1DU^4`}1FjKl)G~e9etn4{z02BU7fd z$@`eu09azGFo?I!P?m!MNnM40WS@>(1Tdp)e<;ceq`4unYyj()#8ag2e~{Yqj~9(a z5RR1!iX{zU>iS-B^Sh)%M!I=mr2FZeL&4iRqi=ihqt}W4y9zxdEn!E5@EIZUIB2X# zutWJ}8H@cxLlFd8N7u&zGgbHqRHDfUTYE{9!b46gZyAuFOc>})f8#a|z4u3~md*%0 z%l$y582|1IR5Gcfm6P&eW=*4OH_MkCIFUUaigK|;o(71uv&%;g7#s=@0A2mX5mp8@ z2dO_rQ*VAqqNf|e=mn+4A`&zd3F<|?v!PVG8o@Q|V zEdw{pOO?$u0@dEct0!pqTguS+7RA>4vH91#wXAY1~hoc~4=sgB1NFfCZs8`m$?ZN+U(bP#SY~QV)uV!}Xh0bHO!?52h zP^o5s`SZG`N0oE1{PMiGJ4zh1r!0O5j9GbXsuD+8z_h;BPC;97#Fw}OKXc`#Pk6(d zW)~8r{PTCez>T9Gp;UBBXEOYB^y{BZXL$7hL{!3ZJ4_bEwf!G;i8dAyt?9yw?zFLC zpy&WRNkM`#i5uTho#?jZlVkYF=6IX-4>0Ms6dvLCYY0A-+n$rUZeC+^z> zL!|5#x%0(b6SHjKhVQQH~$5Q%eGATz&Jy+!>BoJu)eOUK*-?Ocd zk7Lh8b!Nv4!5oSL=@{XHBs!1Il}vAZW4R;#((VL|PyZqBouH?F4mqHA=v=v>Ux)d_ zirt?|q5UZxwp1^1iAxwk;}f8MPO+UUIAa2XqchCYl>(A6wR$;7oz=p<@x{N6rG<=b z(Rx;u7rUZ}s8wrC+~pcW-pb_z%6zD=PvFg=e_Y9iu)18<)mOSVh$&0PJ88xRJH8ebSY*d1Gt}

`B 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�$;i1~zO)9>IJp8Ko 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 z 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%0Am%{^HWmap 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 z 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

= [ 'latencyAggregationType', ]; -export function useServiceOverviewHref( - serviceName: string, - environment?: string -) { - const query = environment - ? { - environment, - } - : {}; +export function useServiceOverviewHref({ + serviceName, + environment, + transactionType, +}: ServiceOverviewLinkProps) { + const query = { environment, transactionType }; return useAPMHref({ path: `/services/${serviceName}/overview`, persistedFilters, - query, + query: removeUndefinedProps(query), }); } export function ServiceOverviewLink({ serviceName, environment, + transactionType, ...rest }: ServiceOverviewLinkProps) { - const href = useServiceOverviewHref(serviceName, environment); + const href = useServiceOverviewHref({ + serviceName, + environment, + transactionType, + }); return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx index aa2e12df6669c..f5c3b799b9deb 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx @@ -35,7 +35,7 @@ describe('Service or transactions overview link', () => { describe('useServiceOrTransactionsOverviewHref', () => { it('returns service link', () => { const { result } = renderHook( - () => useServiceOrTransactionsOverviewHref('foo'), + () => useServiceOrTransactionsOverviewHref({ serviceName: 'foo' }), { wrapper: wrapper({}) } ); expect(result.current).toEqual('/basepath/app/apm/services/foo'); @@ -43,7 +43,7 @@ describe('Service or transactions overview link', () => { it('returns service link with persisted query items', () => { const { result } = renderHook( - () => useServiceOrTransactionsOverviewHref('foo'), + () => useServiceOrTransactionsOverviewHref({ serviceName: 'foo' }), { wrapper: wrapper({ queryParams: { latencyAggregationType: 'avg' } }) } ); expect(result.current).toEqual( diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx index ef762b7e819c7..982d25802e202 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx @@ -9,6 +9,7 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { APMQueryParams } from '../url_helpers'; import { APMLinkExtendProps, useAPMHref } from './APMLink'; +import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; const persistedFilters: Array = [ 'transactionResult', @@ -19,28 +20,35 @@ const persistedFilters: Array = [ 'latencyAggregationType', ]; -export function useServiceOrTransactionsOverviewHref( - serviceName: string, - environment?: string -) { - const query = environment ? { environment } : {}; +interface Props extends APMLinkExtendProps { + serviceName: string; + environment?: string; + transactionType?: string; +} + +export function useServiceOrTransactionsOverviewHref({ + serviceName, + environment, + transactionType, +}: Props) { + const query = { environment, transactionType }; return useAPMHref({ path: `/services/${serviceName}`, persistedFilters, - query, + query: removeUndefinedProps(query), }); } -interface Props extends APMLinkExtendProps { - serviceName: string; - environment?: string; -} - export function ServiceOrTransactionsOverviewLink({ serviceName, environment, + transactionType, ...rest }: Props) { - const href = useServiceOrTransactionsOverviewHref(serviceName, environment); + const href = useServiceOrTransactionsOverviewHref({ + serviceName, + environment, + transactionType, + }); return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx index 90422149f693b..e56e4a32d9bad 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx @@ -8,29 +8,31 @@ import { EuiLink } from '@elastic/eui'; import React from 'react'; import { useLocation } from 'react-router-dom'; +import { removeUndefinedProps } from '../../../../context/url_params_context/helpers'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { APMLinkExtendProps, getAPMHref } from './APMLink'; interface Props extends APMLinkExtendProps { serviceName: string; latencyAggregationType?: string; + transactionType?: string; } export function useTransactionsOverviewHref({ serviceName, latencyAggregationType, -}: { - serviceName: string; - latencyAggregationType?: string; -}) { + transactionType, +}: Props) { const { core } = useApmPluginContext(); const location = useLocation(); const { search } = location; + const query = { latencyAggregationType, transactionType }; + return getAPMHref({ basePath: core.http.basePath, path: `/services/${serviceName}/transactions`, - query: { ...(latencyAggregationType ? { latencyAggregationType } : {}) }, + query: removeUndefinedProps(query), search, }); } @@ -38,11 +40,13 @@ export function useTransactionsOverviewHref({ export function TransactionOverviewLink({ serviceName, latencyAggregationType, + transactionType, ...rest }: Props) { const href = useTransactionsOverviewHref({ serviceName, latencyAggregationType, + transactionType, }); return ; } diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx index 2c086dbb17222..310f5a165a5c8 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx @@ -9,6 +9,13 @@ import { ValuesType } from 'utility-types'; import { RectAnnotation } from '@elastic/charts'; import { EuiTheme } from 'src/plugins/kibana_react/common'; import { rgba } from 'polished'; +import { + ALERT_DURATION, + RULE_ID, + ALERT_START, + ALERT_UUID, +} from '../../../../../../rule_registry/common/technical_rule_data_field_names'; +import { parseTechnicalFields } from '../../../../../../rule_registry/common'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; type Alert = ValuesType< @@ -30,10 +37,11 @@ export function getAlertAnnotations({ theme: EuiTheme; }) { return alerts?.flatMap((alert) => { - const uuid = alert['kibana.rac.alert.uuid']!; - const start = new Date(alert['kibana.rac.alert.start']!).getTime(); - const end = start + alert['kibana.rac.alert.duration.us']! / 1000; - const color = getAlertColor({ ruleId: alert['rule.id']!, theme }); + const parsed = parseTechnicalFields(alert); + const uuid = parsed[ALERT_UUID]!; + const start = new Date(parsed[ALERT_START]!).getTime(); + const end = start + parsed[ALERT_DURATION]! / 1000; + const color = getAlertColor({ ruleId: parsed[RULE_ID]!, theme }); return [ - alert['rule.id'] === AlertType.TransactionDuration || - alert['rule.id'] === AlertType.TransactionDurationAnomaly + alert[RULE_ID]?.[0] === AlertType.TransactionDuration || + alert[RULE_ID]?.[0] === AlertType.TransactionDurationAnomaly )} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index 9aefa55aaaa36..7eceaf5ca8e5d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -9,6 +9,7 @@ import { EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useParams } from 'react-router-dom'; +import { RULE_ID } from '../../../../../../rule_registry/common/technical_rule_data_field_names'; import { AlertType } from '../../../../../common/alert_types'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { asPercent } from '../../../../../common/utils/formatters'; @@ -152,7 +153,7 @@ export function TransactionErrorRateChart({ yDomain={{ min: 0, max: 1 }} customTheme={comparisonChartThem} alerts={alerts.filter( - (alert) => alert['rule.id'] === AlertType.TransactionErrorRate + (alert) => alert[RULE_ID]?.[0] === AlertType.TransactionErrorRate )} /> diff --git a/x-pack/plugins/apm/public/components/shared/transaction_type_select.tsx b/x-pack/plugins/apm/public/components/shared/transaction_type_select.tsx index 9353c37b90728..e257eb3322b9b 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_type_select.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_type_select.tsx @@ -38,6 +38,7 @@ export function TransactionTypeSelect() { return ( <> = { [UI_SETTINGS.TIMEPICKER_QUICK_RANGES]: [ @@ -77,11 +77,6 @@ const mockCore = { }, }; -const mockApmRuleRegistry = ({ - getTypeByRuleId: () => undefined, - registerType: () => undefined, -} as unknown) as ApmRuleRegistry; - const mockConfig: ConfigSchema = { serviceMapEnabled: true, ui: { @@ -116,7 +111,7 @@ export const mockApmPluginContextValue = { config: mockConfig, core: mockCore, plugins: mockPlugin, - apmRuleRegistry: mockApmRuleRegistry, + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), }; export function MockApmPluginContextWrapper({ diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index f7bbe647d8e37..b493363d98f7a 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -34,19 +34,15 @@ import type { HasDataParams, ObservabilityPublicSetup, } from '../../observability/public'; -import { FormatterRuleRegistry } from '../../observability/public'; import type { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '../../triggers_actions_ui/public'; -import { apmRuleRegistrySettings } from '../common/rules/apm_rule_registry_settings'; -import type { APMRuleFieldMap } from '../common/rules/apm_rule_field_map'; import { registerApmAlerts } from './components/alerting/register_apm_alerts'; import { featureCatalogueEntry } from './featureCatalogueEntry'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; export type ApmPluginSetup = ReturnType; -export type ApmRuleRegistry = ApmPluginSetup['ruleRegistry']; export type ApmPluginStart = void; @@ -87,11 +83,6 @@ export class ApmPlugin implements Plugin { pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); } - const apmRuleRegistry = plugins.observability.ruleRegistry.create({ - ...apmRuleRegistrySettings, - fieldMap: {} as APMRuleFieldMap, - ctor: FormatterRuleRegistry, - }); const getApmDataHelper = async () => { const { fetchObservabilityOverviewPageData, @@ -127,6 +118,8 @@ export class ApmPlugin implements Plugin { return { fetchUxOverviewDate, hasRumData }; }; + const { observabilityRuleTypeRegistry } = plugins.observability; + plugins.observability.dashboard.register({ appName: 'ux', hasData: async (params?: HasDataParams) => { @@ -187,12 +180,12 @@ export class ApmPlugin implements Plugin { appMountParameters, config, pluginsStart: pluginsStart as ApmPluginStartDeps, - apmRuleRegistry, + observabilityRuleTypeRegistry, }); }, }); - registerApmAlerts(apmRuleRegistry); + registerApmAlerts(observabilityRuleTypeRegistry); core.application.register({ id: 'ux', @@ -231,14 +224,12 @@ export class ApmPlugin implements Plugin { appMountParameters, config, corePlugins: corePlugins as ApmPluginStartDeps, - apmRuleRegistry, + observabilityRuleTypeRegistry, }); }, }); - return { - ruleRegistry: apmRuleRegistry, - }; + return {}; } public start(core: CoreStart, plugins: ApmPluginStartDeps) { toggleAppLinkInNav(core, this.initializerContext.config.get()); diff --git a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts index 9a362efa90ac0..022fad6fa7840 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts @@ -7,17 +7,19 @@ import { Observable } from 'rxjs'; import { Logger } from 'kibana/server'; +import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../alerting/server'; +import { RuleDataClient } from '../../../../rule_registry/server'; import { registerTransactionDurationAlertType } from './register_transaction_duration_alert_type'; import { registerTransactionDurationAnomalyAlertType } from './register_transaction_duration_anomaly_alert_type'; import { registerErrorCountAlertType } from './register_error_count_alert_type'; import { APMConfig } from '../..'; import { MlPluginSetup } from '../../../../ml/server'; import { registerTransactionErrorRateAlertType } from './register_transaction_error_rate_alert_type'; -import { APMRuleRegistry } from '../../plugin'; export interface RegisterRuleDependencies { - registry: APMRuleRegistry; + ruleDataClient: RuleDataClient; ml?: MlPluginSetup; + alerting: AlertingPluginSetupContract; config$: Observable; logger: Logger; } diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index 15ec5d0ef0bd0..992393f36631c 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -7,6 +7,11 @@ import { schema } from '@kbn/config-schema'; import { take } from 'rxjs/operators'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; @@ -21,7 +26,6 @@ import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; import { RegisterRuleDependencies } from './register_apm_alerts'; -import { createAPMLifecycleRuleType } from './create_apm_lifecycle_rule_type'; const paramsSchema = schema.object({ windowSize: schema.number(), @@ -34,11 +38,18 @@ const paramsSchema = schema.object({ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.ErrorCount]; export function registerErrorCountAlertType({ - registry, + alerting, + logger, + ruleDataClient, config$, }: RegisterRuleDependencies) { - registry.registerType( - createAPMLifecycleRuleType({ + const createLifecycleRuleType = createLifecycleRuleTypeFactory({ + ruleDataClient, + logger, + }); + + alerting.registerType( + createLifecycleRuleType({ id: AlertType.ErrorCount, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, @@ -146,9 +157,8 @@ export function registerErrorCountAlertType({ ? { [SERVICE_ENVIRONMENT]: environment } : {}), [PROCESSOR_EVENT]: ProcessorEvent.error, - 'kibana.observability.evaluation.value': errorCount, - 'kibana.observability.evaluation.threshold': - alertParams.threshold, + [ALERT_EVALUATION_VALUE]: errorCount, + [ALERT_EVALUATION_THRESHOLD]: alertParams.threshold, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index 4918a6cc892b7..ec60ef7916e04 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -8,6 +8,11 @@ import { schema } from '@kbn/config-schema'; import { take } from 'rxjs/operators'; import { QueryContainer } from '@elastic/elasticsearch/api/types'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; import { parseEnvironmentUrlParam } from '../../../common/environment_filter_values'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; import { @@ -24,7 +29,6 @@ import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; import { RegisterRuleDependencies } from './register_apm_alerts'; -import { createAPMLifecycleRuleType } from './create_apm_lifecycle_rule_type'; const paramsSchema = schema.object({ serviceName: schema.string(), @@ -43,130 +47,142 @@ const paramsSchema = schema.object({ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.TransactionDuration]; export function registerTransactionDurationAlertType({ - registry, + alerting, + ruleDataClient, config$, + logger, }: RegisterRuleDependencies) { - registry.registerType( - createAPMLifecycleRuleType({ - id: AlertType.TransactionDuration, - name: alertTypeConfig.name, - actionGroups: alertTypeConfig.actionGroups, - defaultActionGroupId: alertTypeConfig.defaultActionGroupId, - validate: { - params: paramsSchema, - }, - actionVariables: { - context: [ - apmActionVariables.serviceName, - apmActionVariables.transactionType, - apmActionVariables.environment, - apmActionVariables.threshold, - apmActionVariables.triggerValue, - apmActionVariables.interval, - ], - }, - producer: 'apm', - minimumLicenseRequired: 'basic', - executor: async ({ services, params }) => { - const config = await config$.pipe(take(1)).toPromise(); - const alertParams = params; - const indices = await getApmIndices({ - config, - savedObjectsClient: services.savedObjectsClient, - }); - - const searchParams = { - index: indices['apm_oss.transactionIndices'], - body: { - size: 0, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, - }, + const createLifecycleRuleType = createLifecycleRuleTypeFactory({ + ruleDataClient, + logger, + }); + + const type = createLifecycleRuleType({ + id: AlertType.TransactionDuration, + name: alertTypeConfig.name, + actionGroups: alertTypeConfig.actionGroups, + defaultActionGroupId: alertTypeConfig.defaultActionGroupId, + validate: { + params: paramsSchema, + }, + actionVariables: { + context: [ + apmActionVariables.serviceName, + apmActionVariables.transactionType, + apmActionVariables.environment, + apmActionVariables.threshold, + apmActionVariables.triggerValue, + apmActionVariables.interval, + ], + }, + producer: 'apm', + minimumLicenseRequired: 'basic', + executor: async ({ services, params }) => { + const config = await config$.pipe(take(1)).toPromise(); + const alertParams = params; + const indices = await getApmIndices({ + config, + savedObjectsClient: services.savedObjectsClient, + }); + + const searchParams = { + index: indices['apm_oss.transactionIndices'], + body: { + size: 0, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: `now-${alertParams.windowSize}${alertParams.windowUnit}`, }, }, - { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, - { term: { [SERVICE_NAME]: alertParams.serviceName } }, - { term: { [TRANSACTION_TYPE]: alertParams.transactionType } }, - ...environmentQuery(alertParams.environment), - ] as QueryContainer[], - }, + }, + { + term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction }, + }, + { term: { [SERVICE_NAME]: alertParams.serviceName } }, + { + term: { + [TRANSACTION_TYPE]: alertParams.transactionType, + }, + }, + ...environmentQuery(alertParams.environment), + ] as QueryContainer[], }, - aggs: { - latency: - alertParams.aggregationType === 'avg' - ? { avg: { field: TRANSACTION_DURATION } } - : { - percentiles: { - field: TRANSACTION_DURATION, - percents: [ - alertParams.aggregationType === '95th' ? 95 : 99, - ], - }, + }, + aggs: { + latency: + alertParams.aggregationType === 'avg' + ? { avg: { field: TRANSACTION_DURATION } } + : { + percentiles: { + field: TRANSACTION_DURATION, + percents: [ + alertParams.aggregationType === '95th' ? 95 : 99, + ], }, - }, + }, }, - }; - - const response = await alertingEsClient({ - scopedClusterClient: services.scopedClusterClient, - params: searchParams, - }); - - if (!response.aggregations) { - return {}; - } - - const { latency } = response.aggregations; - - const transactionDuration = - 'values' in latency - ? Object.values(latency.values)[0] - : latency?.value; - - const threshold = alertParams.threshold * 1000; - - if (transactionDuration && transactionDuration > threshold) { - const durationFormatter = getDurationFormatter(transactionDuration); - const transactionDurationFormatted = durationFormatter( - transactionDuration - ).formatted; - - const environmentParsed = parseEnvironmentUrlParam( - alertParams.environment - ); - - services - .alertWithLifecycle({ - id: `${AlertType.TransactionDuration}_${environmentParsed.text}`, - fields: { - [SERVICE_NAME]: alertParams.serviceName, - ...(environmentParsed.esFieldValue - ? { [SERVICE_ENVIRONMENT]: environmentParsed.esFieldValue } - : {}), - [TRANSACTION_TYPE]: alertParams.transactionType, - [PROCESSOR_EVENT]: ProcessorEvent.transaction, - 'kibana.observability.evaluation.value': transactionDuration, - 'kibana.observability.evaluation.threshold': - alertParams.threshold * 1000, - }, - }) - .scheduleActions(alertTypeConfig.defaultActionGroupId, { - transactionType: alertParams.transactionType, - serviceName: alertParams.serviceName, - environment: environmentParsed.text, - threshold, - triggerValue: transactionDurationFormatted, - interval: `${alertParams.windowSize}${alertParams.windowUnit}`, - }); - } + }, + }; + const response = await alertingEsClient({ + scopedClusterClient: services.scopedClusterClient, + params: searchParams, + }); + + if (!response.aggregations) { return {}; - }, - }) - ); + } + + const { latency } = response.aggregations; + + const transactionDuration = + 'values' in latency ? Object.values(latency.values)[0] : latency?.value; + + const threshold = alertParams.threshold * 1000; + + if (transactionDuration && transactionDuration > threshold) { + const durationFormatter = getDurationFormatter(transactionDuration); + const transactionDurationFormatted = durationFormatter( + transactionDuration + ).formatted; + + const environmentParsed = parseEnvironmentUrlParam( + alertParams.environment + ); + + services + .alertWithLifecycle({ + id: `${AlertType.TransactionDuration}_${environmentParsed.text}`, + fields: { + [SERVICE_NAME]: alertParams.serviceName, + ...(environmentParsed.esFieldValue + ? { + [SERVICE_ENVIRONMENT]: environmentParsed.esFieldValue, + } + : {}), + [TRANSACTION_TYPE]: alertParams.transactionType, + [PROCESSOR_EVENT]: ProcessorEvent.transaction, + [ALERT_EVALUATION_VALUE]: transactionDuration, + [ALERT_EVALUATION_THRESHOLD]: alertParams.threshold * 1000, + }, + }) + .scheduleActions(alertTypeConfig.defaultActionGroupId, { + transactionType: alertParams.transactionType, + serviceName: alertParams.serviceName, + environment: environmentParsed.text, + threshold, + triggerValue: transactionDurationFormatted, + interval: `${alertParams.windowSize}${alertParams.windowUnit}`, + }); + } + + return {}; + }, + }); + + alerting.registerType(type); } diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts index 67ff7cdb8e4e0..5b093671f70f5 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { compact } from 'lodash'; import { ESSearchResponse } from 'typings/elasticsearch'; import { QueryContainer } from '@elastic/elasticsearch/api/types'; +import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; import { ProcessorEvent } from '../../../common/processor_event'; import { getSeverity } from '../../../common/anomaly_detection'; import { @@ -29,7 +30,12 @@ import { getMLJobs } from '../service_map/get_service_anomalies'; import { apmActionVariables } from './action_variables'; import { RegisterRuleDependencies } from './register_apm_alerts'; import { parseEnvironmentUrlParam } from '../../../common/environment_filter_values'; -import { createAPMLifecycleRuleType } from './create_apm_lifecycle_rule_type'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_SEVERITY_LEVEL, + ALERT_SEVERITY_VALUE, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; const paramsSchema = schema.object({ serviceName: schema.maybe(schema.string()), @@ -49,11 +55,18 @@ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.TransactionDurationAnomaly]; export function registerTransactionDurationAnomalyAlertType({ - registry, + logger, + ruleDataClient, + alerting, ml, }: RegisterRuleDependencies) { - registry.registerType( - createAPMLifecycleRuleType({ + const createLifecycleRuleType = createLifecycleRuleTypeFactory({ + logger, + ruleDataClient, + }); + + alerting.registerType( + createLifecycleRuleType({ id: AlertType.TransactionDurationAnomaly, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, @@ -190,7 +203,7 @@ export function registerTransactionDurationAnomalyAlertType({ const job = mlJobs.find((j) => j.job_id === latest.job_id); if (!job) { - services.logger.warn( + logger.warn( `Could not find matching job for job id ${latest.job_id}` ); return undefined; @@ -231,10 +244,10 @@ export function registerTransactionDurationAnomalyAlertType({ : {}), [TRANSACTION_TYPE]: transactionType, [PROCESSOR_EVENT]: ProcessorEvent.transaction, - 'kibana.rac.alert.severity.level': severityLevel, - 'kibana.rac.alert.severity.value': score, - 'kibana.observability.evaluation.value': score, - 'kibana.observability.evaluation.threshold': threshold, + [ALERT_SEVERITY_LEVEL]: severityLevel, + [ALERT_SEVERITY_VALUE]: score, + [ALERT_EVALUATION_VALUE]: score, + [ALERT_EVALUATION_THRESHOLD]: threshold, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts index bead17e308f06..3c6197c8a02a1 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts @@ -7,6 +7,11 @@ import { schema } from '@kbn/config-schema'; import { take } from 'rxjs/operators'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; import { EVENT_OUTCOME, @@ -22,7 +27,6 @@ import { environmentQuery } from '../../../server/utils/queries'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; -import { createAPMLifecycleRuleType } from './create_apm_lifecycle_rule_type'; import { RegisterRuleDependencies } from './register_apm_alerts'; const paramsSchema = schema.object({ @@ -37,11 +41,18 @@ const paramsSchema = schema.object({ const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.TransactionErrorRate]; export function registerTransactionErrorRateAlertType({ - registry, + alerting, + ruleDataClient, + logger, config$, }: RegisterRuleDependencies) { - registry.registerType( - createAPMLifecycleRuleType({ + const createLifecycleRuleType = createLifecycleRuleTypeFactory({ + ruleDataClient, + logger, + }); + + alerting.registerType( + createLifecycleRuleType({ id: AlertType.TransactionErrorRate, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, @@ -183,9 +194,8 @@ export function registerTransactionErrorRateAlertType({ ...(environment ? { [SERVICE_ENVIRONMENT]: environment } : {}), [TRANSACTION_TYPE]: transactionType, [PROCESSOR_EVENT]: ProcessorEvent.transaction, - 'kibana.observability.evaluation.value': errorRate, - 'kibana.observability.evaluation.threshold': - alertParams.threshold, + [ALERT_EVALUATION_VALUE]: errorRate, + [ALERT_EVALUATION_THRESHOLD]: alertParams.threshold, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { diff --git a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts index 37b3e282d0a59..ce1466bff01a9 100644 --- a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts @@ -8,8 +8,9 @@ import { Logger } from 'kibana/server'; import { of } from 'rxjs'; import { elasticsearchServiceMock } from 'src/core/server/mocks'; +import type { RuleDataClient } from '../../../../../rule_registry/server'; +import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../../alerting/server'; import { APMConfig } from '../../..'; -import { APMRuleRegistry } from '../../../plugin'; export const createRuleTypeMocks = () => { let alertExecutor: (...args: any[]) => Promise; @@ -27,19 +28,16 @@ export const createRuleTypeMocks = () => { error: jest.fn(), } as unknown) as Logger; - const registry = { + const alerting = { registerType: ({ executor }) => { alertExecutor = executor; }, - } as APMRuleRegistry; + } as AlertingPluginSetupContract; const scheduleActions = jest.fn(); const services = { scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), - scopedRuleRegistryClient: { - bulkIndex: jest.fn(), - }, alertInstanceFactory: jest.fn(() => ({ scheduleActions })), alertWithLifecycle: jest.fn(), logger: loggerMock, @@ -47,9 +45,21 @@ export const createRuleTypeMocks = () => { return { dependencies: { - registry, + alerting, config$: mockedConfig$, logger: loggerMock, + ruleDataClient: ({ + getReader: () => { + return { + search: jest.fn(), + }; + }, + getWriter: () => { + return { + bulk: jest.fn(), + }; + }, + } as unknown) as RuleDataClient, }, services, scheduleActions, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts index 6356731cc48d1..42a95afc65ad3 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts @@ -5,33 +5,30 @@ * 2.0. */ +import { ALERT_UUID } from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { RuleDataClient } from '../../../../rule_registry/server'; import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import type { PromiseReturnType } from '../../../../observability/typings/common'; -import type { APMRuleRegistry } from '../../plugin'; import { environmentQuery, rangeQuery } from '../../utils/queries'; export async function getServiceAlerts({ - apmRuleRegistryClient, + ruleDataClient, start, end, serviceName, environment, transactionType, }: { - apmRuleRegistryClient: Exclude< - PromiseReturnType, - undefined - >; + ruleDataClient: RuleDataClient; start: number; end: number; serviceName: string; environment?: string; transactionType: string; }) { - const response = await apmRuleRegistryClient.search({ + const response = await ruleDataClient.getReader().search({ body: { query: { bool: { @@ -68,13 +65,14 @@ export async function getServiceAlerts({ size: 100, fields: ['*'], collapse: { - field: 'kibana.rac.alert.uuid', + field: ALERT_UUID, }, sort: { '@timestamp': 'desc', }, }, + allow_no_indices: true, }); - return response.events; + return response.hits.hits.map((hit) => hit.fields); } diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index e12d089855834..44334889128c4 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -16,7 +16,10 @@ import { Plugin, PluginInitializerContext, } from 'src/core/server'; -import { mapValues } from 'lodash'; +import { mapValues, once } from 'lodash'; +import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets'; +import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; +import { RuleDataClient } from '../../rule_registry/server'; import { APMConfig, APMXPackConfig } from '.'; import { mergeConfigs } from './index'; import { UI_SETTINGS } from '../../../../src/plugins/data/common'; @@ -42,10 +45,12 @@ import { } from './types'; import { registerRoutes } from './routes/register_routes'; import { getGlobalApmServerRouteRepository } from './routes/get_global_apm_server_route_repository'; -import { apmRuleRegistrySettings } from '../common/rules/apm_rule_registry_settings'; -import { apmRuleFieldMap } from '../common/rules/apm_rule_field_map'; - -export type APMRuleRegistry = ReturnType['ruleRegistry']; +import { + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_TYPE, +} from '../common/elasticsearch_fieldnames'; export class APMPlugin implements @@ -124,20 +129,81 @@ export class APMPlugin registerFeaturesUsage({ licensingPlugin: plugins.licensing }); - const apmRuleRegistry = plugins.observability.ruleRegistry.create({ - ...apmRuleRegistrySettings, - fieldMap: apmRuleFieldMap, + const getCoreStart = () => + core.getStartServices().then(([coreStart]) => coreStart); + + const ready = once(async () => { + const componentTemplateName = plugins.ruleRegistry.getFullAssetName( + 'apm-mappings' + ); + + if (!plugins.ruleRegistry.isWriteEnabled()) { + return; + } + + await plugins.ruleRegistry.createOrUpdateComponentTemplate({ + name: componentTemplateName, + body: { + template: { + settings: { + number_of_shards: 1, + }, + mappings: mappingFromFieldMap({ + [SERVICE_NAME]: { + type: 'keyword', + }, + [SERVICE_ENVIRONMENT]: { + type: 'keyword', + }, + [TRANSACTION_TYPE]: { + type: 'keyword', + }, + [PROCESSOR_EVENT]: { + type: 'keyword', + }, + }), + }, + }, + }); + + await plugins.ruleRegistry.createOrUpdateIndexTemplate({ + name: plugins.ruleRegistry.getFullAssetName('apm-index-template'), + body: { + index_patterns: [ + plugins.ruleRegistry.getFullAssetName('observability-apm*'), + ], + composed_of: [ + plugins.ruleRegistry.getFullAssetName( + TECHNICAL_COMPONENT_TEMPLATE_NAME + ), + componentTemplateName, + ], + }, + }); + }); + + ready().catch((err) => { + this.logger!.error(err); + }); + + const ruleDataClient = new RuleDataClient({ + alias: plugins.ruleRegistry.getFullAssetName('observability-apm'), + getClusterClient: async () => { + const coreStart = await getCoreStart(); + return coreStart.elasticsearch.client.asInternalUser; + }, + ready, }); registerRoutes({ core: { setup: core, - start: () => core.getStartServices().then(([coreStart]) => coreStart), + start: getCoreStart, }, logger: this.logger, config: currentConfig, repository: getGlobalApmServerRouteRepository(), - apmRuleRegistry, + ruleDataClient, plugins: mapValues(plugins, (value, key) => { return { setup: value, @@ -157,12 +223,16 @@ export class APMPlugin savedObjectsClient: await getInternalSavedObjectsClient(core), config: await mergedConfig$.pipe(take(1)).toPromise(), }); - registerApmAlerts({ - registry: apmRuleRegistry, - ml: plugins.ml, - config$: mergedConfig$, - logger: this.logger!.get('rule'), - }); + + if (plugins.alerting) { + registerApmAlerts({ + ruleDataClient, + alerting: plugins.alerting, + ml: plugins.ml, + config$: mergedConfig$, + logger: this.logger!.get('rule'), + }); + } return { config$: mergedConfig$, @@ -193,7 +263,6 @@ export class APMPlugin }, }); }, - ruleRegistry: apmRuleRegistry, }; } diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.ts b/x-pack/plugins/apm/server/routes/register_routes/index.ts index f792e078c528a..c9df12fd58208 100644 --- a/x-pack/plugins/apm/server/routes/register_routes/index.ts +++ b/x-pack/plugins/apm/server/routes/register_routes/index.ts @@ -39,14 +39,14 @@ export function registerRoutes({ plugins, logger, config, - apmRuleRegistry, + ruleDataClient, }: { core: APMRouteHandlerResources['core']; plugins: APMRouteHandlerResources['plugins']; logger: APMRouteHandlerResources['logger']; repository: ServerRouteRepository; config: APMRouteHandlerResources['config']; - apmRuleRegistry: APMRouteHandlerResources['apmRuleRegistry']; + ruleDataClient: APMRouteHandlerResources['ruleDataClient']; }) { const routes = repository.getRoutes(); @@ -99,7 +99,7 @@ export function registerRoutes({ }, validatedParams ), - apmRuleRegistry, + ruleDataClient, })) as any; if (Array.isArray(data)) { diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 54e59f2be7ae3..4384d2be78ca0 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -737,30 +737,15 @@ const serviceAlertsRoute = createApmServerRoute({ options: { tags: ['access:apm'], }, - handler: async ({ context, params, apmRuleRegistry }) => { - const alertsClient = context.alerting.getAlertsClient(); - + handler: async ({ context, params, ruleDataClient }) => { const { query: { start, end, environment, transactionType }, path: { serviceName }, } = params; - const apmRuleRegistryClient = await apmRuleRegistry.createScopedRuleRegistryClient( - { - alertsClient, - context, - } - ); - - if (!apmRuleRegistryClient) { - throw Boom.failedDependency( - 'xpack.ruleRegistry.unsafe.write.enabled is set to false' - ); - } - return { alerts: await getServiceAlerts({ - apmRuleRegistryClient, + ruleDataClient, start, end, serviceName, diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 602e1f3e0edb9..13bd631085aac 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -12,11 +12,11 @@ import { KibanaRequest, CoreStart, } from 'src/core/server'; +import { RuleDataClient } from '../../../rule_registry/server'; import { AlertingApiRequestHandlerContext } from '../../../alerting/server'; import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; import { APMConfig } from '..'; import { APMPluginDependencies } from '../types'; -import { APMRuleRegistry } from '../plugin'; export interface ApmPluginRequestHandlerContext extends RequestHandlerContext { licensing: LicensingApiRequestHandlerContext; @@ -62,5 +62,5 @@ export interface APMRouteHandlerResources { start: () => Promise[key]['start']>; }; }; - apmRuleRegistry: APMRuleRegistry; + ruleDataClient: RuleDataClient; } diff --git a/x-pack/plugins/apm/server/types.ts b/x-pack/plugins/apm/server/types.ts index dbc220f9f6b15..a5ba4f39b32b3 100644 --- a/x-pack/plugins/apm/server/types.ts +++ b/x-pack/plugins/apm/server/types.ts @@ -7,6 +7,10 @@ import { ValuesType } from 'utility-types'; import { Observable } from 'rxjs'; import { CoreSetup, CoreStart, KibanaRequest } from 'kibana/server'; +import { + RuleRegistryPluginSetupContract, + RuleRegistryPluginStartContract, +} from '../../rule_registry/server'; import { PluginSetup as DataPluginSetup, PluginStart as DataPluginStart, @@ -115,6 +119,10 @@ interface DependencyMap { setup: DataPluginSetup; start: DataPluginStart; }; + ruleRegistry: { + setup: RuleRegistryPluginSetupContract; + start: RuleRegistryPluginStartContract; + }; } const requiredDependencies = [ @@ -126,6 +134,7 @@ const requiredDependencies = [ 'embeddable', 'infra', 'observability', + 'ruleRegistry', ] as const; const optionalDependencies = [ diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts new file mode 100644 index 0000000000000..97aa934280414 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts @@ -0,0 +1,94 @@ +/* + * 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 { + ExpressionFunctionDefinition, + ExpressionValueFilter, +} from 'src/plugins/expressions/common'; + +// @ts-expect-error untyped local +import { buildESRequest } from '../../../common/lib/request/build_es_request'; + +import { searchService } from '../../../public/services'; + +import { getFunctionHelp } from '../../../i18n'; + +interface Arguments { + index: string | null; + query: string; +} + +export function escount(): ExpressionFunctionDefinition< + 'escount', + ExpressionValueFilter, + Arguments, + any +> { + const { help, args: argHelp } = getFunctionHelp().escount; + + return { + name: 'escount', + type: 'number', + context: { + types: ['filter'], + }, + help, + args: { + query: { + types: ['string'], + aliases: ['_', 'q'], + help: argHelp.query, + default: '"-_index:.kibana"', + }, + index: { + types: ['string'], + default: '_all', + help: argHelp.index, + }, + }, + fn: (input, args, handlers) => { + input.and = input.and.concat([ + { + type: 'filter', + filterType: 'luceneQueryString', + query: args.query, + and: [], + }, + ]); + + const esRequest = buildESRequest( + { + index: args.index, + body: { + track_total_hits: true, + size: 0, + query: { + bool: { + must: [{ match_all: {} }], + }, + }, + }, + }, + input + ); + + const search = searchService.getService().search; + const req = { + params: { + ...esRequest, + }, + }; + + return search + .search(req) + .toPromise() + .then((resp: any) => { + return resp.rawResponse.hits.total; + }); + }, + }; +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts new file mode 100644 index 0000000000000..c40e1ffd62439 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts @@ -0,0 +1,141 @@ +/* + * 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 { + ExpressionFunctionDefinition, + ExpressionValueFilter, +} from 'src/plugins/expressions/common'; + +// @ts-expect-error untyped local +import { buildESRequest } from '../../../common/lib/request/build_es_request'; + +import { searchService } from '../../../public/services'; +import { ESSQL_SEARCH_STRATEGY } from '../../../common/lib/constants'; +import { EssqlSearchStrategyRequest, EssqlSearchStrategyResponse } from '../../../types'; +import { getFunctionHelp } from '../../../i18n'; + +interface Arguments { + index: string; + query: string; + sort: string; + fields: string; + metaFields: string; + count: number; +} + +export function esdocs(): ExpressionFunctionDefinition< + 'esdocs', + ExpressionValueFilter, + Arguments, + any +> { + const { help, args: argHelp } = getFunctionHelp().esdocs; + + return { + name: 'esdocs', + type: 'datatable', + context: { + types: ['filter'], + }, + help, + args: { + query: { + types: ['string'], + aliases: ['_', 'q'], + help: argHelp.query, + default: '-_index:.kibana', + }, + count: { + types: ['number'], + default: 1000, + help: argHelp.count, + }, + fields: { + help: argHelp.fields, + types: ['string'], + }, + index: { + types: ['string'], + default: '_all', + help: argHelp.index, + }, + // TODO: This arg isn't being used in the function. + // We need to restore this functionality or remove it as an arg. + metaFields: { + help: argHelp.metaFields, + types: ['string'], + }, + sort: { + types: ['string'], + help: argHelp.sort, + }, + }, + fn: async (input, args, handlers) => { + const { count, index, fields, sort } = args; + + input.and = input.and.concat([ + { + type: 'filter', + filterType: 'luceneQueryString', + query: args.query, + and: [], + }, + ]); + + // Load ad-hoc to avoid adding to the page load bundle size + const squel = await import('safe-squel'); + + let query = squel.select({ + autoQuoteTableNames: true, + autoQuoteFieldNames: true, + autoQuoteAliasNames: true, + nameQuoteCharacter: '"', + }); + + if (index) { + query.from(index); + } + + if (fields) { + const allFields = fields.split(',').map((field) => field.trim()); + allFields.forEach((field) => (query = query.field(field))); + } + + if (sort) { + const [sortField, sortOrder] = sort.split(',').map((str) => str.trim()); + if (sortField) { + query.order(`"${sortField}"`, sortOrder === 'asc'); + } + } + + const search = searchService.getService().search; + + const req = { + count, + query: query.toString(), + filter: input.and, + }; + + // We're requesting the data using the ESSQL strategy because + // the SQL routes return type information with the result set + return search + .search(req, { + strategy: ESSQL_SEARCH_STRATEGY, + }) + .toPromise() + .then((resp: EssqlSearchStrategyResponse) => { + return { + type: 'datatable', + meta: { + type: 'essql', + }, + ...resp, + }; + }); + }, + }; +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts new file mode 100644 index 0000000000000..1339c93032ea9 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts @@ -0,0 +1,103 @@ +/* + * 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 { + ExpressionFunctionDefinition, + ExpressionValueFilter, +} from 'src/plugins/expressions/common'; +import { searchService } from '../../../public/services'; +import { ESSQL_SEARCH_STRATEGY } from '../../../common/lib/constants'; +import { EssqlSearchStrategyRequest, EssqlSearchStrategyResponse } from '../../../types'; +import { getFunctionHelp } from '../../../i18n'; + +interface Arguments { + query: string; + parameter: Array; + count: number; + timezone: string; +} + +export function essql(): ExpressionFunctionDefinition< + 'essql', + ExpressionValueFilter, + Arguments, + any +> { + const { help, args: argHelp } = getFunctionHelp().essql; + + return { + name: 'essql', + type: 'datatable', + context: { + types: ['filter'], + }, + help, + args: { + query: { + aliases: ['_', 'q'], + types: ['string'], + help: argHelp.query, + }, + parameter: { + aliases: ['param'], + types: ['string', 'number', 'boolean'], + multi: true, + help: argHelp.parameter, + }, + count: { + types: ['number'], + help: argHelp.count, + default: 1000, + }, + timezone: { + aliases: ['tz'], + types: ['string'], + default: 'UTC', + help: argHelp.timezone, + }, + }, + fn: (input, args, handlers) => { + const search = searchService.getService().search; + const { parameter, ...restOfArgs } = args; + const req = { + ...restOfArgs, + params: parameter, + filter: input.and, + }; + + return search + .search(req, { + strategy: ESSQL_SEARCH_STRATEGY, + }) + .toPromise() + .then((resp: EssqlSearchStrategyResponse) => { + return { + type: 'datatable', + meta: { + type: 'essql', + }, + ...resp, + }; + }) + .catch((e) => { + let message = `Unexpected error from Elasticsearch: ${e.message}`; + if (e.err) { + const { type, reason } = e.err.attributes; + if (type === 'parsing_exception') { + message = `Couldn't parse Elasticsearch SQL query. You may need to add double quotes to names containing special characters. Check your query and try again. Error: ${reason}`; + } else { + message = `Unexpected error from Elasticsearch: ${type} - ${reason}`; + } + } + + // Re-write the error message before surfacing it up + e.message = message; + throw e; + }); + }, + }; +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts index 6e7c43135f414..2cfdebafb70df 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts @@ -10,5 +10,17 @@ import { functions as externalFunctions } from '../external'; import { location } from './location'; import { markdown } from './markdown'; import { urlparam } from './urlparam'; +import { escount } from './escount'; +import { esdocs } from './esdocs'; +import { essql } from './essql'; -export const functions = [location, markdown, urlparam, ...commonFunctions, ...externalFunctions]; +export const functions = [ + location, + markdown, + urlparam, + escount, + esdocs, + essql, + ...commonFunctions, + ...externalFunctions, +]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/escount.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/escount.ts index 206e47413ae56..95f5ef446a470 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/server/escount.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/server/escount.ts @@ -9,10 +9,9 @@ import { ExpressionFunctionDefinition, ExpressionValueFilter, } from 'src/plugins/expressions/common'; -/* eslint-disable */ // @ts-expect-error untyped local -import { buildESRequest } from '../../../server/lib/build_es_request'; -/* eslint-enable */ +import { buildESRequest } from '../../../common/lib/request/build_es_request'; + import { getFunctionHelp } from '../../../i18n'; interface Arguments { diff --git a/x-pack/plugins/canvas/common/lib/constants.ts b/x-pack/plugins/canvas/common/lib/constants.ts index 697389fe2ce7c..2b916033ce557 100644 --- a/x-pack/plugins/canvas/common/lib/constants.ts +++ b/x-pack/plugins/canvas/common/lib/constants.ts @@ -44,3 +44,4 @@ export const API_ROUTE_SHAREABLE_RUNTIME_DOWNLOAD = `/public/canvas/${SHAREABLE_ export const CANVAS_EMBEDDABLE_CLASSNAME = `canvasEmbeddable`; export const CONTEXT_MENU_TOP_BORDER_CLASSNAME = 'canvasContextMenu--topBorder'; export const API_ROUTE_FUNCTIONS = `${API_ROUTE}/fns`; +export const ESSQL_SEARCH_STRATEGY = 'essql'; diff --git a/x-pack/plugins/canvas/server/lib/build_bool_array.ts b/x-pack/plugins/canvas/common/lib/request/build_bool_array.ts similarity index 92% rename from x-pack/plugins/canvas/server/lib/build_bool_array.ts rename to x-pack/plugins/canvas/common/lib/request/build_bool_array.ts index 826449ca6ad39..c0d630b4c405e 100644 --- a/x-pack/plugins/canvas/server/lib/build_bool_array.ts +++ b/x-pack/plugins/canvas/common/lib/request/build_bool_array.ts @@ -6,7 +6,7 @@ */ import { getESFilter } from './get_es_filter'; -import { ExpressionValueFilter } from '../../types'; +import { ExpressionValueFilter } from '../../../types'; const compact = (arr: T[]) => (Array.isArray(arr) ? arr.filter((val) => Boolean(val)) : []); diff --git a/x-pack/plugins/canvas/server/lib/build_es_request.js b/x-pack/plugins/canvas/common/lib/request/build_es_request.js similarity index 100% rename from x-pack/plugins/canvas/server/lib/build_es_request.js rename to x-pack/plugins/canvas/common/lib/request/build_es_request.js diff --git a/x-pack/plugins/canvas/server/lib/filters.ts b/x-pack/plugins/canvas/common/lib/request/filters.ts similarity index 98% rename from x-pack/plugins/canvas/server/lib/filters.ts rename to x-pack/plugins/canvas/common/lib/request/filters.ts index 8c6b485c4ccae..f1465fe48bdcf 100644 --- a/x-pack/plugins/canvas/server/lib/filters.ts +++ b/x-pack/plugins/canvas/common/lib/request/filters.ts @@ -11,7 +11,7 @@ import { CanvasTimeFilter, CanvasLuceneFilter, CanvasExactlyFilter, -} from '../../types'; +} from '../../../types'; /* TODO: This could be pluggable diff --git a/x-pack/plugins/canvas/server/lib/format_response.js b/x-pack/plugins/canvas/common/lib/request/format_response.js similarity index 100% rename from x-pack/plugins/canvas/server/lib/format_response.js rename to x-pack/plugins/canvas/common/lib/request/format_response.js diff --git a/x-pack/plugins/canvas/server/lib/get_es_filter.ts b/x-pack/plugins/canvas/common/lib/request/get_es_filter.ts similarity index 93% rename from x-pack/plugins/canvas/server/lib/get_es_filter.ts rename to x-pack/plugins/canvas/common/lib/request/get_es_filter.ts index 85335a4be06d7..353a793adcd17 100644 --- a/x-pack/plugins/canvas/server/lib/get_es_filter.ts +++ b/x-pack/plugins/canvas/common/lib/request/get_es_filter.ts @@ -12,7 +12,7 @@ */ import { filters } from './filters'; -import { ExpressionValueFilter } from '../../types'; +import { ExpressionValueFilter } from '../../../types'; export function getESFilter(filter: ExpressionValueFilter) { if (!filter.filterType || !filters[filter.filterType]) { diff --git a/x-pack/plugins/canvas/server/lib/normalize_type.ts b/x-pack/plugins/canvas/common/lib/request/normalize_type.ts similarity index 100% rename from x-pack/plugins/canvas/server/lib/normalize_type.ts rename to x-pack/plugins/canvas/common/lib/request/normalize_type.ts diff --git a/x-pack/plugins/canvas/server/lib/sanitize_name.ts b/x-pack/plugins/canvas/common/lib/request/sanitize_name.ts similarity index 100% rename from x-pack/plugins/canvas/server/lib/sanitize_name.ts rename to x-pack/plugins/canvas/common/lib/request/sanitize_name.ts diff --git a/x-pack/plugins/canvas/i18n/functions/dict/escount.ts b/x-pack/plugins/canvas/i18n/functions/dict/escount.ts index d88156ba32ce4..af1337360ba6d 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/escount.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/escount.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { escount } from '../../../canvas_plugin_src/functions/server/escount'; +import { escount } from '../../../canvas_plugin_src/functions/browser/escount'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { ELASTICSEARCH, LUCENE } from '../../constants'; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/esdocs.ts b/x-pack/plugins/canvas/i18n/functions/dict/esdocs.ts index b78425de144ef..6be5acdb8bc90 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/esdocs.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/esdocs.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { esdocs } from '../../../canvas_plugin_src/functions/server/esdocs'; +import { esdocs } from '../../../canvas_plugin_src/functions/browser/esdocs'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { ELASTICSEARCH, LUCENE } from '../../constants'; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/essql.ts b/x-pack/plugins/canvas/i18n/functions/dict/essql.ts index cfe848455dc3f..6304db945fc3f 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/essql.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/essql.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { essql } from '../../../canvas_plugin_src/functions/server/essql'; +import { essql } from '../../../canvas_plugin_src/functions/browser/essql'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { ELASTICSEARCH, SQL, ISO8601, UTC } from '../../constants'; @@ -27,6 +27,12 @@ export const help: FunctionHelp> = { SQL, }, }), + parameter: i18n.translate('xpack.canvas.functions.essql.args.parameterHelpText', { + defaultMessage: 'A parameter to be passed to the {SQL} query.', + values: { + SQL, + }, + }), count: i18n.translate('xpack.canvas.functions.essql.args.countHelpText', { defaultMessage: 'The number of documents to retrieve. For better performance, use a smaller data set.', diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 750b542116a75..d31a5a18cecc1 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -22,7 +22,7 @@ import { getSessionStorage } from './lib/storage'; import { SESSIONSTORAGE_LASTPATH } from '../common/lib/constants'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { ExpressionsSetup, ExpressionsStart } from '../../../../src/plugins/expressions/public'; -import { DataPublicPluginSetup } from '../../../../src/plugins/data/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; @@ -54,6 +54,7 @@ export interface CanvasStartDeps { inspector: InspectorStart; uiActions: UiActionsStart; charts: ChartsPluginStart; + data: DataPublicPluginStart; presentationUtil: PresentationUtilPluginStart; } diff --git a/x-pack/plugins/canvas/public/services/context.tsx b/x-pack/plugins/canvas/public/services/context.tsx index 4c18aa68fb51e..e078efe18b542 100644 --- a/x-pack/plugins/canvas/public/services/context.tsx +++ b/x-pack/plugins/canvas/public/services/context.tsx @@ -25,6 +25,7 @@ const defaultContextValue = { notify: {}, platform: {}, navLink: {}, + search: {}, }; const context = createContext(defaultContextValue as CanvasServices); @@ -54,6 +55,7 @@ export const ServicesProvider: FC<{ notify: specifiedProviders.notify.getService(), platform: specifiedProviders.platform.getService(), navLink: specifiedProviders.navLink.getService(), + search: specifiedProviders.search.getService(), reporting: specifiedProviders.reporting.getService(), labs: specifiedProviders.labs.getService(), }; diff --git a/x-pack/plugins/canvas/public/services/expressions.ts b/x-pack/plugins/canvas/public/services/expressions.ts index 131919e1eefea..fd733862c4b67 100644 --- a/x-pack/plugins/canvas/public/services/expressions.ts +++ b/x-pack/plugins/canvas/public/services/expressions.ts @@ -24,6 +24,11 @@ export const expressionsServiceFactory: CanvasServiceFactory const loadServerFunctionWrappers = async () => { if (!cached) { cached = (async () => { + const labService = startPlugins.presentationUtil.labsService; + const useDataSearchProject = labService.getProject('labs:canvas:useDataService'); + const hasDataSearch = useDataSearchProject.status.isEnabled; + const dataSearchFns = ['essql', 'esdocs', 'escount']; + const serverFunctionList = await coreSetup.http.get(API_ROUTE_FUNCTIONS); const batchedFunction = bfetch.batchedFunction({ url: API_ROUTE_FUNCTIONS }); const { serialize } = serializeProvider(expressions.getTypes()); @@ -32,9 +37,16 @@ export const expressionsServiceFactory: CanvasServiceFactory // function that matches its definition, but which simply // calls the server-side function endpoint. Object.keys(serverFunctionList).forEach((functionName) => { - if (expressions.getFunction(functionName)) { + // Allow function to be overwritten if we want to use + // the server-hosted essql, esdocs, and escount functions + if (dataSearchFns.includes(functionName)) { + if (hasDataSearch && expressions.getFunction(functionName)) { + return; + } + } else if (expressions.getFunction(functionName)) { return; } + const fn = () => ({ ...serverFunctionList[functionName], fn: (input: any, args: any) => { diff --git a/x-pack/plugins/canvas/public/services/index.ts b/x-pack/plugins/canvas/public/services/index.ts index 1566d6f28085a..cbe7de43eff95 100644 --- a/x-pack/plugins/canvas/public/services/index.ts +++ b/x-pack/plugins/canvas/public/services/index.ts @@ -13,10 +13,12 @@ import { platformServiceFactory } from './platform'; import { navLinkServiceFactory } from './nav_link'; import { embeddablesServiceFactory } from './embeddables'; import { expressionsServiceFactory } from './expressions'; +import { searchServiceFactory } from './search'; import { labsServiceFactory } from './labs'; import { reportingServiceFactory } from './reporting'; export { NotifyService } from './notify'; +export { SearchService } from './search'; export { PlatformService } from './platform'; export { NavLinkService } from './nav_link'; export { EmbeddablesService } from './embeddables'; @@ -80,6 +82,7 @@ export const services = { notify: new CanvasServiceProvider(notifyServiceFactory), platform: new CanvasServiceProvider(platformServiceFactory), navLink: new CanvasServiceProvider(navLinkServiceFactory), + search: new CanvasServiceProvider(searchServiceFactory), reporting: new CanvasServiceProvider(reportingServiceFactory), labs: new CanvasServiceProvider(labsServiceFactory), }; @@ -92,6 +95,7 @@ export interface CanvasServices { notify: ServiceFromProvider; platform: ServiceFromProvider; navLink: ServiceFromProvider; + search: ServiceFromProvider; reporting: ServiceFromProvider; labs: ServiceFromProvider; } @@ -120,5 +124,6 @@ export const { platform: platformService, navLink: navLinkService, expressions: expressionsService, + search: searchService, reporting: reportingService, } = services; diff --git a/x-pack/plugins/canvas/public/services/search.ts b/x-pack/plugins/canvas/public/services/search.ts new file mode 100644 index 0000000000000..0fe5c89c77096 --- /dev/null +++ b/x-pack/plugins/canvas/public/services/search.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { CanvasServiceFactory } from '.'; + +export interface SearchService { + search: DataPublicPluginStart['search']; +} + +export const searchServiceFactory: CanvasServiceFactory = ( + setup, + start, + canvasSetup, + canvasStart +) => { + return { + search: canvasStart.data.search, + }; +}; diff --git a/x-pack/plugins/canvas/public/services/stubs/index.ts b/x-pack/plugins/canvas/public/services/stubs/index.ts index 786582ed94bd2..7246a34d7f491 100644 --- a/x-pack/plugins/canvas/public/services/stubs/index.ts +++ b/x-pack/plugins/canvas/public/services/stubs/index.ts @@ -13,6 +13,7 @@ import { navLinkService } from './nav_link'; import { notifyService } from './notify'; import { labsService } from './labs'; import { platformService } from './platform'; +import { searchService } from './search'; export const stubs: CanvasServices = { embeddables: embeddablesService, @@ -21,6 +22,7 @@ export const stubs: CanvasServices = { navLink: navLinkService, notify: notifyService, platform: platformService, + search: searchService, labs: labsService, }; diff --git a/x-pack/plugins/canvas/public/services/stubs/search.ts b/x-pack/plugins/canvas/public/services/stubs/search.ts new file mode 100644 index 0000000000000..a4558a93e38a4 --- /dev/null +++ b/x-pack/plugins/canvas/public/services/stubs/search.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +const noop = (..._args: any[]): any => {}; + +export const searchService: any = { + search: noop, +}; diff --git a/x-pack/plugins/canvas/server/lib/essql_strategy.ts b/x-pack/plugins/canvas/server/lib/essql_strategy.ts new file mode 100644 index 0000000000000..795b4fedaaaab --- /dev/null +++ b/x-pack/plugins/canvas/server/lib/essql_strategy.ts @@ -0,0 +1,96 @@ +/* + * 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 { from } from 'rxjs'; +import { map, zipObject } from 'lodash'; + +import { ISearchStrategy, PluginStart } from 'src/plugins/data/server'; + +import { getKbnServerError } from '../../../../../src/plugins/kibana_utils/server'; +import { EssqlSearchStrategyRequest, EssqlSearchStrategyResponse } from '../../types'; + +import { buildBoolArray } from '../../common/lib/request/build_bool_array'; +import { sanitizeName } from '../../common/lib/request/sanitize_name'; +import { normalizeType } from '../../common/lib/request/normalize_type'; + +export const essqlSearchStrategyProvider = ( + data: PluginStart +): ISearchStrategy => { + return { + search: (request, options, { esClient }) => { + const { count, query, filter, timezone, params } = request; + + const searchUntilEnd = async () => { + try { + let response = await esClient.asCurrentUser.sql.query({ + format: 'json', + body: { + query, + // @ts-expect-error `params` missing from `QuerySqlRequest` type + params, + field_multi_value_leniency: true, + time_zone: timezone, + fetch_size: count, + client_id: 'canvas', + filter: { + bool: { + must: [{ match_all: {} }, ...buildBoolArray(filter)], + }, + }, + }, + }); + + let body = response.body; + + const columns = body.columns!.map(({ name, type }) => { + return { + id: sanitizeName(name), + name: sanitizeName(name), + meta: { type: normalizeType(type) }, + }; + }); + const columnNames = map(columns, 'name'); + let rows = body.rows.map((row) => zipObject(columnNames, row)); + + // If we still have rows to retrieve, continue requesting data + // using the cursor until we have everything + while (rows.length < count && body.cursor !== undefined) { + response = await esClient.asCurrentUser.sql.query({ + format: 'json', + body: { + cursor: body.cursor, + }, + }); + + body = response.body; + + rows = [...rows, ...body.rows.map((row) => zipObject(columnNames, row))]; + } + + // If we used a cursor, clean it up + if (body.cursor !== undefined) { + await esClient.asCurrentUser.sql.clearCursor({ + body: { + cursor: body.cursor, + }, + }); + } + + return { + columns, + rows, + rawResponse: response, + }; + } catch (e) { + throw getKbnServerError(e); + } + }; + + return from(searchUntilEnd()); + }, + }; +}; diff --git a/x-pack/plugins/canvas/server/lib/query_es_sql.ts b/x-pack/plugins/canvas/server/lib/query_es_sql.ts index a315657dadd2b..2c4416094914d 100644 --- a/x-pack/plugins/canvas/server/lib/query_es_sql.ts +++ b/x-pack/plugins/canvas/server/lib/query_es_sql.ts @@ -6,9 +6,9 @@ */ import { map, zipObject } from 'lodash'; -import { buildBoolArray } from './build_bool_array'; -import { sanitizeName } from './sanitize_name'; -import { normalizeType } from './normalize_type'; +import { buildBoolArray } from '../../common/lib/request/build_bool_array'; +import { sanitizeName } from '../../common/lib/request/sanitize_name'; +import { normalizeType } from '../../common/lib/request/normalize_type'; import { LegacyAPICaller } from '../../../../../src/core/server'; import { ExpressionValueFilter } from '../../types'; diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index 9360825830e56..9ccf3c251fecc 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -6,10 +6,15 @@ */ import { CoreSetup, PluginInitializerContext, Plugin, Logger, CoreStart } from 'src/core/server'; +import { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, +} from 'src/plugins/data/server'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { HomeServerPluginSetup } from 'src/plugins/home/server'; +import { ESSQL_SEARCH_STRATEGY } from '../common/lib/constants'; import { ReportingSetup } from '../../reporting/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { getCanvasFeature } from './feature'; @@ -19,6 +24,7 @@ import { loadSampleData } from './sample_data'; import { setupInterpreter } from './setup_interpreter'; import { customElementType, workpadType, workpadTemplateType } from './saved_objects'; import { initializeTemplates } from './templates'; +import { essqlSearchStrategyProvider } from './lib/essql_strategy'; import { getUISettings } from './ui_settings'; interface PluginsSetup { @@ -26,17 +32,22 @@ interface PluginsSetup { features: FeaturesPluginSetup; home: HomeServerPluginSetup; bfetch: BfetchServerSetup; + data: DataPluginSetup; reporting?: ReportingSetup; usageCollection?: UsageCollectionSetup; } +interface PluginsStart { + data: DataPluginStart; +} + export class CanvasPlugin implements Plugin { private readonly logger: Logger; constructor(public readonly initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); } - public setup(coreSetup: CoreSetup, plugins: PluginsSetup) { + public setup(coreSetup: CoreSetup, plugins: PluginsSetup) { coreSetup.uiSettings.register(getUISettings()); coreSetup.savedObjects.registerType(customElementType); coreSetup.savedObjects.registerType(workpadType); @@ -64,6 +75,11 @@ export class CanvasPlugin implements Plugin { registerCanvasUsageCollector(plugins.usageCollection, globalConfig.kibana.index); setupInterpreter(plugins.expressions); + + coreSetup.getStartServices().then(([_, depsStart]) => { + const strategy = essqlSearchStrategyProvider(depsStart.data); + plugins.data.search.registerSearchStrategy(ESSQL_SEARCH_STRATEGY, strategy); + }); } public start(coreStart: CoreStart) { diff --git a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts index 7ceace8b84dbd..20a4775847c91 100644 --- a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts +++ b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts @@ -9,7 +9,7 @@ import { mapValues, keys } from 'lodash'; import { schema } from '@kbn/config-schema'; import { API_ROUTE } from '../../../common/lib'; import { catchErrorHandler } from '../catch_error_handler'; -import { normalizeType } from '../../lib/normalize_type'; +import { normalizeType } from '../../../common/lib/request/normalize_type'; import { RouteInitializerDeps } from '..'; const ESFieldsRequestSchema = schema.object({ diff --git a/x-pack/plugins/canvas/types/index.ts b/x-pack/plugins/canvas/types/index.ts index 80314cab06258..09ae1510be6da 100644 --- a/x-pack/plugins/canvas/types/index.ts +++ b/x-pack/plugins/canvas/types/index.ts @@ -14,5 +14,6 @@ export * from './functions'; export * from './renderers'; export * from './shortcuts'; export * from './state'; +export * from './strategy'; export * from './style'; export * from './telemetry'; diff --git a/x-pack/plugins/canvas/types/strategy.ts b/x-pack/plugins/canvas/types/strategy.ts new file mode 100644 index 0000000000000..1c94059f0c9ca --- /dev/null +++ b/x-pack/plugins/canvas/types/strategy.ts @@ -0,0 +1,31 @@ +/* + * 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 { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; +import { QuerySqlResponse } from '@elastic/elasticsearch/api/types'; +import { IKibanaSearchRequest } from 'src/plugins/data/common'; +import { ExpressionValueFilter } from '.'; +export interface EssqlSearchStrategyRequest extends IKibanaSearchRequest { + count: number; + query: string; + params?: Array; + timezone?: string; + filter: ExpressionValueFilter[]; +} + +export interface EssqlSearchStrategyResponse { + columns: Array<{ + id: string; + name: string; + meta: { + type: string; + }; + }>; + rows: any[]; + + rawResponse: ApiResponse; +} diff --git a/x-pack/plugins/cases/common/api/cases/comment.ts b/x-pack/plugins/cases/common/api/cases/comment.ts index 41ad0e87f14d2..8434e419ef75a 100644 --- a/x-pack/plugins/cases/common/api/cases/comment.ts +++ b/x-pack/plugins/cases/common/api/cases/comment.ts @@ -9,6 +9,21 @@ import * as rt from 'io-ts'; import { UserRT } from '../user'; +const BucketsAggs = rt.array( + rt.type({ + key: rt.string, + }) +); + +export const GetCaseIdsByAlertIdAggsRt = rt.type({ + references: rt.type({ + doc_count: rt.number, + caseIds: rt.type({ + buckets: BucketsAggs, + }), + }), +}); + /** * this is used to differentiate between an alert attached to a top-level case and a group of alerts that should only * be attached to a sub case. The reason we need this is because an alert group comment will have references to both a case and @@ -123,3 +138,4 @@ export type CommentPatchRequest = rt.TypeOf; export type CommentPatchAttributes = rt.TypeOf; export type CommentRequestUserType = rt.TypeOf; export type CommentRequestAlertType = rt.TypeOf; +export type GetCaseIdsByAlertIdAggs = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/runtime_types.ts b/x-pack/plugins/cases/common/api/runtime_types.ts index 8001eb80cec73..f608b7effe969 100644 --- a/x-pack/plugins/cases/common/api/runtime_types.ts +++ b/x-pack/plugins/cases/common/api/runtime_types.ts @@ -13,6 +13,10 @@ import { isObject } from 'lodash/fp'; type ErrorFactory = (message: string) => Error; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + * Bug fix for the TODO is in the format_errors package + */ export const formatErrors = (errors: rt.Errors): string[] => { const err = errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index f9fae2466a59b..966305524c059 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -31,6 +31,8 @@ export const CASE_STATUS_URL = `${CASES_URL}/status`; export const CASE_TAGS_URL = `${CASES_URL}/tags`; export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions`; +export const CASE_ALERTS_URL = `${CASES_URL}/alerts/{alert_id}`; + /** * Action routes */ diff --git a/x-pack/plugins/cases/public/components/callout/translations.ts b/x-pack/plugins/cases/public/components/callout/translations.ts index 3f551c5cf0170..6d4b55603a06f 100644 --- a/x-pack/plugins/cases/public/components/callout/translations.ts +++ b/x-pack/plugins/cases/public/components/callout/translations.ts @@ -15,7 +15,7 @@ export const READ_ONLY_SAVED_OBJECT_MSG = i18n.translate( 'xpack.cases.readOnlySavedObjectDescription', { defaultMessage: - 'You only have permissions to view cases. If you need to open and update cases, contact your Kibana administrator.', + 'You only have privileges to view cases. If you need to open and update cases, contact your Kibana administrator.', } ); diff --git a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts new file mode 100644 index 0000000000000..1fc41874fe9d5 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts @@ -0,0 +1,48 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import Boom from '@hapi/boom'; + +import { RouteDeps } from '../../types'; +import { wrapError } from '../../utils'; +import { CASE_ALERTS_URL } from '../../../../../common/constants'; + +export function initGetCaseIdsByAlertIdApi({ caseService, router, logger }: RouteDeps) { + router.get( + { + path: CASE_ALERTS_URL, + validate: { + params: schema.object({ + alert_id: schema.string(), + }), + }, + }, + async (context, request, response) => { + try { + const alertId = request.params.alert_id; + if (alertId == null || alertId === '') { + throw Boom.badRequest('The `alertId` is not valid'); + } + const client = context.core.savedObjects.client; + const caseIds = await caseService.getCaseIdsByAlertId({ + client, + alertId, + }); + + return response.ok({ + body: caseIds, + }); + } catch (error) { + logger.error( + `Failed to retrieve case ids for this alert id: ${request.params.alert_id}: ${error}` + ); + return response.customError(wrapError(error)); + } + } + ); +} diff --git a/x-pack/plugins/cases/server/routes/api/index.ts b/x-pack/plugins/cases/server/routes/api/index.ts index c5b7aa85dc33e..a1635254dd09b 100644 --- a/x-pack/plugins/cases/server/routes/api/index.ts +++ b/x-pack/plugins/cases/server/routes/api/index.ts @@ -38,6 +38,7 @@ import { initPatchSubCasesApi } from './cases/sub_case/patch_sub_cases'; import { initFindSubCasesApi } from './cases/sub_case/find_sub_cases'; import { initDeleteSubCasesApi } from './cases/sub_case/delete_sub_cases'; import { ENABLE_CASE_CONNECTOR } from '../../../common/constants'; +import { initGetCaseIdsByAlertIdApi } from './cases/alerts/get_cases'; /** * Default page number when interacting with the saved objects API. @@ -86,4 +87,6 @@ export function initCaseApi(deps: RouteDeps) { initGetCasesStatusApi(deps); // Tags initGetTagsApi(deps); + // Alerts + initGetCaseIdsByAlertIdApi(deps); } diff --git a/x-pack/plugins/cases/server/services/index.ts b/x-pack/plugins/cases/server/services/index.ts index a27a8860e96b5..11b8cef6ab5a5 100644 --- a/x-pack/plugins/cases/server/services/index.ts +++ b/x-pack/plugins/cases/server/services/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { AggregationContainer } from '@elastic/elasticsearch/api/types'; import { KibanaRequest, Logger, @@ -17,6 +18,7 @@ import { SavedObjectsBulkResponse, SavedObjectsFindResult, } from 'kibana/server'; +import { nodeBuilder } from '../../../../../src/plugins/data/common'; import { AuthenticatedUser, SecurityPluginSetup } from '../../../security/server'; import { @@ -34,6 +36,7 @@ import { CaseResponse, caseTypeField, CasesFindRequest, + GetCaseIdsByAlertIdAggs, } from '../../common'; import { combineFilters, defaultSortField, groupTotalAlertsByID } from '../common'; import { defaultPage, defaultPerPage } from '../routes/api'; @@ -113,6 +116,10 @@ interface GetCommentArgs extends ClientArgs { commentId: string; } +interface GetCaseIdsByAlertIdArgs extends ClientArgs { + alertId: string; +} + interface PostCaseArgs extends ClientArgs { attributes: ESCaseAttributes; } @@ -220,6 +227,7 @@ export interface CaseServiceSetup { getSubCases(args: GetSubCasesArgs): Promise>; getCases(args: GetCasesArgs): Promise>; getComment(args: GetCommentArgs): Promise>; + getCaseIdsByAlertId(args: GetCaseIdsByAlertIdArgs): Promise; getTags(args: ClientArgs): Promise; getReporters(args: ClientArgs): Promise; getUser(args: GetUserArgs): Promise; @@ -899,6 +907,56 @@ export class CaseService implements CaseServiceSetup { } } + private buildCaseIdsAggs = (size: number = 100): Record => ({ + references: { + nested: { + path: `${CASE_COMMENT_SAVED_OBJECT}.references`, + }, + aggregations: { + caseIds: { + terms: { + field: `${CASE_COMMENT_SAVED_OBJECT}.references.id`, + size, + }, + }, + }, + }, + }); + + public async getCaseIdsByAlertId({ + client, + alertId, + }: GetCaseIdsByAlertIdArgs): Promise { + try { + this.log.debug(`Attempting to GET all cases for alert id ${alertId}`); + + let response = await client.find({ + type: CASE_COMMENT_SAVED_OBJECT, + fields: [], + page: 1, + perPage: 1, + sortField: defaultSortField, + aggs: this.buildCaseIdsAggs(), + filter: nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.alertId`, alertId), + }); + if (response.total > 100) { + response = await client.find({ + type: CASE_COMMENT_SAVED_OBJECT, + fields: [], + page: 1, + perPage: 1, + sortField: defaultSortField, + aggs: this.buildCaseIdsAggs(response.total), + filter: nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.alertId`, alertId), + }); + } + return response.aggregations?.references.caseIds.buckets.map((b) => b.key) ?? []; + } catch (error) { + this.log.error(`Error on GET all cases for alert id ${alertId}: ${error}`); + throw error; + } + } + /** * Default behavior is to retrieve all comments that adhere to a given filter (if one is included). * to override this pass in the either the page or perPage options. diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index 51eb0bbb1a7e4..d67a297508b14 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -31,6 +31,7 @@ export const createCaseServiceMock = (): CaseServiceMock => ({ getAllSubCaseComments: jest.fn(), getCase: jest.fn(), getCases: jest.fn(), + getCaseIdsByAlertId: jest.fn(), getComment: jest.fn(), getMostRecentSubCase: jest.fn(), getSubCase: jest.fn(), diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/auto_follow_pattern_list.test.js b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/auto_follow_pattern_list.test.js index 4a0eaef1e1e9a..1e7eb9562313d 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/auto_follow_pattern_list.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/auto_follow_pattern_list.test.js @@ -63,7 +63,6 @@ describe('', () => { }); describe('when there are multiple pages of auto-follow patterns', () => { - let find; let component; let table; let actions; @@ -83,7 +82,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadAutoFollowPatternsResponse({ patterns: autoFollowPatterns }); // Mount the component - ({ find, component, table, actions, form } = setup()); + ({ component, table, actions, form } = setup()); await nextTick(); // Make sure that the http request is fulfilled component.update(); @@ -98,9 +97,8 @@ describe('', () => { expect(tableCellsValues.length).toBe(10); }); - // Skipped until we can figure out how to get this test to work. - test.skip('search works', () => { - form.setInputValue(find('autoFollowPatternSearch'), 'unique'); + test('search works', () => { + form.setInputValue('autoFollowPatternSearch', 'unique'); const { tableCellsValues } = table.getMetaData('autoFollowPatternListTable'); expect(tableCellsValues.length).toBe(1); }); diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js index eb85e726d653a..c2b414ddbd845 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js @@ -69,7 +69,6 @@ describe('', () => { }); describe('when there are multiple pages of follower indices', () => { - let find; let component; let table; let actions; @@ -93,7 +92,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadFollowerIndicesResponse({ indices: followerIndices }); // Mount the component - ({ find, component, table, actions, form } = setup()); + ({ component, table, actions, form } = setup()); await nextTick(); // Make sure that the http request is fulfilled component.update(); @@ -108,9 +107,8 @@ describe('', () => { expect(tableCellsValues.length).toBe(10); }); - // Skipped until we can figure out how to get this test to work. - test.skip('search works', () => { - form.setInputValue(find('followerIndexSearch'), 'unique'); + test('search works', () => { + form.setInputValue('followerIndexSearch', 'unique'); const { tableCellsValues } = table.getMetaData('followerIndexListTable'); expect(tableCellsValues.length).toBe(1); }); diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/index.ts b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/index.ts index b1725ed8450d5..e88010e0439a5 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/index.ts +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/index.ts @@ -7,3 +7,4 @@ import './breadcrumbs.mock'; import './track_ui_metric.mock'; +import './search_box.mock'; diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/search_box.mock.tsx b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/search_box.mock.tsx new file mode 100644 index 0000000000000..f5a51ed044745 --- /dev/null +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/mocks/search_box.mock.tsx @@ -0,0 +1,22 @@ +/* + * 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 React from 'react'; +import { EuiSearchBoxProps } from '@elastic/eui/src/components/search_bar/search_box'; + +jest.mock('@elastic/eui/lib/components/search_bar/search_box', () => { + return { + EuiSearchBox: (props: EuiSearchBoxProps) => ( + ) => { + props.onSearch(event.target.value); + }} + /> + ), + }; +}); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js index 7ef6be2db7235..87002c936179a 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/auto_follow_pattern_table/auto_follow_pattern_table.js @@ -55,11 +55,12 @@ const getFilteredPatterns = (autoFollowPatterns, queryText) => { const normalizedSearchText = queryText.toLowerCase(); return autoFollowPatterns.filter((autoFollowPattern) => { + // default values to avoid undefined errors const { - name, - remoteCluster, - followIndexPatternPrefix, - followIndexPatternSuffix, + name = '', + remoteCluster = '', + followIndexPatternPrefix = '', + followIndexPatternSuffix = '', } = autoFollowPattern; const inName = name.toLowerCase().includes(normalizedSearchText); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js index 3ccb60a90b251..f63d55cda1e06 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js @@ -54,7 +54,8 @@ const getFilteredIndices = (followerIndices, queryText) => { const normalizedSearchText = queryText.toLowerCase(); return followerIndices.filter((followerIndex) => { - const { name, remoteCluster, leaderIndex } = followerIndex; + // default values to avoid undefined errors + const { name = '', remoteCluster = '', leaderIndex = '' } = followerIndex; if (name.toLowerCase().includes(normalizedSearchText)) { return true; diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx index 801db2e16ab3b..bd46724a77934 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { Subject } from 'rxjs'; import { FlyoutCreateDrilldownAction, OpenFlyoutAddDrilldownParams, @@ -22,6 +23,9 @@ const actionParams: OpenFlyoutAddDrilldownParams = { start: () => ({ core: { overlays, + application: { + currentAppId$: new Subject(), + }, } as any, plugins: { uiActionsEnhanced, diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx index 4c0db8f317e51..cdbf17f2eb637 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx @@ -7,11 +7,14 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { distinctUntilChanged, filter, map, skip, take, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { Action } from '../../../../../../../../src/plugins/ui_actions/public'; import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { CONTEXT_MENU_TRIGGER, EmbeddableContext, + ViewMode, } from '../../../../../../../../src/plugins/embeddable/public'; import { isEnhancedEmbeddable, @@ -82,7 +85,11 @@ export class FlyoutCreateDrilldownAction implements Action { } const templates = createDrilldownTemplatesFromSiblings(embeddable); - + const closed$ = new Subject(); + const close = () => { + closed$.next(true); + handle.close(); + }; const handle = core.overlays.openFlyout( toMountPoint( { triggers={[...ensureNestedTriggers(embeddable.supportedTriggers()), CONTEXT_MENU_TRIGGER]} placeContext={{ embeddable }} templates={templates} - onClose={() => handle.close()} + onClose={close} /> ), { @@ -100,5 +107,24 @@ export class FlyoutCreateDrilldownAction implements Action { 'data-test-subj': 'createDrilldownFlyout', } ); + + // Close flyout on application change. + core.application.currentAppId$.pipe(takeUntil(closed$), skip(1), take(1)).subscribe(() => { + close(); + }); + + // Close flyout on dashboard switch to "view" mode. + embeddable + .getInput$() + .pipe( + takeUntil(closed$), + map((input) => input.viewMode), + distinctUntilChanged(), + filter((mode) => mode !== ViewMode.EDIT), + take(1) + ) + .subscribe(() => { + close(); + }); } } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx index 30ba1b9842c96..e4c2d5b657630 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { Subject } from 'rxjs'; import { FlyoutEditDrilldownAction, FlyoutEditDrilldownParams } from './flyout_edit_drilldown'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { ViewMode } from '../../../../../../../../src/plugins/embeddable/public'; @@ -32,6 +33,9 @@ const actionParams: FlyoutEditDrilldownParams = { start: () => ({ core: { overlays, + application: { + currentAppId$: new Subject(), + }, } as any, plugins: { uiActionsEnhanced: uiActions, diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.tsx index 44eb63bbc504b..09ea886f9a2d0 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.tsx @@ -6,6 +6,8 @@ */ import React from 'react'; +import { distinctUntilChanged, filter, map, skip, take, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { Action } from '../../../../../../../../src/plugins/ui_actions/public'; import { reactToUiComponent, @@ -67,7 +69,11 @@ export class FlyoutEditDrilldownAction implements Action { } const templates = createDrilldownTemplatesFromSiblings(embeddable); - + const closed$ = new Subject(); + const close = () => { + closed$.next(true); + handle.close(); + }; const handle = core.overlays.openFlyout( toMountPoint( { triggers={[...ensureNestedTriggers(embeddable.supportedTriggers()), CONTEXT_MENU_TRIGGER]} placeContext={{ embeddable }} templates={templates} - onClose={() => handle.close()} + onClose={close} /> ), { @@ -84,5 +90,24 @@ export class FlyoutEditDrilldownAction implements Action { 'data-test-subj': 'editDrilldownFlyout', } ); + + // Close flyout on application change. + core.application.currentAppId$.pipe(takeUntil(closed$), skip(1), take(1)).subscribe(() => { + close(); + }); + + // Close flyout on dashboard switch to "view" mode. + embeddable + .getInput$() + .pipe( + takeUntil(closed$), + map((input) => input.viewMode), + distinctUntilChanged(), + filter((mode) => mode !== ViewMode.EDIT), + take(1) + ) + .subscribe(() => { + close(); + }); } } diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx index dcc39e9fb385a..13dfe12ccb5e4 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx @@ -60,7 +60,7 @@ describe('Background Search Session Management Main', () => { ELASTIC_WEBSITE_URL: `boo/`, DOC_LINK_VERSION: `#foo`, links: { - elasticsearch: { asyncSearch: `mock-url` } as any, + search: { sessions: `mock-url` } as any, } as any, }; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx index 123cc3dcb97cc..4d97092209c67 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx @@ -129,10 +129,10 @@ describe('Background Search Session Management Table', () => { expect(table.find('tbody td').map((node) => node.text())).toMatchInlineSnapshot(` Array [ "App", - "Namevery background search", - "StatusIn progress", + "Namevery background search ", + "StatusExpired", "Created2 Dec, 2020, 00:19:32", - "Expiration7 Dec, 2020, 00:19:32", + "Expiration--", "", "", ] diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts index f1f38fecfe012..3857b08ad0a3a 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts @@ -87,6 +87,35 @@ describe('Search Sessions Management API', () => { `); }); + test('completed session with expired time is showed as expired', async () => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return { + saved_objects: [ + { + id: 'hello-pizza-123', + attributes: { + name: 'Veggie', + appId: 'pizza', + status: 'complete', + expires: moment().subtract(3, 'days'), + initialState: {}, + restoreState: {}, + }, + }, + ], + } as SavedObjectsFindResponse; + }); + + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + + const res = await api.fetchTableData(); + expect(res[0].status).toBe(SearchSessionStatus.EXPIRED); + }); + test('handle error from sessionsClient response', async () => { sessionsClient.find = jest.fn().mockRejectedValue(new Error('implementation is so bad')); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts index 943fc7d26d36d..3710dfa16e76b 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts @@ -17,12 +17,16 @@ import { } from '../../../../../../../src/plugins/data/public'; import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common'; import { ACTION } from '../components/actions'; -import { PersistedSearchSessionSavedObjectAttributes, UISession } from '../types'; +import { + PersistedSearchSessionSavedObjectAttributes, + UISearchSessionState, + UISession, +} from '../types'; import { SessionsConfigSchema } from '..'; type UrlGeneratorsStart = SharePluginStart['urlGenerators']; -function getActions(status: SearchSessionStatus) { +function getActions(status: UISearchSessionState) { const actions: ACTION[] = []; actions.push(ACTION.INSPECT); actions.push(ACTION.RENAME); @@ -30,9 +34,33 @@ function getActions(status: SearchSessionStatus) { actions.push(ACTION.EXTEND); actions.push(ACTION.DELETE); } + + if (status === SearchSessionStatus.EXPIRED) { + actions.push(ACTION.DELETE); + } + return actions; } +/** + * Status we display on mgtm UI might be different from the one inside the saved object + * @param status + */ +function getUIStatus(session: PersistedSearchSessionSavedObjectAttributes): UISearchSessionState { + const isSessionExpired = () => { + const curTime = moment(); + return curTime.diff(moment(session.expires), 'ms') > 0; + }; + + switch (session.status) { + case SearchSessionStatus.COMPLETE: + case SearchSessionStatus.IN_PROGRESS: + return isSessionExpired() ? SearchSessionStatus.EXPIRED : session.status; + } + + return session.status; +} + async function getUrlFromState( urls: UrlGeneratorsStart, urlGeneratorId: string, @@ -59,12 +87,12 @@ const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema) appId, created, expires, - status, urlGeneratorId, initialState, restoreState, } = savedObject.attributes; + const status = getUIStatus(savedObject.attributes); const actions = getActions(status); // TODO: initialState should be saved without the searchSessionID diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts index 38db89e88a6e1..545c7f7ec26b2 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts @@ -12,8 +12,7 @@ export class AsyncSearchIntroDocumentation { constructor(docs: DocLinksStart) { const { links } = docs; - // TODO: There should be Kibana documentation link about Search Sessions in Kibana - this.docUrl = links.elasticsearch.asyncSearch; + this.docUrl = links.search.sessions; } public getElasticsearchDocLink() { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts index 369eac450e07b..d0d5ee9fb17dd 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts @@ -25,13 +25,15 @@ export type PersistedSearchSessionSavedObjectAttributes = SearchSessionSavedObje > >; +export type UISearchSessionState = SearchSessionStatus; + export interface UISession { id: string; name: string; appId: string; created: string; expires: string | null; - status: SearchSessionStatus; + status: UISearchSessionState; actions?: ACTION[]; reloadUrl: string; restoreUrl: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.test.tsx index cf30fac3c5f49..9d1bbafc82662 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.test.tsx @@ -15,6 +15,7 @@ import { EngineCreation } from './'; describe('EngineCreation', () => { const DEFAULT_VALUES = { + isLoading: false, name: '', rawName: '', language: 'Universal', @@ -87,6 +88,15 @@ describe('EngineCreation', () => { false ); }); + + it('passes isLoading state', () => { + setMockValues({ ...DEFAULT_VALUES, isLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="NewEngineSubmitButton"]').prop('isLoading')).toEqual( + true + ); + }); }); describe('EngineCreationNameFormRow', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.tsx index 4e1d7bc3e8e48..250c941009ecb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation.tsx @@ -40,7 +40,7 @@ import { import { EngineCreationLogic } from './engine_creation_logic'; export const EngineCreation: React.FC = () => { - const { name, rawName, language } = useValues(EngineCreationLogic); + const { name, rawName, language, isLoading } = useValues(EngineCreationLogic); const { setLanguage, setRawName, submitEngine } = useActions(EngineCreationLogic); return ( @@ -104,10 +104,11 @@ export const EngineCreation: React.FC = () => { {ENGINE_CREATION_FORM_SUBMIT_BUTTON_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.test.ts index 272e4fb3a25c0..6d5c4480e45f6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.test.ts @@ -23,6 +23,7 @@ describe('EngineCreationLogic', () => { const { setQueuedSuccessMessage, flashAPIErrors } = mockFlashMessageHelpers; const DEFAULT_VALUES = { + isLoading: false, name: '', rawName: '', language: 'Universal', @@ -63,6 +64,28 @@ describe('EngineCreationLogic', () => { expect(EngineCreationLogic.values.name).toEqual('name-with-special-characters'); }); }); + + describe('submitEngine', () => { + it('sets isLoading to true', () => { + mount({ isLoading: false }); + EngineCreationLogic.actions.submitEngine(); + expect(EngineCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + isLoading: true, + }); + }); + }); + + describe('onSubmitError', () => { + it('resets isLoading to false', () => { + mount({ isLoading: true }); + EngineCreationLogic.actions.onSubmitError(); + expect(EngineCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + isLoading: false, + }); + }); + }); }); describe('listeners', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.ts index 6cea32f826e7a..844cd8fb4088a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_creation/engine_creation_logic.ts @@ -22,9 +22,11 @@ interface EngineCreationActions { setLanguage(language: string): { language: string }; setRawName(rawName: string): { rawName: string }; submitEngine(): void; + onSubmitError(): void; } interface EngineCreationValues { + isLoading: boolean; language: string; name: string; rawName: string; @@ -37,8 +39,16 @@ export const EngineCreationLogic = kea ({ language }), setRawName: (rawName) => ({ rawName }), submitEngine: true, + onSubmitError: true, }, reducers: { + isLoading: [ + false, + { + submitEngine: () => true, + onSubmitError: () => false, + }, + ], language: [ DEFAULT_LANGUAGE, { @@ -67,6 +77,7 @@ export const EngineCreationLogic = kea { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx index 6b4a081d2c515..e6a7c03d2aab4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx @@ -55,7 +55,6 @@ export const EmptyState: React.FC = () => { actions={ <> { expect(submitButton.prop('disabled')).toEqual(true); }); + + it('passes isLoading state', () => { + setMockValues({ ...DEFAULT_VALUES, isLoading: true }); + const wrapper = shallow(); + const submitButton = wrapper.find('[data-test-subj="NewMetaEngineSubmitButton"]'); + + expect(submitButton.prop('isLoading')).toEqual(true); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation.tsx index 85c24f1e42368..02a1768a7528e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation.tsx @@ -65,7 +65,7 @@ export const MetaEngineCreation: React.FC = () => { submitEngine, } = useActions(MetaEngineCreationLogic); - const { rawName, name, indexedEngineNames, selectedIndexedEngineNames } = useValues( + const { rawName, name, indexedEngineNames, selectedIndexedEngineNames, isLoading } = useValues( MetaEngineCreationLogic ); @@ -157,10 +157,11 @@ export const MetaEngineCreation: React.FC = () => { selectedIndexedEngineNames.length === 0 || selectedIndexedEngineNames.length > maxEnginesPerMetaEngine } + isLoading={isLoading} type="submit" data-test-subj="NewMetaEngineSubmitButton" - fill color="secondary" + fill > {META_ENGINE_CREATION_FORM_SUBMIT_BUTTON_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.test.ts index 6ffe7034584a1..d8968316d1b72 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.test.ts @@ -23,6 +23,7 @@ describe('MetaEngineCreationLogic', () => { const { setQueuedSuccessMessage, flashAPIErrors } = mockFlashMessageHelpers; const DEFAULT_VALUES = { + isLoading: false, indexedEngineNames: [], name: '', rawName: '', @@ -76,6 +77,28 @@ describe('MetaEngineCreationLogic', () => { ]); }); }); + + describe('submitEngine', () => { + it('sets isLoading to true', () => { + mount({ isLoading: false }); + MetaEngineCreationLogic.actions.submitEngine(); + expect(MetaEngineCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + isLoading: true, + }); + }); + }); + + describe('onSubmitError', () => { + it('resets isLoading to false', () => { + mount({ isLoading: true }); + MetaEngineCreationLogic.actions.onSubmitError(); + expect(MetaEngineCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + isLoading: false, + }); + }); + }); }); describe('listeners', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.ts index d94eb82a49c0e..472a0dee12b7f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/meta_engine_creation/meta_engine_creation_logic.ts @@ -25,6 +25,7 @@ interface MetaEngineCreationValues { name: string; rawName: string; selectedIndexedEngineNames: string[]; + isLoading: boolean; } interface MetaEngineCreationActions { @@ -38,6 +39,7 @@ interface MetaEngineCreationActions { selectedIndexedEngineNames: MetaEngineCreationValues['selectedIndexedEngineNames'] ): { selectedIndexedEngineNames: MetaEngineCreationValues['selectedIndexedEngineNames'] }; submitEngine(): void; + onSubmitError(): void; } export const MetaEngineCreationLogic = kea< @@ -50,9 +52,17 @@ export const MetaEngineCreationLogic = kea< setIndexedEngineNames: (indexedEngineNames) => ({ indexedEngineNames }), setRawName: (rawName) => ({ rawName }), setSelectedIndexedEngineNames: (selectedIndexedEngineNames) => ({ selectedIndexedEngineNames }), - submitEngine: () => null, + submitEngine: true, + onSubmitError: true, }, reducers: { + isLoading: [ + false, + { + submitEngine: () => true, + onSubmitError: () => false, + }, + ], indexedEngineNames: [ [], { @@ -121,6 +131,7 @@ export const MetaEngineCreationLogic = kea< actions.onEngineCreationSuccess(); } catch (e) { flashAPIErrors(e); + actions.onSubmitError(); } }, }), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/constants.ts new file mode 100644 index 0000000000000..903d1768f3cc1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/constants.ts @@ -0,0 +1,20 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const LICENSE_CALLOUT_BODY = i18n.translate('xpack.enterpriseSearch.licenseCalloutBody', { + defaultMessage: + 'Enterprise authentication via SAML, document-level permission and authorization support, custom search experiences and more are available with a valid Platinum license.', +}); + +export const LICENSE_CALLOUT_BUTTON = i18n.translate( + 'xpack.enterpriseSearch.licenseCalloutButton', + { + defaultMessage: 'Manage your license', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/index.ts new file mode 100644 index 0000000000000..80a9355ee118e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { LicenseCallout } from './license_callout'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.test.tsx new file mode 100644 index 0000000000000..26892f7cec7ae --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 { setMockValues } from '../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiPanel, EuiText } from '@elastic/eui'; + +import { EuiButtonTo } from '../../../shared/react_router_helpers'; + +import { LicenseCallout } from './'; + +describe('LicenseCallout', () => { + it('renders when non-platinum or on trial', () => { + setMockValues({ + hasPlatinumLicense: false, + isTrial: true, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiPanel)).toHaveLength(1); + expect(wrapper.find(EuiText)).toHaveLength(2); + expect(wrapper.find(EuiButtonTo).prop('to')).toEqual( + '/app/management/stack/license_management' + ); + }); + + it('does not render for platinum', () => { + setMockValues({ + hasPlatinumLicense: true, + isTrial: false, + }); + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.tsx new file mode 100644 index 0000000000000..4a4de17450f1b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/license_callout/license_callout.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; + +import { useValues } from 'kea'; + +import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; + +import { LicensingLogic } from '../../../shared/licensing'; +import { EuiButtonTo } from '../../../shared/react_router_helpers'; + +import { PRODUCT_SELECTOR_CALLOUT_HEADING } from '../../constants'; + +import { LICENSE_CALLOUT_BODY, LICENSE_CALLOUT_BUTTON } from './constants'; + +export const LicenseCallout: React.FC = () => { + const { hasPlatinumLicense, isTrial } = useValues(LicensingLogic); + + if (hasPlatinumLicense && !isTrial) return null; + + return ( + + + + +

{PRODUCT_SELECTOR_CALLOUT_HEADING}

+
+ {LICENSE_CALLOUT_BODY} +
+ + + + {LICENSE_CALLOUT_BUTTON} + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx index 9ee34634e3797..9c0f7c2bac94b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx @@ -13,8 +13,10 @@ import { shallow } from 'enzyme'; import { EuiPage } from '@elastic/eui'; +import { LicenseCallout } from '../license_callout'; import { ProductCard } from '../product_card'; import { SetupGuideCta } from '../setup_guide'; +import { TrialCallout } from '../trial_callout'; import { ProductSelector } from './'; @@ -26,6 +28,15 @@ describe('ProductSelector', () => { expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); expect(wrapper.find(ProductCard)).toHaveLength(2); expect(wrapper.find(SetupGuideCta)).toHaveLength(1); + expect(wrapper.find(LicenseCallout)).toHaveLength(0); + }); + + it('renders the license and trial callouts', () => { + setMockValues({ config: { host: 'localhost' } }); + const wrapper = shallow(); + + expect(wrapper.find(TrialCallout)).toHaveLength(1); + expect(wrapper.find(LicenseCallout)).toHaveLength(1); }); describe('access checks when host is set', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx index f2476a5770c25..a7eb2424e797a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx @@ -29,8 +29,10 @@ import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/ import AppSearchImage from '../../assets/app_search.png'; import WorkplaceSearchImage from '../../assets/workplace_search.png'; +import { LicenseCallout } from '../license_callout'; import { ProductCard } from '../product_card'; import { SetupGuideCta } from '../setup_guide'; +import { TrialCallout } from '../trial_callout'; interface ProductSelectorProps { access: { @@ -53,6 +55,7 @@ export const ProductSelector: React.FC = ({ access }) => { + @@ -88,8 +91,8 @@ export const ProductSelector: React.FC = ({ access }) => { )} - - {!config.host && } + + {config.host ? : } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx index 17260cc15793a..277af62b882c6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EuiPanelTo } from '../../../shared/react_router_helpers'; +import { PRODUCT_SELECTOR_CALLOUT_HEADING } from '../../constants'; import CtaImage from './assets/getting_started.png'; import './setup_guide_cta.scss'; @@ -20,11 +21,7 @@ export const SetupGuideCta: React.FC = () => ( -

- {i18n.translate('xpack.enterpriseSearch.overview.setupCta.title', { - defaultMessage: 'Enterprise-grade functionality for teams big and small', - })} -

+

{PRODUCT_SELECTOR_CALLOUT_HEADING}

{i18n.translate('xpack.enterpriseSearch.overview.setupCta.description', { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/index.ts new file mode 100644 index 0000000000000..fdcfa70ffa948 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { TrialCallout } from './trial_callout'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.test.tsx new file mode 100644 index 0000000000000..2ed2f9e43e9c4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.test.tsx @@ -0,0 +1,32 @@ +/* + * 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 { setMockValues } from '../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiCallOut } from '@elastic/eui'; + +import { TrialCallout } from './'; + +describe('TrialCallout', () => { + it('renders when non-platinum or on trial', () => { + setMockValues({ isTrial: true }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + }); + + it('does not render when not on trial', () => { + setMockValues({ isTrial: false }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(0); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.tsx new file mode 100644 index 0000000000000..1140d42b9189f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/trial_callout/trial_callout.tsx @@ -0,0 +1,49 @@ +/* + * 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 React from 'react'; + +import { useValues } from 'kea'; +import moment from 'moment'; + +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { LicensingLogic } from '../../../shared/licensing'; + +export const TrialCallout: React.FC = () => { + const { license, isTrial } = useValues(LicensingLogic); + + if (!isTrial) return null; + + const title = ( + <> + {' '} + + + + + + + ); + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/constants.ts new file mode 100644 index 0000000000000..c5997222fef6e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/constants.ts @@ -0,0 +1,15 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const PRODUCT_SELECTOR_CALLOUT_HEADING = i18n.translate( + 'xpack.enterpriseSearch.productSelectorCalloutTitle', + { + defaultMessage: 'Enterprise-grade functionality for teams big and small', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts index b7569111c1ade..4ea74e1c0d4f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts @@ -158,5 +158,34 @@ describe('LicensingLogic', () => { expect(LicensingLogic.values.hasGoldLicense).toEqual(false); }); }); + + describe('isTrial', () => { + it('is true for active trial license', () => { + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.isTrial).toEqual(true); + }); + + it('is false if the trial license is expired', () => { + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + }); + + it('is false for all non-trial licenses', () => { + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.isTrial).toEqual(false); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts index 627401cdc6147..7d0222f476214 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts @@ -15,6 +15,7 @@ interface LicensingValues { licenseSubscription: Subscription | null; hasPlatinumLicense: boolean; hasGoldLicense: boolean; + isTrial: boolean; } interface LicensingActions { setLicense(license: ILicense): ILicense; @@ -56,6 +57,10 @@ export const LicensingLogic = kea [selectors.license], + (license) => license?.isActive && license?.type === 'trial', + ], }, events: ({ props, actions, values }) => ({ afterMount: () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/constants.tsx new file mode 100644 index 0000000000000..3cc43532eda73 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/constants.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const FORM_ID = 'schemaAddFieldForm'; + +export const ADD_FIELD_MODAL_TITLE = i18n.translate( + 'xpack.enterpriseSearch.schema.addFieldModal.title', + { defaultMessage: 'Add a new field' } +); +export const ADD_FIELD_MODAL_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.schema.addFieldModal.description', + { defaultMessage: 'Once added, a field cannot be removed from your schema.' } +); +export const ADD_FIELD_BUTTON = i18n.translate( + 'xpack.enterpriseSearch.schema.addFieldModal.addFieldButtonLabel', + { defaultMessage: 'Add field' } +); + +export const FIELD_NAME_PLACEHOLDER = i18n.translate( + 'xpack.enterpriseSearch.schema.addFieldModal.fieldNamePlaceholder', + { defaultMessage: 'Enter a field name' } +); +export const FIELD_NAME_CORRECT_NOTE = i18n.translate( + 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.correct', + { defaultMessage: 'Field names can only contain lowercase letters, numbers, and underscores' } +); +export const FIELD_NAME_CORRECTED_NOTE = (correctedName: string) => ( + {correctedName}, + }} + /> +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.test.tsx similarity index 74% rename from x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.test.tsx index 12bc61d723919..186560c694d24 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.test.tsx @@ -9,12 +9,13 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { EuiFieldText, EuiModal } from '@elastic/eui'; +import { EuiForm, EuiFieldText, EuiModal } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; -import { FIELD_NAME_CORRECTED_PREFIX } from './constants'; -import { SchemaType } from './types'; +import { SchemaFieldTypeSelect } from '../index'; +import { SchemaType } from '../types'; -import { SchemaFieldTypeSelect, SchemaAddFieldModal } from './'; +import { SchemaAddFieldModal } from './'; describe('SchemaAddFieldModal', () => { const addNewField = jest.fn(); @@ -53,8 +54,15 @@ describe('SchemaAddFieldModal', () => { expect(setState).toHaveBeenCalledWith(false); }); + it('passes disabled state', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="SchemaAddFieldNameField"]').prop('disabled')).toBe(true); + expect(wrapper.find('[data-test-subj="SchemaSelect"]').prop('disabled')).toBe(true); + expect(wrapper.find('[data-test-subj="SchemaAddFieldButton"]').prop('disabled')).toBe(true); + }); + it('handles input change - with non-formatted name', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(setStateMock); const wrapper = shallow(); const input = wrapper.find(EuiFieldText); input.simulate('change', { currentTarget: { value: 'foobar' } }); @@ -65,16 +73,14 @@ describe('SchemaAddFieldModal', () => { }); it('handles input change - with formatted name', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(setStateMock); const wrapper = shallow(); const input = wrapper.find(EuiFieldText); input.simulate('change', { currentTarget: { value: 'foo-bar' } }); - expect(wrapper.find('[data-test-subj="SchemaAddFieldNameRow"]').prop('helpText')).toEqual( - - {FIELD_NAME_CORRECTED_PREFIX} foo_bar - - ); + const helpText = wrapper + .find('[data-test-subj="SchemaAddFieldNameRow"]') + .prop('helpText') as React.ReactElement; + expect(helpText.type).toEqual(FormattedMessage); }); it('handles field type select change', () => { @@ -87,10 +93,9 @@ describe('SchemaAddFieldModal', () => { }); it('handles form submission', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(setStateMock); const wrapper = shallow(); const preventDefault = jest.fn(); - wrapper.find('form').simulate('submit', { preventDefault }); + wrapper.find(EuiForm).simulate('submit', { preventDefault }); expect(addNewField).toHaveBeenCalled(); expect(setState).toHaveBeenCalled(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.tsx new file mode 100644 index 0000000000000..902417d02665e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/index.tsx @@ -0,0 +1,141 @@ +/* + * 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 React, { ChangeEvent, FormEvent, useEffect, useState } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, +} from '@elastic/eui'; + +import { CANCEL_BUTTON_LABEL } from '../../constants'; +import { FIELD_NAME, FIELD_TYPE } from '../constants'; +import { SchemaFieldTypeSelect } from '../index'; +import { SchemaType } from '../types'; + +import { + ADD_FIELD_MODAL_TITLE, + ADD_FIELD_MODAL_DESCRIPTION, + ADD_FIELD_BUTTON, + FORM_ID, + FIELD_NAME_PLACEHOLDER, + FIELD_NAME_CORRECT_NOTE, + FIELD_NAME_CORRECTED_NOTE, +} from './constants'; +import { formatFieldName } from './utils'; + +interface Props { + addNewField(fieldName: string, newFieldType: string): void; + closeAddFieldModal(): void; + disableForm?: boolean; + addFieldFormErrors?: string[] | null; +} + +export const SchemaAddFieldModal: React.FC = ({ + addNewField, + addFieldFormErrors, + closeAddFieldModal, + disableForm, +}) => { + const [loading, setLoading] = useState(false); + const [newFieldType, updateNewFieldType] = useState(SchemaType.Text); + const [formattedFieldName, setFormattedFieldName] = useState(''); + const [rawFieldName, setRawFieldName] = useState(''); + + useEffect(() => { + if (addFieldFormErrors) setLoading(false); + }, [addFieldFormErrors]); + + const handleChange = ({ currentTarget: { value } }: ChangeEvent) => { + setRawFieldName(value); + setFormattedFieldName(formatFieldName(value)); + }; + + const submitForm = (e: FormEvent) => { + e.preventDefault(); + addNewField(formattedFieldName, newFieldType); + setLoading(true); + }; + + const fieldNameNote = + rawFieldName !== formattedFieldName + ? FIELD_NAME_CORRECTED_NOTE(formattedFieldName) + : FIELD_NAME_CORRECT_NOTE; + + return ( + + + {ADD_FIELD_MODAL_TITLE} + + +

{ADD_FIELD_MODAL_DESCRIPTION}

+ + + + + + + + + + + updateNewFieldType(type)} + disabled={disableForm} + data-test-subj="SchemaSelect" + /> + + + + +
+ + {CANCEL_BUTTON_LABEL} + + {ADD_FIELD_BUTTON} + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.test.ts new file mode 100644 index 0000000000000..917478cd0aa03 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.test.ts @@ -0,0 +1,26 @@ +/* + * 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 { formatFieldName } from './utils'; + +describe('formatFieldName', () => { + it('removes leading and trailing spaces', () => { + expect(formatFieldName(' helloworld ')).toEqual('helloworld'); + }); + + it('converts all special characters to underscores', () => { + expect(formatFieldName('hello!#@$123---world')).toEqual('hello_123_world'); + }); + + it('strips leading and trailing special characters/underscores', () => { + expect(formatFieldName('!!helloworld__')).toEqual('helloworld'); + }); + + it('lowercases any caps', () => { + expect(formatFieldName('HELLO_WORLD')).toEqual('hello_world'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.ts new file mode 100644 index 0000000000000..6242a337871b1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/add_field_modal/utils.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const formatFieldName = (rawName: string) => + rawName + .trim() + .replace(/[^a-zA-Z0-9]+/g, '_') + .replace(/^(_+)|(_+)$/g, '') + .toLowerCase(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/constants.ts index 3791626f54398..134e5e7c3dde3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/constants.ts @@ -7,65 +7,10 @@ import { i18n } from '@kbn/i18n'; -export const FIELD_NAME_CORRECT_NOTE = i18n.translate( - 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.correct', - { - defaultMessage: 'Field names can only contain lowercase letters, numbers, and underscores', - } -); +export const FIELD_NAME = i18n.translate('xpack.enterpriseSearch.schema.fieldNameLabel', { + defaultMessage: 'Field name', +}); -export const FIELD_NAME_CORRECTED_PREFIX = i18n.translate( - 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.corrected', - { - defaultMessage: 'The field will be named', - } -); - -export const FIELD_NAME_MODAL_TITLE = i18n.translate( - 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.title', - { - defaultMessage: 'Add a New Field', - } -); - -export const FIELD_NAME_MODAL_DESCRIPTION = i18n.translate( - 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.description', - { - defaultMessage: 'Once added, a field cannot be removed from your schema.', - } -); - -export const FIELD_NAME_MODAL_ADD_FIELD = i18n.translate( - 'xpack.enterpriseSearch.schema.addFieldModal.fieldNameNote.addField', - { - defaultMessage: 'Add field', - } -); - -export const ERROR_TABLE_ID_HEADER = i18n.translate( - 'xpack.enterpriseSearch.schema.errorsTable.heading.id', - { - defaultMessage: 'id', - } -); - -export const ERROR_TABLE_ERROR_HEADER = i18n.translate( - 'xpack.enterpriseSearch.schema.errorsTable.heading.error', - { - defaultMessage: 'Error', - } -); - -export const ERROR_TABLE_REVIEW_CONTROL = i18n.translate( - 'xpack.enterpriseSearch.schema.errorsTable.control.review', - { - defaultMessage: 'Review', - } -); - -export const ERROR_TABLE_VIEW_LINK = i18n.translate( - 'xpack.enterpriseSearch.schema.errorsTable.link.view', - { - defaultMessage: 'View', - } -); +export const FIELD_TYPE = i18n.translate('xpack.enterpriseSearch.schema.fieldTypeLabel', { + defaultMessage: 'Field type', +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/constants.ts new file mode 100644 index 0000000000000..844f0c8041915 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/constants.ts @@ -0,0 +1,28 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ERROR_TABLE_ID_HEADER = i18n.translate( + 'xpack.enterpriseSearch.schema.errorsTable.heading.id', + { defaultMessage: 'ID' } +); + +export const ERROR_TABLE_ERROR_HEADER = i18n.translate( + 'xpack.enterpriseSearch.schema.errorsTable.heading.error', + { defaultMessage: 'Error' } +); + +export const ERROR_TABLE_REVIEW_CONTROL = i18n.translate( + 'xpack.enterpriseSearch.schema.errorsTable.control.review', + { defaultMessage: 'Review' } +); + +export const ERROR_TABLE_VIEW_LINK = i18n.translate( + 'xpack.enterpriseSearch.schema.errorsTable.link.view', + { defaultMessage: 'View' } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx similarity index 55% rename from x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx index a15d39c447126..5413da9d56161 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.test.tsx @@ -9,11 +9,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiAccordion, EuiTableRow } from '@elastic/eui'; +import { EuiAccordion, EuiTableRow, EuiTableHeaderCell } from '@elastic/eui'; -import { EuiButtonEmptyTo } from '../react_router_helpers'; +import { EuiLinkTo } from '../../react_router_helpers'; -import { SchemaErrorsAccordion } from './schema_errors_accordion'; +import { SchemaType } from '../types'; + +import { SchemaErrorsAccordion } from './'; describe('SchemaErrorsAccordion', () => { const props = { @@ -30,8 +32,7 @@ describe('SchemaErrorsAccordion', () => { ], }, schema: { - id: 'string', - name: 'boolean', + id: SchemaType.Text, }, }; @@ -40,12 +41,16 @@ describe('SchemaErrorsAccordion', () => { expect(wrapper.find(EuiAccordion)).toHaveLength(1); expect(wrapper.find(EuiTableRow)).toHaveLength(2); - expect(wrapper.find(EuiButtonEmptyTo)).toHaveLength(0); }); - it('renders document buttons', () => { - const wrapper = shallow(); + it('conditionally renders a view column', () => { + const generateViewPath = jest.fn((id: string) => `/documents/${id}`); + const wrapper = shallow( + + ); - expect(wrapper.find(EuiButtonEmptyTo)).toHaveLength(2); + expect(wrapper.find(EuiTableHeaderCell)).toHaveLength(3); + expect(wrapper.find(EuiLinkTo)).toHaveLength(2); + expect(wrapper.find(EuiLinkTo).first().prop('to')).toEqual('/documents/foo'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx new file mode 100644 index 0000000000000..35675d246a15e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/index.tsx @@ -0,0 +1,113 @@ +/* + * 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 React from 'react'; + +import { + EuiAccordion, + EuiFlexGroup, + EuiFlexItem, + EuiTable, + EuiTableBody, + EuiTableHeader, + EuiTableHeaderCell, + EuiTableRow, + EuiTableRowCell, +} from '@elastic/eui'; + +import { EuiLinkTo } from '../../react_router_helpers'; +import { TruncatedContent } from '../../truncate'; + +import { Schema, FieldCoercionErrors } from '../types'; + +import { + ERROR_TABLE_ID_HEADER, + ERROR_TABLE_ERROR_HEADER, + ERROR_TABLE_REVIEW_CONTROL, + ERROR_TABLE_VIEW_LINK, +} from './constants'; + +import './schema_errors_accordion.scss'; + +interface Props { + fieldCoercionErrors: FieldCoercionErrors; + schema: Schema; + generateViewPath?(externalId: string): string; +} + +export const SchemaErrorsAccordion: React.FC = ({ + fieldCoercionErrors, + schema, + generateViewPath, +}) => ( + <> + {Object.keys(fieldCoercionErrors).map((fieldName) => { + const fieldType = schema[fieldName]; + const errors = fieldCoercionErrors[fieldName]; + + const accordionHeader = ( + + + + + + + + {fieldType} + + + {/* Mock an EuiButton without actually creating one - we shouldn't nest a button within a button */} +
+ {ERROR_TABLE_REVIEW_CONTROL} +
+
+
+ ); + + return ( + + + + {ERROR_TABLE_ID_HEADER} + {ERROR_TABLE_ERROR_HEADER} + {generateViewPath && } + + + {errors.map((error) => { + const { external_id: id, error: errorMessage } = error; + return ( + + + + + {errorMessage} + {generateViewPath && ( + + {ERROR_TABLE_VIEW_LINK} + + )} + + ); + })} + + + + ); + })} + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/schema_errors_accordion.scss b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/schema_errors_accordion.scss new file mode 100644 index 0000000000000..3b69019e9d460 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/errors_accordion/schema_errors_accordion.scss @@ -0,0 +1,26 @@ +/* + * 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. + */ + +.schemaErrorsAccordion { + // Fixes the accordion button content not stretching to full-width + .euiAccordion__button > :last-child { + flex-grow: 1; + } + + // Tweak spacing + .euiAccordion__childWrapper { + position: relative; + top: -($euiSizeS); + padding-left: $euiSizeL; + padding-right: $euiSizeL; + } + + // Remove extra border-bottom on last table row + .euiTableRow:last-child .euiTableRowCell { + border-bottom: none; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/schema/index.ts index b47d7ccabb57e..81fa785a9c553 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/index.ts @@ -5,6 +5,7 @@ * 2.0. */ -export { SchemaAddFieldModal } from './schema_add_field_modal'; +export { SchemaAddFieldModal } from './add_field_modal'; export { SchemaFieldTypeSelect } from './field_type_select'; export { SchemaErrorsCallout } from './errors_callout'; +export { SchemaErrorsAccordion } from './errors_accordion'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.tsx deleted file mode 100644 index e6f7bffc2d83f..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.tsx +++ /dev/null @@ -1,152 +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 React, { ChangeEvent, FormEvent, useEffect, useState } from 'react'; - -import { - EuiButton, - EuiButtonEmpty, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiForm, - EuiFormRow, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiSpacer, -} from '@elastic/eui'; - -import { CANCEL_BUTTON_LABEL } from '../constants'; - -import { - FIELD_NAME_CORRECT_NOTE, - FIELD_NAME_CORRECTED_PREFIX, - FIELD_NAME_MODAL_TITLE, - FIELD_NAME_MODAL_DESCRIPTION, - FIELD_NAME_MODAL_ADD_FIELD, -} from './constants'; -import { SchemaType } from './types'; - -import { SchemaFieldTypeSelect } from './'; - -interface ISchemaAddFieldModalProps { - disableForm?: boolean; - addFieldFormErrors?: string[] | null; - addNewField(fieldName: string, newFieldType: string): void; - closeAddFieldModal(): void; -} - -export const SchemaAddFieldModal: React.FC = ({ - addNewField, - addFieldFormErrors, - closeAddFieldModal, - disableForm, -}) => { - const [loading, setLoading] = useState(false); - const [newFieldType, updateNewFieldType] = useState(SchemaType.Text); - const [formattedFieldName, setFormattedFieldName] = useState(''); - const [rawFieldName, setRawFieldName] = useState(''); - - useEffect(() => { - if (addFieldFormErrors) setLoading(false); - }, [addFieldFormErrors]); - - const handleChange = ({ currentTarget: { value } }: ChangeEvent) => { - setRawFieldName(value); - setFormattedFieldName(formatFieldName(value)); - }; - - const submitForm = (e: FormEvent) => { - e.preventDefault(); - addNewField(formattedFieldName, newFieldType); - setLoading(true); - }; - - const fieldNameNote = - rawFieldName !== formattedFieldName ? ( - <> - {FIELD_NAME_CORRECTED_PREFIX} {formattedFieldName} - - ) : ( - FIELD_NAME_CORRECT_NOTE - ); - - return ( - -
- - {FIELD_NAME_MODAL_TITLE} - - -

{FIELD_NAME_MODAL_DESCRIPTION}

- - - - - - - - - - - updateNewFieldType(type)} - disabled={disableForm} - data-test-subj="SchemaSelect" - /> - - - - -
- - {CANCEL_BUTTON_LABEL} - - {FIELD_NAME_MODAL_ADD_FIELD} - - -
-
- ); -}; - -const formatFieldName = (rawName: string) => - rawName - .trim() - .replace(/[^a-zA-Z0-9]+/g, '_') - .replace(/^[^a-zA-Z0-9]+/, '') - .replace(/[^a-zA-Z0-9]+$/, '') - .toLowerCase(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.scss b/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.scss deleted file mode 100644 index e8e55ad2827c5..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.scss +++ /dev/null @@ -1,20 +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. - */ - -.schemaFieldError { - border-top: 1px solid $euiColorLightShade; - - &:last-child { - border-bottom: 1px solid $euiColorLightShade; - } - - // Something about the EuiFlexGroup being inside a button collapses the row of items. - // This wrapper div was injected by EUI and had 'with: auto' on it. - .euiIEFlexWrapFix { - width: 100%; - } -} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.tsx deleted file mode 100644 index c2b3ab614fe47..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_errors_accordion.tsx +++ /dev/null @@ -1,121 +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 React from 'react'; - -import { - EuiAccordion, - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiTable, - EuiTableBody, - EuiTableHeader, - EuiTableHeaderCell, - EuiTableRow, - EuiTableRowCell, -} from '@elastic/eui'; - -import { EuiButtonEmptyTo } from '../react_router_helpers'; - -import { TruncatedContent } from '../truncate'; - -import './schema_errors_accordion.scss'; - -import { - ERROR_TABLE_ID_HEADER, - ERROR_TABLE_ERROR_HEADER, - ERROR_TABLE_REVIEW_CONTROL, - ERROR_TABLE_VIEW_LINK, -} from './constants'; -import { FieldCoercionErrors } from './types'; - -interface ISchemaErrorsAccordionProps { - fieldCoercionErrors: FieldCoercionErrors; - schema: { [key: string]: string }; - itemId?: string; - getRoute?(itemId: string, externalId: string): string; -} - -export const SchemaErrorsAccordion: React.FC = ({ - fieldCoercionErrors, - schema, - itemId, - getRoute, -}) => ( - <> - {Object.keys(fieldCoercionErrors).map((fieldName, fieldNameIndex) => { - const errorInfos = fieldCoercionErrors[fieldName]; - - const accordionHeader = ( - - - - - - - - - {schema[fieldName]} - - - - {/* href is needed here because a button cannot be nested in a button or console will error and EuiAccordion uses a button to wrap this. */} - - {ERROR_TABLE_REVIEW_CONTROL} - - - - ); - - return ( - - - - {ERROR_TABLE_ID_HEADER} - {ERROR_TABLE_ERROR_HEADER} - - - - {errorInfos.map((error, errorIndex) => { - const showViewButton = getRoute && itemId; - const documentPath = getRoute && itemId ? getRoute(itemId, error.external_id) : ''; - - const viewButton = showViewButton && ( - - {ERROR_TABLE_VIEW_LINK} - - ); - - return ( - - - - - {error.error} - {showViewButton ? viewButton : } - - ); - })} - - - - ); - })} - -); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx index e9276b8ec3878..f600d089c6e06 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx @@ -14,7 +14,7 @@ import { useParams } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { SchemaErrorsAccordion } from '../../../../../shared/schema/schema_errors_accordion'; +import { SchemaErrorsAccordion } from '../../../../../shared/schema'; import { SchemaChangeErrors } from './schema_change_errors'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx index 7f7b26e380c55..e300823aa3ed3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx @@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom'; import { useActions, useValues } from 'kea'; -import { SchemaErrorsAccordion } from '../../../../../shared/schema/schema_errors_accordion'; +import { SchemaErrorsAccordion } from '../../../../../shared/schema'; import { ViewContentHeader } from '../../../../components/shared/view_content_header'; import { SCHEMA_ERRORS_HEADING } from './constants'; @@ -32,11 +32,7 @@ export const SchemaChangeErrors: React.FC = () => { return ( <> - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index a9526d9450993..3c9cf01bbb4a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -293,13 +293,13 @@ describe('RoleMappingsLogic', () => { expect(http.post).toHaveBeenCalledWith('/api/workplace_search/org/role_mappings', { body: JSON.stringify({ + roleType: 'admin', + allGroups: false, + authProvider: [ANY_AUTH_PROVIDER], rules: { username: '', }, - roleType: 'admin', groups: [], - allGroups: false, - authProvider: [ANY_AUTH_PROVIDER], }), }); await nextTick(); @@ -317,13 +317,13 @@ describe('RoleMappingsLogic', () => { `/api/workplace_search/org/role_mappings/${wsRoleMapping.id}`, { body: JSON.stringify({ + roleType: 'admin', + allGroups: true, + authProvider: [ANY_AUTH_PROVIDER, 'other_auth'], rules: { username: 'user', }, - roleType: 'admin', groups: [], - allGroups: true, - authProvider: [ANY_AUTH_PROVIDER, 'other_auth'], }), } ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index 6e3b74f95f707..0df7f0ba37569 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -28,8 +28,8 @@ import { } from './constants'; interface RoleMappingsServerDetails { - multipleAuthProvidersConfig: boolean; roleMappings: WSRoleMapping[]; + multipleAuthProvidersConfig: boolean; } interface RoleMappingServerDetails { @@ -41,68 +41,69 @@ interface RoleMappingServerDetails { roleMapping?: WSRoleMapping; } +const getFirstAttributeName = (roleMapping: WSRoleMapping): AttributeName => + Object.entries(roleMapping.rules)[0][0] as AttributeName; +const getFirstAttributeValue = (roleMapping: WSRoleMapping): string => + Object.entries(roleMapping.rules)[0][1] as string; + interface RoleMappingsActions { - setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; - setRoleMappingData(data: RoleMappingServerDetails): RoleMappingServerDetails; - handleRoleChange(roleType: Role): { roleType: Role }; handleAllGroupsSelectionChange(selected: boolean): { selected: boolean }; + handleAuthProviderChange(value: string[]): { value: string[] }; handleAttributeSelectorChange( value: AttributeName, firstElasticsearchRole: string ): { value: AttributeName; firstElasticsearchRole: string }; handleAttributeValueChange(value: string): { value: string }; + handleDeleteMapping(): void; handleGroupSelectionChange( groupId: string, selected: boolean ): { groupId: string; selected: boolean }; - handleAuthProviderChange(value: string[]): { value: string[] }; - resetState(): void; - initializeRoleMapping(roleId?: string): { roleId?: string }; + handleRoleChange(roleType: Role): { roleType: Role }; handleSaveMapping(): void; - handleDeleteMapping(): void; + initializeRoleMapping(roleId?: string): { roleId?: string }; initializeRoleMappings(): void; + resetState(): void; + setRoleMappingData(data: RoleMappingServerDetails): RoleMappingServerDetails; + setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; } interface RoleMappingsValues { + includeInAllGroups: boolean; + attributeName: AttributeName; + attributeValue: string; attributes: string[]; availableAuthProviders: string[]; + availableGroups: RoleGroup[]; + dataLoading: boolean; elasticsearchRoles: string[]; + multipleAuthProvidersConfig: boolean; roleMapping: WSRoleMapping | null; roleMappings: WSRoleMapping[]; roleType: Role; - attributeValue: string; - attributeName: AttributeName; - dataLoading: boolean; - multipleAuthProvidersConfig: boolean; - availableGroups: RoleGroup[]; - selectedGroups: Set; - includeInAllGroups: boolean; selectedAuthProviders: string[]; + selectedGroups: Set; } -const getFirstAttributeName = (roleMapping: WSRoleMapping): AttributeName => - Object.entries(roleMapping.rules)[0][0] as AttributeName; -const getFirstAttributeValue = (roleMapping: WSRoleMapping): string => - Object.entries(roleMapping.rules)[0][1] as string; - export const RoleMappingsLogic = kea>({ + path: ['enterprise_search', 'workplace_search', 'role_mappings'], actions: { setRoleMappingsData: (data: RoleMappingsServerDetails) => data, setRoleMappingData: (data: RoleMappingServerDetails) => data, + handleAuthProviderChange: (value: string[]) => ({ value }), handleRoleChange: (roleType: Role) => ({ roleType }), handleGroupSelectionChange: (groupId: string, selected: boolean) => ({ groupId, selected }), - handleAllGroupsSelectionChange: (selected: boolean) => ({ selected }), handleAttributeSelectorChange: (value: string, firstElasticsearchRole: string) => ({ value, firstElasticsearchRole, }), handleAttributeValueChange: (value: string) => ({ value }), - handleAuthProviderChange: (value: string[]) => ({ value }), - resetState: () => true, + handleAllGroupsSelectionChange: (selected: boolean) => ({ selected }), + resetState: true, + initializeRoleMappings: true, initializeRoleMapping: (roleId?: string) => ({ roleId }), - handleSaveMapping: () => true, - handleDeleteMapping: () => true, - initializeRoleMappings: () => true, + handleDeleteMapping: true, + handleSaveMapping: true, }, reducers: { dataLoading: [ @@ -120,10 +121,12 @@ export const RoleMappingsLogic = kea [], }, ], - attributes: [ - [], + multipleAuthProvidersConfig: [ + false, { - setRoleMappingData: (_, { attributes }) => attributes, + setRoleMappingsData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, + setRoleMappingData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, + resetState: () => false, }, ], availableGroups: [ @@ -132,33 +135,10 @@ export const RoleMappingsLogic = kea availableGroups, }, ], - selectedGroups: [ - new Set(), - { - setRoleMappingData: (_, { roleMapping, availableGroups }) => - roleMapping - ? new Set(roleMapping.groups.map((group) => group.id)) - : new Set( - availableGroups - .filter((group) => group.name === DEFAULT_GROUP_NAME) - .map((group) => group.id) - ), - handleGroupSelectionChange: (groups, { groupId, selected }) => { - const newSelectedGroupNames = new Set(groups as Set); - if (selected) { - newSelectedGroupNames.add(groupId); - } else { - newSelectedGroupNames.delete(groupId); - } - return newSelectedGroupNames; - }, - }, - ], - includeInAllGroups: [ - false, + attributes: [ + [], { - setRoleMappingData: (_, { roleMapping }) => (roleMapping ? roleMapping.allGroups : false), - handleAllGroupsSelectionChange: (_, { selected }) => selected, + setRoleMappingData: (_, { attributes }) => attributes, }, ], elasticsearchRoles: [ @@ -182,6 +162,13 @@ export const RoleMappingsLogic = kea roleType, }, ], + includeInAllGroups: [ + false, + { + setRoleMappingData: (_, { roleMapping }) => (roleMapping ? roleMapping.allGroups : false), + handleAllGroupsSelectionChange: (_, { selected }) => selected, + }, + ], attributeValue: [ '', { @@ -202,18 +189,32 @@ export const RoleMappingsLogic = kea 'username', }, ], - availableAuthProviders: [ - [], + selectedGroups: [ + new Set(), { - setRoleMappingData: (_, { authProviders }) => authProviders, + setRoleMappingData: (_, { roleMapping, availableGroups }) => + roleMapping + ? new Set(roleMapping.groups.map((group) => group.id)) + : new Set( + availableGroups + .filter((group) => group.name === DEFAULT_GROUP_NAME) + .map((group) => group.id) + ), + handleGroupSelectionChange: (groups, { groupId, selected }) => { + const newSelectedGroupNames = new Set(groups as Set); + if (selected) { + newSelectedGroupNames.add(groupId); + } else { + newSelectedGroupNames.delete(groupId); + } + return newSelectedGroupNames; + }, }, ], - multipleAuthProvidersConfig: [ - false, + availableAuthProviders: [ + [], { - setRoleMappingsData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, - setRoleMappingData: (_, { multipleAuthProvidersConfig }) => multipleAuthProvidersConfig, - resetState: () => false, + setRoleMappingData: (_, { authProviders }) => authProviders, }, ], selectedAuthProviders: [ @@ -264,13 +265,13 @@ export const RoleMappingsLogic = kea { + const { roleMapping } = values; + if (!roleMapping) return; + const { http } = HttpLogic.values; const { navigateToUrl } = KibanaLogic.values; - const { roleMapping } = values; - if (!roleMapping) { - return; - } const route = `/api/workplace_search/org/role_mappings/${roleMapping.id}`; + if (window.confirm(DELETE_ROLE_MAPPING_MESSAGE)) { try { await http.delete(route); @@ -295,13 +296,13 @@ export const RoleMappingsLogic = kea { }); }); - describe('when the spaces plugin is unavailable', () => { + describe('when the Spaces plugin is unavailable', () => { describe('when security is disabled', () => { it('should allow all access', async () => { const spaces = undefined; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.test.ts index 8ddb254a3cde1..ae308c8397536 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.test.ts @@ -33,7 +33,7 @@ describe('reference application routes', () => { }); expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/:engineName/search_ui/field_config', + path: '/as/engines/:engineName/reference_application/field_config', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.ts index 160b1454c5f22..33684843b218a 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_ui.ts @@ -23,7 +23,7 @@ export function registerSearchUIRoutes({ }, }, enterpriseSearchRequestHandler.createRequest({ - path: '/as/engines/:engineName/search_ui/field_config', + path: '/as/engines/:engineName/reference_application/field_config', }) ); } diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index 0de03e54e1f79..ba809187a549e 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -27,6 +27,20 @@ describe('Features Plugin', () => { namespaceType: 'single' as 'single', }, ]); + typeRegistry.getImportableAndExportableTypes.mockReturnValue([ + { + name: 'hidden-importableAndExportable', + hidden: true, + mappings: { properties: {} }, + namespaceType: 'single' as 'single', + }, + { + name: 'not-hidden-importableAndExportable', + hidden: false, + mappings: { properties: {} }, + namespaceType: 'single' as 'single', + }, + ]); coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry); }); @@ -87,7 +101,9 @@ describe('Features Plugin', () => { `); }); - it('registers kibana features with not hidden saved objects types', async () => { + it('registers kibana features with visible saved objects types and hidden saved object types that are importable and exportable', async () => { + typeRegistry.isHidden.mockReturnValueOnce(true); + typeRegistry.isHidden.mockReturnValueOnce(false); const plugin = new FeaturesPlugin(initContext); await plugin.setup(coreSetup, {}); const { getKibanaFeatures } = plugin.start(coreStart); @@ -98,6 +114,8 @@ describe('Features Plugin', () => { expect(soTypes.includes('foo')).toBe(true); expect(soTypes.includes('bar')).toBe(false); + expect(soTypes.includes('hidden-importableAndExportable')).toBe(true); + expect(soTypes.includes('not-hidden-importableAndExportable')).toBe(false); }); it('returns registered elasticsearch features', async () => { diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 09a5b78ad868a..60a48a539f81e 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -128,7 +128,15 @@ export class FeaturesPlugin private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { const registry = savedObjects.getTypeRegistry(); - const savedObjectTypes = registry.getVisibleTypes().map((t) => t.name); + const savedObjectVisibleTypes = registry.getVisibleTypes().map((t) => t.name); + const savedObjectImportableAndExportableHiddenTypes = registry + .getImportableAndExportableTypes() + .filter((t) => registry.isHidden(t.name)) + .map((t) => t.name); + + const savedObjectTypes = Array.from( + new Set([...savedObjectVisibleTypes, ...savedObjectImportableAndExportableHiddenTypes]) + ); this.logger.debug( `Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${ diff --git a/x-pack/plugins/file_upload/public/validate_index_name.ts b/x-pack/plugins/file_upload/public/validate_index_name.ts index cd190188b6a63..c13f09f58ae21 100644 --- a/x-pack/plugins/file_upload/public/validate_index_name.ts +++ b/x-pack/plugins/file_upload/public/validate_index_name.ts @@ -23,6 +23,12 @@ export function checkIndexPatternValid(name: string) { } export const validateIndexName = async (indexName: string) => { + if (!indexName) { + return i18n.translate('xpack.fileUpload.indexNameRequired', { + defaultMessage: 'Index name required', + }); + } + if (!checkIndexPatternValid(indexName)) { return i18n.translate('xpack.fileUpload.indexNameContainsIllegalCharactersErrorMessage', { defaultMessage: 'Index name contains illegal characters.', diff --git a/x-pack/plugins/file_upload/server/routes.ts b/x-pack/plugins/file_upload/server/routes.ts index 8e6651ed891c6..847a57afb391c 100644 --- a/x-pack/plugins/file_upload/server/routes.ts +++ b/x-pack/plugins/file_upload/server/routes.ts @@ -185,7 +185,7 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge /** * @apiGroup FileDataVisualizer * - * @api {post} /internal/file_upload/index_exists ES Field caps wrapper checks if index exists + * @api {post} /internal/file_upload/index_exists ES indices exists wrapper checks if index exists * @apiName IndexExists */ router.post( @@ -200,20 +200,10 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge }, async (context, request, response) => { try { - const { index } = request.body; - - const options = { - index: [index], - fields: ['*'], - ignore_unavailable: true, - allow_no_indices: true, - }; - - const { body } = await context.core.elasticsearch.client.asCurrentUser.fieldCaps(options); - const exists = Array.isArray(body.indices) && body.indices.length !== 0; - return response.ok({ - body: { exists }, - }); + const { + body: indexExists, + } = await context.core.elasticsearch.client.asCurrentUser.indices.exists(request.body); + return response.ok({ body: { exists: indexExists } }); } catch (e) { return response.customError(wrapError(e)); } diff --git a/x-pack/plugins/fleet/common/constants/agent.ts b/x-pack/plugins/fleet/common/constants/agent.ts index 92e24256c7a2b..6d85f658f2240 100644 --- a/x-pack/plugins/fleet/common/constants/agent.ts +++ b/x-pack/plugins/fleet/common/constants/agent.ts @@ -6,9 +6,6 @@ */ export const AGENT_SAVED_OBJECT_TYPE = 'fleet-agents'; -// TODO: Remove this saved object type. Core will drop any saved objects of -// this type during migrations. See https://github.com/elastic/kibana/issues/91869 -export const AGENT_EVENT_SAVED_OBJECT_TYPE = 'fleet-agent-events'; export const AGENT_ACTION_SAVED_OBJECT_TYPE = 'fleet-agent-actions'; export const AGENT_TYPE_PERMANENT = 'PERMANENT'; diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 377cb8d8bd871..037c0ee506a05 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -85,7 +85,6 @@ export const AGENT_API_ROUTES = { INFO_PATTERN: `${API_ROOT}/agents/{agentId}`, UPDATE_PATTERN: `${API_ROOT}/agents/{agentId}`, DELETE_PATTERN: `${API_ROOT}/agents/{agentId}`, - EVENTS_PATTERN: `${API_ROOT}/agents/{agentId}/events`, CHECKIN_PATTERN: `${API_ROOT}/agents/{agentId}/checkin`, ACKS_PATTERN: `${API_ROOT}/agents/{agentId}/acks`, ACTIONS_PATTERN: `${API_ROOT}/agents/{agentId}/actions`, diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml index a2647b71c70cc..c21651ca7f8be 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml @@ -30,10 +30,6 @@ properties: $ref: ./agent_metadata.yaml id: type: string - current_error_events: - type: array - items: - $ref: ./agent_event.yaml access_api_key: type: string status: @@ -45,5 +41,4 @@ required: - active - enrolled_at - id - - current_error_events - status diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@events.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@events.yaml deleted file mode 100644 index db8d28f72b5a2..0000000000000 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@events.yaml +++ /dev/null @@ -1,11 +0,0 @@ -parameters: - - schema: - type: string - name: agentId - in: path - required: true -get: - summary: Fleet - Agent - Events - tags: [] - responses: {} - operationId: get-fleet-agents-agentId-events diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts index f6656b881c598..1346628758a49 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts @@ -83,7 +83,6 @@ const getAgent = ({ policy_revision: 1, packages: ['system'], last_checkin: '2020-10-01T14:43:27.255Z', - current_error_events: [], status: 'online', }; if (upgradeable) { diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index 6156decf8641d..1e51f2d6163cc 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -134,7 +134,6 @@ export const fleetSetupRouteService = { export const agentRouteService = { getInfoPath: (agentId: string) => AGENT_API_ROUTES.INFO_PATTERN.replace('{agentId}', agentId), getUpdatePath: (agentId: string) => AGENT_API_ROUTES.UPDATE_PATTERN.replace('{agentId}', agentId), - getEventsPath: (agentId: string) => AGENT_API_ROUTES.EVENTS_PATTERN.replace('{agentId}', agentId), getUnenrollPath: (agentId: string) => AGENT_API_ROUTES.UNENROLL_PATTERN.replace('{agentId}', agentId), getBulkUnenrollPath: () => AGENT_API_ROUTES.BULK_UNENROLL_PATTERN, diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 0629a67f0d8d3..de00f623b829b 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -93,38 +93,6 @@ export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & { }; export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes; -export interface NewAgentEvent { - type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; - subtype: // State - | 'RUNNING' - | 'STARTING' - | 'IN_PROGRESS' - | 'CONFIG' - | 'FAILED' - | 'STOPPING' - | 'STOPPED' - | 'DEGRADED' - | 'UPDATING' - // Action results - | 'DATA_DUMP' - // Actions - | 'ACKNOWLEDGED' - | 'UNKNOWN'; - timestamp: string; - message: string; - payload?: any; - agent_id: string; - action_id?: string; - policy_id?: string; - stream_id?: string; -} - -export interface AgentEvent extends NewAgentEvent { - id: string; -} - -export type AgentEventSOAttributes = NewAgentEvent; - export interface AgentMetadata { [x: string]: any; } @@ -149,14 +117,12 @@ interface AgentBase { export interface Agent extends AgentBase { id: string; - current_error_events: AgentEvent[]; access_api_key?: string; status?: string; packages: string[]; } export interface AgentSOAttributes extends AgentBase { - current_error_events?: string; packages?: string[]; } diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 47f9112d4ab59..e6da9d4498ce2 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -5,15 +5,7 @@ * 2.0. */ -import type { - Agent, - AgentAction, - NewAgentAction, - NewAgentEvent, - AgentEvent, - AgentStatus, - AgentType, -} from '../models'; +import type { Agent, AgentAction, NewAgentAction } from '../models'; export interface GetAgentsRequest { query: { @@ -43,52 +35,6 @@ export interface GetOneAgentResponse { item: Agent; } -export interface PostAgentCheckinRequest { - params: { - agentId: string; - }; - body: { - status?: 'online' | 'error' | 'degraded'; - local_metadata?: Record; - events?: NewAgentEvent[]; - }; -} - -export interface PostAgentCheckinResponse { - action: string; - - actions: AgentAction[]; -} - -export interface PostAgentEnrollRequest { - body: { - type: AgentType; - metadata: { - local: Record; - user_provided: Record; - }; - }; -} - -export interface PostAgentEnrollResponse { - action: string; - - item: Agent & { status: AgentStatus }; -} - -export interface PostAgentAcksRequest { - body: { - events: AgentEvent[]; - }; - params: { - agentId: string; - }; -} - -export interface PostAgentAcksResponse { - action: string; -} - export interface PostNewAgentActionRequest { body: { action: NewAgentAction; @@ -185,24 +131,6 @@ export type PostBulkAgentReassignResponse = Record< } >; -export interface GetOneAgentEventsRequest { - params: { - agentId: string; - }; - query: { - page: number; - perPage: number; - kuery?: string; - }; -} - -export interface GetOneAgentEventsResponse { - list: AgentEvent[]; - total: number; - page: number; - perPage: number; -} - export interface DeleteAgentRequest { params: { agentId: string; diff --git a/x-pack/plugins/fleet/public/applications/fleet/constants/index.ts b/x-pack/plugins/fleet/public/applications/fleet/constants/index.ts index 539e7d38f4015..bbf14dcae45fc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/constants/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/constants/index.ts @@ -11,7 +11,6 @@ export { AGENT_API_ROUTES, SO_SEARCH_LIMIT, AGENT_POLICY_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_request/agents.ts b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_request/agents.ts index 90ca762173a80..0fbe59f3f48ee 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_request/agents.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_request/agents.ts @@ -9,8 +9,6 @@ import { agentRouteService } from '../../services'; import type { GetOneAgentResponse, - GetOneAgentEventsResponse, - GetOneAgentEventsRequest, PostAgentUnenrollRequest, PostBulkAgentUnenrollRequest, PostBulkAgentUnenrollResponse, @@ -44,19 +42,6 @@ export function useGetOneAgent(agentId: string, options?: RequestOptions) { }); } -export function useGetOneAgentEvents( - agentId: string, - query: GetOneAgentEventsRequest['query'], - options?: RequestOptions -) { - return useRequest({ - path: agentRouteService.getEventsPath(agentId), - method: 'get', - query, - ...options, - }); -} - export function useGetAgents(query: GetAgentsRequest['query'], options?: RequestOptions) { return useRequest({ method: 'get', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx index eb693775a65c9..f597d2c6758a8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_health.tsx @@ -82,9 +82,6 @@ export const AgentHealth: React.FunctionComponent = ({ agent }) => { lastCheckIn: , }} /> - {agent.current_error_events.map((event, idx) => ( -

{event.message}

- ))} ) : ( = ({ required={true} defaultValue={policyIdDefaultValue} {...form.policyIdInput.props} - options={agentPolicies.map((agentPolicy) => ({ - value: agentPolicy.id, - text: agentPolicy.name, - }))} + options={agentPolicies + .filter((agentPolicy) => !agentPolicy.is_managed) + .map((agentPolicy) => ({ + value: agentPolicy.id, + text: agentPolicy.name, + }))} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx index 5d867a1c4c93c..6d141b0c9ebf1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx @@ -31,7 +31,7 @@ import { useStartServices, sendDeleteOneEnrollmentAPIKey, } from '../../../hooks'; -import type { EnrollmentAPIKey } from '../../../types'; +import type { EnrollmentAPIKey, GetAgentPoliciesResponseItem } from '../../../types'; import { SearchBar } from '../../../components/search_bar'; import { NewEnrollmentTokenFlyout } from './components/new_enrollment_key_flyout'; @@ -171,9 +171,21 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { }); const agentPolicies = agentPoliciesRequest.data ? agentPoliciesRequest.data.items : []; + const agentPoliciesById = agentPolicies.reduce( + (acc: { [key: string]: GetAgentPoliciesResponseItem }, policy) => { + acc[policy.id] = policy; + return acc; + }, + {} + ); const total = enrollmentAPIKeysRequest?.data?.total ?? 0; - const items = enrollmentAPIKeysRequest?.data?.list ?? []; + const rowItems = + enrollmentAPIKeysRequest?.data?.list.filter((enrollmentKey) => { + if (!agentPolicies.length || !enrollmentKey.policy_id) return false; + const agentPolicy = agentPoliciesById[enrollmentKey.policy_id]; + return !agentPolicy?.is_managed; + }) || []; const columns = [ { @@ -203,7 +215,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { defaultMessage: 'Agent policy', }), render: (policyId: string) => { - const agentPolicy = agentPolicies.find((c) => c.id === policyId); + const agentPolicy = agentPoliciesById[policyId]; const value = agentPolicy ? agentPolicy.name : policyId; return ( @@ -314,7 +326,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { /> ) } - items={total ? items : []} + items={total ? rowItems : []} itemId="id" columns={columns} pagination={{ diff --git a/x-pack/plugins/fleet/public/applications/fleet/types/index.ts b/x-pack/plugins/fleet/public/applications/fleet/types/index.ts index 0d85bfcdb6af6..ea9bab2fb74e5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/types/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/types/index.ts @@ -13,7 +13,6 @@ export { AgentMetadata, AgentPolicy, NewAgentPolicy, - AgentEvent, SimplifiedAgentStatus, EnrollmentAPIKey, PackagePolicy, @@ -64,8 +63,6 @@ export { PostBulkAgentUpgradeRequest, PostAgentUpgradeResponse, PostBulkAgentUpgradeResponse, - GetOneAgentEventsRequest, - GetOneAgentEventsResponse, GetAgentStatusRequest, GetAgentStatusResponse, PutAgentReassignRequest, diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index e2f800f67705d..cc7f2b11007a5 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -36,7 +36,6 @@ export { // Saved object types SO_SEARCH_LIMIT, AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_ACTION_SAVED_OBJECT_TYPE, AGENT_POLICY_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 704df5970b345..61c3a83242c57 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -46,7 +46,6 @@ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGES_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, } from './constants'; @@ -128,7 +127,6 @@ const allSavedObjectTypes = [ PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGES_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, ]; diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index c485cae4b3e37..9280b7f0b2e0d 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -11,7 +11,6 @@ import type { TypeOf } from '@kbn/config-schema'; import type { GetAgentsResponse, GetOneAgentResponse, - GetOneAgentEventsResponse, GetAgentStatusResponse, PutAgentReassignResponse, PostBulkAgentReassignResponse, @@ -21,7 +20,6 @@ import type { GetOneAgentRequestSchema, UpdateAgentRequestSchema, DeleteAgentRequestSchema, - GetOneAgentEventsRequestSchema, GetAgentStatusRequestSchema, PutAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, @@ -57,39 +55,6 @@ export const getAgentHandler: RequestHandler< } }; -export const getAgentEventsHandler: RequestHandler< - TypeOf, - TypeOf -> = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - try { - const { page, perPage, kuery } = request.query; - const { items, total } = await AgentService.getAgentEvents(soClient, request.params.agentId, { - page, - perPage, - kuery, - }); - - const body: GetOneAgentEventsResponse = { - list: items, - total, - page, - perPage, - }; - - return response.ok({ - body, - }); - } catch (error) { - if (error.isBoom && error.output.statusCode === 404) { - return response.notFound({ - body: { message: `Agent ${request.params.agentId} not found` }, - }); - } - return defaultIngestErrorHandler({ error, response }); - } -}; - export const deleteAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index 9b33443d0dca3..db5b01b319e00 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -11,7 +11,6 @@ import { PLUGIN_ID, AGENT_API_ROUTES } from '../../constants'; import { GetAgentsRequestSchema, GetOneAgentRequestSchema, - GetOneAgentEventsRequestSchema, UpdateAgentRequestSchema, DeleteAgentRequestSchema, PostAgentUnenrollRequestSchema, @@ -31,7 +30,6 @@ import { getAgentHandler, updateAgentHandler, deleteAgentHandler, - getAgentEventsHandler, getAgentStatusForAgentPolicyHandler, putAgentsReassignHandler, postBulkAgentsReassignHandler, @@ -109,16 +107,6 @@ export const registerAPIRoutes = (router: IRouter, config: FleetConfigType) => { putAgentsReassignHandler ); - // Get agent events - router.get( - { - path: AGENT_API_ROUTES.EVENTS_PATTERN, - validate: GetOneAgentEventsRequestSchema, - options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - getAgentEventsHandler - ); - // Get agent status for policy router.get( { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index bc9b2d9f9dc86..e2e33f1ee6c26 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -15,7 +15,6 @@ import { PACKAGES_SAVED_OBJECT_TYPE, ASSETS_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_ACTION_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, @@ -24,7 +23,6 @@ import { import { migrateAgentActionToV7100, - migrateAgentEventToV7100, migrateAgentPolicyToV7100, migrateAgentToV7100, migrateEnrollmentApiKeysToV7100, @@ -133,33 +131,6 @@ const getSavedObjectTypes = ( '7.10.0': migrateAgentActionToV7100(encryptedSavedObjects), }, }, - // TODO: Remove this saved object type. Core will drop any saved objects of - // this type during migrations. See https://github.com/elastic/kibana/issues/91869 - [AGENT_EVENT_SAVED_OBJECT_TYPE]: { - name: AGENT_EVENT_SAVED_OBJECT_TYPE, - hidden: false, - namespaceType: 'agnostic', - management: { - importableAndExportable: false, - }, - mappings: { - properties: { - type: { type: 'keyword' }, - subtype: { type: 'keyword' }, - agent_id: { type: 'keyword' }, - action_id: { type: 'keyword' }, - policy_id: { type: 'keyword' }, - stream_id: { type: 'keyword' }, - timestamp: { type: 'date' }, - message: { type: 'text' }, - payload: { type: 'text' }, - data: { type: 'text' }, - }, - }, - migrations: { - '7.10.0': migrateAgentEventToV7100, - }, - }, [AGENT_POLICY_SAVED_OBJECT_TYPE]: { name: AGENT_POLICY_SAVED_OBJECT_TYPE, hidden: false, diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_10_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_10_0.ts index f3f6050a8cde2..50780f168c459 100644 --- a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_10_0.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_10_0.ts @@ -10,7 +10,6 @@ import type { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/s import type { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server'; import type { Agent, - AgentEvent, AgentPolicy, PackagePolicy, EnrollmentAPIKey, @@ -35,18 +34,6 @@ export const migrateAgentToV7100: SavedObjectMigrationFn< return agentDoc; }; -export const migrateAgentEventToV7100: SavedObjectMigrationFn< - Exclude & { - config_id?: string; - }, - AgentEvent -> = (agentEventDoc) => { - agentEventDoc.attributes.policy_id = agentEventDoc.attributes.config_id; - delete agentEventDoc.attributes.config_id; - - return agentEventDoc; -}; - export const migrateAgentPolicyToV7100: SavedObjectMigrationFn< Exclude & { package_configs: string[] | PackagePolicy[]; diff --git a/x-pack/plugins/fleet/server/services/agents/events.ts b/x-pack/plugins/fleet/server/services/agents/events.ts deleted file mode 100644 index e61ce04f1b640..0000000000000 --- a/x-pack/plugins/fleet/server/services/agents/events.ts +++ /dev/null @@ -1,48 +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 { SavedObjectsClientContract } from 'src/core/server'; - -import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; -import type { AgentEventSOAttributes, AgentEvent } from '../../types'; -import { normalizeKuery } from '../saved_object'; - -export async function getAgentEvents( - soClient: SavedObjectsClientContract, - agentId: string, - options: { - kuery?: string; - page: number; - perPage: number; - } -) { - const { page, perPage, kuery } = options; - - // eslint-disable-next-line @typescript-eslint/naming-convention - const { total, saved_objects } = await soClient.find({ - type: AGENT_EVENT_SAVED_OBJECT_TYPE, - filter: - kuery && kuery !== '' ? normalizeKuery(AGENT_EVENT_SAVED_OBJECT_TYPE, kuery) : undefined, - perPage, - page, - sortField: 'timestamp', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - search: agentId, - searchFields: ['agent_id'], - }); - - const items: AgentEvent[] = saved_objects.map((so) => { - return { - id: so.id, - ...so.attributes, - payload: so.attributes.payload ? JSON.parse(so.attributes.payload) : undefined, - }; - }); - - return { items, total }; -} diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts index c003d3d546e83..c4273a57ddffd 100644 --- a/x-pack/plugins/fleet/server/services/agents/helpers.ts +++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts @@ -21,7 +21,6 @@ export function searchHitToAgent(hit: FleetServerAgentESResponse): Agent { id: hit._id, ...hit._source, policy_revision: hit._source?.policy_revision_idx, - current_error_events: [], access_api_key: undefined, status: undefined, packages: hit._source?.packages ?? [], diff --git a/x-pack/plugins/fleet/server/services/agents/index.ts b/x-pack/plugins/fleet/server/services/agents/index.ts index 66303514c4fe7..ede548c6fd60d 100644 --- a/x-pack/plugins/fleet/server/services/agents/index.ts +++ b/x-pack/plugins/fleet/server/services/agents/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -export * from './events'; export * from './unenroll'; export * from './upgrade'; export * from './status'; diff --git a/x-pack/plugins/fleet/server/services/agents/saved_objects.ts b/x-pack/plugins/fleet/server/services/agents/saved_objects.ts index 8438048f12a87..eec9c9c7c16ff 100644 --- a/x-pack/plugins/fleet/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/fleet/server/services/agents/saved_objects.ts @@ -26,9 +26,6 @@ export function savedObjectToAgent(so: SavedObject): Agent { return { id: so.id, ...so.attributes, - current_error_events: so.attributes.current_error_events - ? JSON.parse(so.attributes.current_error_events) - : [], local_metadata: so.attributes.local_metadata, user_provided_metadata: so.attributes.user_provided_metadata, access_api_key: undefined, diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index 737b6874a8133..a0338aced65ff 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -8,7 +8,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import pMap from 'p-map'; -import { AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import type { AgentStatus } from '../../types'; import { AgentStatusKueryHelper } from '../../../common/services'; import { esKuery } from '../../../../../../src/plugins/data/server'; @@ -84,7 +84,6 @@ export async function getAgentStatusForAgentPolicy( ); return { - events: await getEventsCount(soClient, agentPolicyId), total: allActive.total, inactive: all.total - allActive.total, online: online.total, @@ -92,20 +91,7 @@ export async function getAgentStatusForAgentPolicy( offline: offline.total, updating: updating.total, other: all.total - online.total - error.total - offline.total, + /* @deprecated Agent events do not exists anymore */ + events: 0, }; } - -async function getEventsCount(soClient: SavedObjectsClientContract, agentPolicyId?: string) { - const { total } = await soClient.find({ - type: AGENT_EVENT_SAVED_OBJECT_TYPE, - searchFields: ['policy_id'], - search: agentPolicyId, - perPage: 0, - page: 1, - sortField: 'timestamp', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - }); - - return total; -} diff --git a/x-pack/plugins/fleet/server/services/hosts_utils.test.ts b/x-pack/plugins/fleet/server/services/hosts_utils.test.ts new file mode 100644 index 0000000000000..785fbbc3f5bdd --- /dev/null +++ b/x-pack/plugins/fleet/server/services/hosts_utils.test.ts @@ -0,0 +1,27 @@ +/* + * 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 { normalizeHostsForAgents } from './hosts_utils'; + +describe('normalizeHostsForAgents', () => { + const scenarios = [ + { sourceUrl: 'http://test.fr', expectedUrl: 'http://test.fr:80' }, + { sourceUrl: 'http://test.fr/test/toto', expectedUrl: 'http://test.fr:80/test/toto' }, + { sourceUrl: 'https://test.fr', expectedUrl: 'https://test.fr:443' }, + { sourceUrl: 'https://test.fr/test/toto', expectedUrl: 'https://test.fr:443/test/toto' }, + { sourceUrl: 'https://test.fr:9243', expectedUrl: 'https://test.fr:9243' }, + { sourceUrl: 'https://test.fr:9243/test/toto', expectedUrl: 'https://test.fr:9243/test/toto' }, + ]; + + for (const scenario of scenarios) { + it(`should transform ${scenario.sourceUrl} correctly`, () => { + const url = normalizeHostsForAgents(scenario.sourceUrl); + + expect(url).toEqual(scenario.expectedUrl); + }); + } +}); diff --git a/x-pack/plugins/fleet/server/services/hosts_utils.ts b/x-pack/plugins/fleet/server/services/hosts_utils.ts new file mode 100644 index 0000000000000..2db77bc12879b --- /dev/null +++ b/x-pack/plugins/fleet/server/services/hosts_utils.ts @@ -0,0 +1,30 @@ +/* + * 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. + */ + +function getPortForURL(url: URL) { + if (url.port !== '') { + return url.port; + } + + if (url.protocol === 'http:') { + return '80'; + } + + if (url.protocol === 'https:') { + return '443'; + } +} + +export function normalizeHostsForAgents(host: string) { + // Elastic Agent is not using default port for http|https for Fleet server and ES https://github.com/elastic/beats/issues/25420 + const hostURL = new URL(host); + + // We are building the URL manualy as url format will not include the port if the port is 80 or 443 + return `${hostURL.protocol}//${hostURL.hostname}:${getPortForURL(hostURL)}${ + hostURL.pathname === '/' ? '' : hostURL.pathname + }`; +} diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 6f043be25b67c..b3857ba5c0ef3 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -12,6 +12,7 @@ import { DEFAULT_OUTPUT, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; import { decodeCloudId } from '../../common'; import { appContextService } from './app_context'; +import { normalizeHostsForAgents } from './hosts_utils'; const SAVED_OBJECT_TYPE = OUTPUT_SAVED_OBJECT_TYPE; @@ -49,14 +50,6 @@ class OutputService { }; } - public async updateOutput( - soClient: SavedObjectsClientContract, - id: string, - data: Partial - ) { - await soClient.update(SAVED_OBJECT_TYPE, id, data); - } - public async getDefaultOutputId(soClient: SavedObjectsClientContract) { const outputs = await this.getDefaultOutput(soClient); @@ -72,9 +65,15 @@ class OutputService { output: NewOutput, options?: { id?: string } ): Promise { + const data = { ...output }; + + if (data.hosts) { + data.hosts = data.hosts.map(normalizeHostsForAgents); + } + const newSo = await soClient.create( SAVED_OBJECT_TYPE, - output as Output, + data as Output, options ); @@ -98,7 +97,13 @@ class OutputService { } public async update(soClient: SavedObjectsClientContract, id: string, data: Partial) { - const outputSO = await soClient.update(SAVED_OBJECT_TYPE, id, data); + const updateData = { ...data }; + + if (updateData.hosts) { + updateData.hosts = updateData.hosts.map(normalizeHostsForAgents); + } + + const outputSO = await soClient.update(SAVED_OBJECT_TYPE, id, updateData); if (outputSO.error) { throw new Error(outputSO.error.message); diff --git a/x-pack/plugins/fleet/server/services/settings.test.ts b/x-pack/plugins/fleet/server/services/settings.test.ts index bec0f737c0bc8..75712c471f20c 100644 --- a/x-pack/plugins/fleet/server/services/settings.test.ts +++ b/x-pack/plugins/fleet/server/services/settings.test.ts @@ -8,7 +8,7 @@ import { savedObjectsClientMock } from 'src/core/server/mocks'; import { appContextService } from './app_context'; -import { getCloudFleetServersHosts, normalizeFleetServerHost, settingsSetup } from './settings'; +import { getCloudFleetServersHosts, settingsSetup } from './settings'; jest.mock('./app_context'); @@ -205,22 +205,3 @@ describe('settingsSetup', () => { expect(soClientMock.update).not.toBeCalled(); }); }); - -describe('normalizeFleetServerHost', () => { - const scenarios = [ - { sourceUrl: 'http://test.fr', expectedUrl: 'http://test.fr:80' }, - { sourceUrl: 'http://test.fr/test/toto', expectedUrl: 'http://test.fr:80/test/toto' }, - { sourceUrl: 'https://test.fr', expectedUrl: 'https://test.fr:443' }, - { sourceUrl: 'https://test.fr/test/toto', expectedUrl: 'https://test.fr:443/test/toto' }, - { sourceUrl: 'https://test.fr:9243', expectedUrl: 'https://test.fr:9243' }, - { sourceUrl: 'https://test.fr:9243/test/toto', expectedUrl: 'https://test.fr:9243/test/toto' }, - ]; - - for (const scenario of scenarios) { - it(`should transform ${scenario.sourceUrl} correctly`, () => { - const url = normalizeFleetServerHost(scenario.sourceUrl); - - expect(url).toEqual(scenario.expectedUrl); - }); - } -}); diff --git a/x-pack/plugins/fleet/server/services/settings.ts b/x-pack/plugins/fleet/server/services/settings.ts index 6b1a17fc5b060..226fbb29467c2 100644 --- a/x-pack/plugins/fleet/server/services/settings.ts +++ b/x-pack/plugins/fleet/server/services/settings.ts @@ -12,6 +12,7 @@ import { decodeCloudId, GLOBAL_SETTINGS_SAVED_OBJECT_TYPE } from '../../common'; import type { SettingsSOAttributes, Settings, BaseSettings } from '../../common'; import { appContextService } from './app_context'; +import { normalizeHostsForAgents } from './hosts_utils'; export async function getSettings(soClient: SavedObjectsClientContract): Promise { const res = await soClient.find({ @@ -51,42 +52,18 @@ export async function settingsSetup(soClient: SavedObjectsClientContract) { } } -function getPortForURL(url: URL) { - if (url.port !== '') { - return url.port; - } - - if (url.protocol === 'http:') { - return '80'; - } - - if (url.protocol === 'https:') { - return '443'; - } -} - -export function normalizeFleetServerHost(host: string) { - // Fleet server is not using default port for http|https https://github.com/elastic/beats/issues/25420 - const fleetServerURL = new URL(host); - - // We are building the URL manualy as url format will not include the port if the port is 80 or 443 - return `${fleetServerURL.protocol}//${fleetServerURL.hostname}:${getPortForURL(fleetServerURL)}${ - fleetServerURL.pathname === '/' ? '' : fleetServerURL.pathname - }`; -} - export async function saveSettings( soClient: SavedObjectsClientContract, newData: Partial> ): Promise & Pick> { + const data = { ...newData }; + if (data.fleet_server_hosts) { + data.fleet_server_hosts = data.fleet_server_hosts.map(normalizeHostsForAgents); + } + try { const settings = await getSettings(soClient); - const data = { ...newData }; - if (data.fleet_server_hosts) { - data.fleet_server_hosts = data.fleet_server_hosts.map(normalizeFleetServerHost); - } - const res = await soClient.update( GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, settings.id, @@ -102,7 +79,7 @@ export async function saveSettings( const defaultSettings = createDefaultSettings(); const res = await soClient.create(GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, { ...defaultSettings, - ...newData, + ...data, }); return { diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 87808e03fe70b..886b4a26b833b 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -12,9 +12,6 @@ export { AgentSOAttributes, AgentStatus, AgentType, - NewAgentEvent, - AgentEvent, - AgentEventSOAttributes, AgentAction, AgentPolicyAction, AgentPolicyActionV7_9, diff --git a/x-pack/plugins/fleet/server/types/models/agent.ts b/x-pack/plugins/fleet/server/types/models/agent.ts index 192bb83a88718..aef0dfb8b2f83 100644 --- a/x-pack/plugins/fleet/server/types/models/agent.ts +++ b/x-pack/plugins/fleet/server/types/models/agent.ts @@ -15,55 +15,6 @@ export const AgentTypeSchema = schema.oneOf([ schema.literal(AGENT_TYPE_TEMPORARY), ]); -const AgentEventBase = { - type: schema.oneOf([ - schema.literal('STATE'), - schema.literal('ERROR'), - schema.literal('ACTION_RESULT'), - schema.literal('ACTION'), - ]), - subtype: schema.oneOf([ - // State - schema.oneOf([ - schema.literal('RUNNING'), - schema.literal('STARTING'), - schema.literal('IN_PROGRESS'), - schema.literal('CONFIG'), - schema.literal('FAILED'), - schema.literal('STOPPING'), - schema.literal('STOPPED'), - schema.literal('DEGRADED'), - schema.literal('UPDATING'), - ]), - // Action results - schema.literal('DATA_DUMP'), - // Actions - schema.literal('ACKNOWLEDGED'), - schema.literal('UNKNOWN'), - ]), - timestamp: schema.string(), - message: schema.string(), - payload: schema.maybe(schema.any()), - agent_id: schema.string(), - action_id: schema.maybe(schema.string()), - policy_id: schema.maybe(schema.string()), - stream_id: schema.maybe(schema.string()), -}; - -export const AckEventSchema = schema.object({ - ...AgentEventBase, - ...{ action_id: schema.string() }, -}); - -export const NewAgentEventSchema = schema.object({ - ...AgentEventBase, -}); - -export const AgentEventSchema = schema.object({ - ...AgentEventBase, - id: schema.string(), -}); - export const NewAgentActionSchema = schema.oneOf([ schema.object({ type: schema.oneOf([ diff --git a/x-pack/plugins/fleet/server/types/models/package_policy.ts b/x-pack/plugins/fleet/server/types/models/package_policy.ts index 1f39b3135cb3f..5a8fd70a9b84e 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -24,6 +24,7 @@ const ConfigRecordSchema = schema.recordOf( schema.object({ type: schema.maybe(schema.string()), value: schema.maybe(schema.any()), + frozen: schema.maybe(schema.boolean()), }) ); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index a58849ee4ab4b..3f66c8159562a 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -25,138 +25,6 @@ export const GetOneAgentRequestSchema = { }), }; -export const PostAgentCheckinRequestParamsJSONSchema = { - type: 'object', - properties: { - agentId: { type: 'string' }, - }, - required: ['agentId'], -}; - -export const PostAgentCheckinRequestBodyJSONSchema = { - type: 'object', - properties: { - status: { type: 'string', enum: ['online', 'error', 'degraded'] }, - local_metadata: { - additionalProperties: { - anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'object' }], - }, - }, - events: { - type: 'array', - items: { - type: 'object', - properties: { - type: { type: 'string', enum: ['STATE', 'ERROR', 'ACTION_RESULT', 'ACTION'] }, - subtype: { - type: 'string', - enum: [ - 'RUNNING', - 'STARTING', - 'IN_PROGRESS', - 'CONFIG', - 'FAILED', - 'STOPPING', - 'STOPPED', - 'DEGRADED', - 'DATA_DUMP', - 'ACKNOWLEDGED', - 'UPDATING', - 'UNKNOWN', - ], - }, - timestamp: { type: 'string' }, - message: { type: 'string' }, - payload: { type: 'object', additionalProperties: true }, - agent_id: { type: 'string' }, - action_id: { type: 'string' }, - policy_id: { type: 'string' }, - stream_id: { type: 'string' }, - }, - required: ['type', 'subtype', 'timestamp', 'message', 'agent_id'], - additionalProperties: false, - }, - }, - }, - additionalProperties: false, -}; - -export const PostAgentEnrollRequestBodyJSONSchema = { - type: 'object', - properties: { - type: { type: 'string', enum: ['EPHEMERAL', 'PERMANENT', 'TEMPORARY'] }, - // TODO deprecated should be removed in 8.0.0 - shared_id: { type: 'string' }, - metadata: { - type: 'object', - properties: { - local: { - type: 'object', - additionalProperties: true, - }, - user_provided: { - type: 'object', - additionalProperties: true, - }, - }, - additionalProperties: false, - required: ['local', 'user_provided'], - }, - }, - additionalProperties: false, - required: ['type', 'metadata'], -}; - -export const PostAgentAcksRequestParamsJSONSchema = { - type: 'object', - properties: { - agentId: { type: 'string' }, - }, - required: ['agentId'], -}; - -export const PostAgentAcksRequestBodyJSONSchema = { - type: 'object', - properties: { - events: { - type: 'array', - item: { - type: 'object', - properties: { - type: { type: 'string', enum: ['STATE', 'ERROR', 'ACTION_RESULT', 'ACTION'] }, - subtype: { - type: 'string', - enum: [ - 'RUNNING', - 'STARTING', - 'IN_PROGRESS', - 'CONFIG', - 'FAILED', - 'STOPPING', - 'STOPPED', - 'DEGRADED', - 'DATA_DUMP', - 'ACKNOWLEDGED', - 'UNKNOWN', - ], - }, - timestamp: { type: 'string' }, - message: { type: 'string' }, - payload: { type: 'object', additionalProperties: true }, - agent_id: { type: 'string' }, - action_id: { type: 'string' }, - policy_id: { type: 'string' }, - stream_id: { type: 'string' }, - }, - required: ['type', 'subtype', 'timestamp', 'message', 'agent_id', 'action_id'], - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - required: ['events'], -}; - export const PostNewAgentActionRequestSchema = { body: schema.object({ action: NewAgentActionSchema, @@ -222,17 +90,6 @@ export const PostBulkAgentReassignRequestSchema = { }), }; -export const GetOneAgentEventsRequestSchema = { - params: schema.object({ - agentId: schema.string(), - }), - query: schema.object({ - page: schema.number({ defaultValue: 1 }), - perPage: schema.number({ defaultValue: 20 }), - kuery: schema.maybe(schema.string()), - }), -}; - export const DeleteAgentRequestSchema = { params: schema.object({ agentId: schema.string(), diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx index 808d8ad238c3e..77b0372e24994 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx @@ -12,7 +12,6 @@ import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_reac import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; import { licensingMock } from '../../../../licensing/public/mocks'; import { App } from '../../../public/application/app'; -import { TestSubjects } from '../helpers'; const breadcrumbService = createBreadcrumbsMock(); @@ -37,7 +36,7 @@ const getTestBedConfig = (initialEntries: string[]): TestBedConfig => ({ const initTestBed = (initialEntries: string[]) => registerTestBed(AppWithContext, getTestBedConfig(initialEntries))(); -export interface AppTestBed extends TestBed { +export interface AppTestBed extends TestBed { actions: { clickPolicyNameLink: () => void; clickCreatePolicyButton: () => void; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index 26ed0a7728d2f..67a978bb08471 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -5,88 +5,23 @@ * 2.0. */ -import React from 'react'; import { act } from 'react-dom/test-utils'; +import { TestBedConfig } from '@kbn/test/jest'; -import { registerTestBed, TestBedConfig } from '@kbn/test/jest'; - -import { licensingMock } from '../../../../licensing/public/mocks'; - -import { EditPolicy } from '../../../public/application/sections/edit_policy'; -import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; - -import { Phases as PolicyPhases } from '../../../common/types'; - -import { KibanaContextProvider } from '../../../public/shared_imports'; import { AppServicesContext } from '../../../public/types'; -import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; - -import { TestSubjects } from '../helpers'; -import { POLICY_NAME } from './constants'; - -type Phases = keyof PolicyPhases; - -window.scrollTo = jest.fn(); -jest.mock('@elastic/eui', () => { - const original = jest.requireActual('@elastic/eui'); - - return { - ...original, - // Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions, - // which does not produce a valid component wrapper - EuiComboBox: (props: any) => ( - { - props.onChange([syntheticEvent['0']]); - }} - /> - ), - EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. - }; -}); - -const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { - return { - memoryRouter: { - initialEntries: [`/policies/edit/${POLICY_NAME}`], - componentRoutePath: `/policies/edit/:policyName`, - }, - defaultProps: { - getUrlForApp: () => {}, - }, - ...testBedConfigArgs, - }; -}; - -const breadcrumbService = createBreadcrumbsMock(); - -const MyComponent = ({ appServicesContext, ...rest }: any) => { - return ( - - - - ); -}; - -const initTestBed = (arg?: { - appServicesContext?: Partial; - testBedConfig?: Partial; -}) => { - const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; - return registerTestBed(MyComponent, getTestBedConfig(testBedConfigArgs))(rest); -}; +import { + Phase, + createEnablePhaseAction, + createNodeAllocationActions, + createFormToggleAction, + createFormSetValueAction, + setReplicas, + savePolicy, +} from '../helpers'; +import { initTestBed } from './init_test_bed'; type SetupReturn = ReturnType; - export type EditPolicyTestBed = SetupReturn extends Promise ? U : SetupReturn; export const setup = async (arg?: { @@ -97,13 +32,6 @@ export const setup = async (arg?: { const { find, component, form, exists } = testBed; - const createFormToggleAction = (dataTestSubject: string) => async (checked: boolean) => { - await act(async () => { - form.toggleEuiSwitch(dataTestSubject, checked); - }); - component.update(); - }; - const createFormCheckboxAction = (dataTestSubject: string) => async (checked: boolean) => { await act(async () => { form.selectCheckBox(dataTestSubject, checked); @@ -111,15 +39,6 @@ export const setup = async (arg?: { component.update(); }; - function createFormSetValueAction(dataTestSubject: string) { - return async (value: V) => { - await act(async () => { - form.setInputValue(dataTestSubject, value); - }); - component.update(); - }; - } - const setWaitForSnapshotPolicy = async (snapshotPolicyName: string) => { act(() => { find('snapshotPolicyCombobox').simulate('change', [{ label: snapshotPolicyName }]); @@ -127,16 +46,9 @@ export const setup = async (arg?: { component.update(); }; - const savePolicy = async () => { - await act(async () => { - find('savePolicyButton').simulate('click'); - }); - component.update(); - }; - - const toggleDefaultRollover = createFormToggleAction('useDefaultRolloverSwitch'); + const toggleDefaultRollover = createFormToggleAction(testBed, 'useDefaultRolloverSwitch'); - const toggleRollover = createFormToggleAction('rolloverSwitch'); + const toggleRollover = createFormToggleAction(testBed, 'rolloverSwitch'); const setMaxPrimaryShardSize = async (value: string, units?: string) => { await act(async () => { @@ -162,7 +74,7 @@ export const setup = async (arg?: { component.update(); }; - const setMaxDocs = createFormSetValueAction('hot-selectedMaxDocuments'); + const setMaxDocs = createFormSetValueAction(testBed, 'hot-selectedMaxDocuments'); const setMaxAge = async (value: string, units?: string) => { await act(async () => { @@ -174,69 +86,64 @@ export const setup = async (arg?: { component.update(); }; - const createForceMergeActions = (phase: Phases) => { + const createForceMergeActions = (phase: Phase) => { const toggleSelector = `${phase}-forceMergeSwitch`; return { forceMergeFieldExists: () => exists(toggleSelector), - toggleForceMerge: createFormToggleAction(toggleSelector), - setForcemergeSegmentsCount: createFormSetValueAction(`${phase}-selectedForceMergeSegments`), + toggleForceMerge: createFormToggleAction(testBed, toggleSelector), + setForcemergeSegmentsCount: createFormSetValueAction( + testBed, + `${phase}-selectedForceMergeSegments` + ), setBestCompression: createFormCheckboxAction(`${phase}-bestCompression`), }; }; - const createIndexPriorityActions = (phase: Phases) => { + const createIndexPriorityActions = (phase: Phase) => { const toggleSelector = `${phase}-indexPrioritySwitch`; return { indexPriorityExists: () => exists(toggleSelector), - toggleIndexPriority: createFormToggleAction(toggleSelector), - setIndexPriority: createFormSetValueAction(`${phase}-indexPriority`), + toggleIndexPriority: createFormToggleAction(testBed, toggleSelector), + setIndexPriority: createFormSetValueAction(testBed, `${phase}-indexPriority`), }; }; - const enable = (phase: Phases) => createFormToggleAction(`enablePhaseSwitch-${phase}`); - - const createMinAgeActions = (phase: Phases) => { + const createMinAgeActions = (phase: Phase) => { return { hasMinAgeInput: () => exists(`${phase}-selectedMinimumAge`), - setMinAgeValue: createFormSetValueAction(`${phase}-selectedMinimumAge`), - setMinAgeUnits: createFormSetValueAction(`${phase}-selectedMinimumAgeUnits`), + setMinAgeValue: createFormSetValueAction(testBed, `${phase}-selectedMinimumAge`), + setMinAgeUnits: createFormSetValueAction(testBed, `${phase}-selectedMinimumAgeUnits`), hasRolloverTipOnMinAge: () => exists(`${phase}-rolloverMinAgeInputIconTip`), }; }; - const setReplicas = (phase: Phases) => async (value: string) => { - if (!exists(`${phase}-selectedReplicaCount`)) { - await createFormToggleAction(`${phase}-setReplicasSwitch`)(true); - } - await createFormSetValueAction(`${phase}-selectedReplicaCount`)(value); - }; - - const createShrinkActions = (phase: Phases) => { + const createShrinkActions = (phase: Phase) => { const toggleSelector = `${phase}-shrinkSwitch`; return { shrinkExists: () => exists(toggleSelector), - toggleShrink: createFormToggleAction(toggleSelector), - setShrink: createFormSetValueAction(`${phase}-primaryShardCount`), + toggleShrink: createFormToggleAction(testBed, toggleSelector), + setShrink: createFormSetValueAction(testBed, `${phase}-primaryShardCount`), }; }; - const createSetFreeze = (phase: Phases) => createFormToggleAction(`${phase}-freezeSwitch`); - const createFreezeExists = (phase: Phases) => () => exists(`${phase}-freezeSwitch`); + const createSetFreeze = (phase: Phase) => + createFormToggleAction(testBed, `${phase}-freezeSwitch`); + const createFreezeExists = (phase: Phase) => () => exists(`${phase}-freezeSwitch`); - const createReadonlyActions = (phase: Phases) => { + const createReadonlyActions = (phase: Phase) => { const toggleSelector = `${phase}-readonlySwitch`; return { readonlyExists: () => exists(toggleSelector), - toggleReadonly: createFormToggleAction(toggleSelector), + toggleReadonly: createFormToggleAction(testBed, toggleSelector), }; }; - const createSearchableSnapshotActions = (phase: Phases) => { + const createSearchableSnapshotActions = (phase: Phase) => { const fieldSelector = `searchableSnapshotField-${phase}`; const licenseCalloutSelector = `${fieldSelector}.searchableSnapshotDisabledDueToLicense`; const toggleSelector = `${fieldSelector}.searchableSnapshotToggle`; - const toggleSearchableSnapshot = createFormToggleAction(toggleSelector); + const toggleSearchableSnapshot = createFormToggleAction(testBed, toggleSelector); return { searchableSnapshotDisabled: () => exists(licenseCalloutSelector) && find(licenseCalloutSelector).props().disabled === true, @@ -269,54 +176,6 @@ export const setup = async (arg?: { const hasRolloverSettingRequiredCallout = (): boolean => exists('rolloverSettingsRequired'); - const createNodeAllocationActions = (phase: Phases) => { - const controlsSelector = `${phase}-dataTierAllocationControls`; - const dataTierSelector = `${controlsSelector}.dataTierSelect`; - const nodeAttrsSelector = `${phase}-selectedNodeAttrs`; - - const openNodeAttributesSection = async () => { - await act(async () => { - find(dataTierSelector).simulate('click'); - }); - component.update(); - }; - - return { - hasDataTierAllocationControls: () => exists(controlsSelector), - openNodeAttributesSection, - hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector), - getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'), - setDataAllocation: async (value: DataTierAllocationType) => { - await openNodeAttributesSection(); - - await act(async () => { - switch (value) { - case 'node_roles': - find(`${controlsSelector}.defaultDataAllocationOption`).simulate('click'); - break; - case 'node_attrs': - find(`${controlsSelector}.customDataAllocationOption`).simulate('click'); - break; - default: - find(`${controlsSelector}.noneDataAllocationOption`).simulate('click'); - } - }); - component.update(); - }, - setSelectedNodeAttribute: createFormSetValueAction(nodeAttrsSelector), - hasNoNodeAttrsWarning: () => exists('noNodeAttributesWarning'), - hasDefaultAllocationWarning: () => exists('defaultAllocationWarning'), - hasDefaultAllocationNotice: () => exists('defaultAllocationNotice'), - hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), - openNodeDetailsFlyout: async () => { - await act(async () => { - find(`${phase}-viewNodeDetailsFlyoutButton`).simulate('click'); - }); - component.update(); - }, - }; - }; - const expectErrorMessages = (expectedMessages: string[]) => { const errorMessages = component.find('.euiFormErrorText'); expect(errorMessages.length).toBe(expectedMessages.length); @@ -346,10 +205,10 @@ export const setup = async (arg?: { ...testBed, runTimers, actions: { - saveAsNewPolicy: createFormToggleAction('saveAsNewSwitch'), - setPolicyName: createFormSetValueAction('policyNameField'), + saveAsNewPolicy: createFormToggleAction(testBed, 'saveAsNewSwitch'), + setPolicyName: createFormSetValueAction(testBed, 'policyNameField'), setWaitForSnapshotPolicy, - savePolicy, + savePolicy: () => savePolicy(testBed), hasGlobalErrorCallout: () => exists('policyFormErrorsCallout'), expectErrorMessages, timeline: { @@ -375,30 +234,30 @@ export const setup = async (arg?: { ...createSearchableSnapshotActions('hot'), }, warm: { - enable: enable('warm'), + enable: createEnablePhaseAction(testBed, 'warm'), ...createMinAgeActions('warm'), - setReplicas: setReplicas('warm'), + setReplicas: (value: string) => setReplicas(testBed, 'warm', value), hasErrorIndicator: () => exists('phaseErrorIndicator-warm'), ...createShrinkActions('warm'), ...createForceMergeActions('warm'), ...createReadonlyActions('warm'), ...createIndexPriorityActions('warm'), - ...createNodeAllocationActions('warm'), + ...createNodeAllocationActions(testBed, 'warm'), }, cold: { - enable: enable('cold'), + enable: createEnablePhaseAction(testBed, 'cold'), ...createMinAgeActions('cold'), - setReplicas: setReplicas('cold'), + setReplicas: (value: string) => setReplicas(testBed, 'cold', value), setFreeze: createSetFreeze('cold'), freezeExists: createFreezeExists('cold'), ...createReadonlyActions('cold'), hasErrorIndicator: () => exists('phaseErrorIndicator-cold'), ...createIndexPriorityActions('cold'), ...createSearchableSnapshotActions('cold'), - ...createNodeAllocationActions('cold'), + ...createNodeAllocationActions(testBed, 'cold'), }, frozen: { - enable: enable('frozen'), + enable: createEnablePhaseAction(testBed, 'frozen'), ...createMinAgeActions('frozen'), hasErrorIndicator: () => exists('phaseErrorIndicator-frozen'), ...createSearchableSnapshotActions('frozen'), diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts deleted file mode 100644 index e289991780c04..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts +++ /dev/null @@ -1,503 +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 { act } from 'react-dom/test-utils'; -import { setupEnvironment } from '../../helpers/setup_environment'; -import { EditPolicyTestBed, setup } from '../edit_policy.helpers'; -import { - POLICY_WITH_MIGRATE_OFF, - POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, - POLICY_WITH_NODE_ROLE_ALLOCATION, -} from '../constants'; - -describe(' node allocation', () => { - let testBed: EditPolicyTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - server.restore(); - }); - - beforeEach(async () => { - server.respondImmediately = true; - httpRequestsMockHelpers.setLoadPolicies([]); - httpRequestsMockHelpers.setListNodes({ - nodesByRoles: { data: ['node1'] }, - nodesByAttributes: { 'attribute:true': ['node1'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - httpRequestsMockHelpers.setNodesDetails('attribute:true', [ - { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, - ]); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - describe('warm phase', () => { - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.warm.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows warning instead of node attributes input when none exist', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeTruthy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.warm.getNodeAttributesSelectOptions().length).toBe(2); - }); - - test('shows view node attributes link when attribute selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); - - expect(actions.warm.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.warm.getNodeAttributesSelectOptions().length).toBe(2); - await actions.warm.setSelectedNodeAttribute('attribute:true'); - - await actions.warm.openNodeDetailsFlyout(); - expect(actions.warm.hasNodeDetailsFlyout()).toBeTruthy(); - }); - - test('shows default allocation warning when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationWarning()).toBeTruthy(); - }); - - test('when configuring warm phase shows default allocation notice when hot tier exists, but not warm tier', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationNotice()).toBeTruthy(); - }); - - test(`doesn't show default allocation notice when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationNotice()).toBeFalsy(); - }); - }); - - describe('cold phase', () => { - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.cold.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows warning instead of node attributes input when none exist', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeTruthy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.cold.getNodeAttributesSelectOptions().length).toBe(2); - }); - - test('shows view node attributes link when attribute selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; - - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); - - expect(actions.cold.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.cold.getNodeAttributesSelectOptions().length).toBe(2); - await actions.cold.setSelectedNodeAttribute('attribute:true'); - - await actions.cold.openNodeDetailsFlyout(); - expect(actions.cold.hasNodeDetailsFlyout()).toBeTruthy(); - }); - - test('shows default allocation warning when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationWarning()).toBeTruthy(); - }); - - [ - { - nodesByRoles: { data_hot: ['test'] }, - previousActiveRole: 'hot', - }, - { - nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - previousActiveRole: 'warm', - }, - ].forEach(({ nodesByRoles, previousActiveRole }) => { - test(`shows default allocation notice when ${previousActiveRole} tiers exists, but not cold tier`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component, find } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationNotice()).toBeTruthy(); - expect(find('defaultAllocationNotice').text()).toContain( - `This policy will move data in the cold phase to ${previousActiveRole} tier nodes` - ); - }); - }); - - test(`doesn't show default allocation notice when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationNotice()).toBeFalsy(); - }); - }); - - describe('not on cloud', () => { - test('shows all allocation options, even if using legacy config', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component, exists } = testBed; - - component.update(); - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - // Assert that default, custom and 'none' options exist - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeTruthy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - }); - }); - - describe('on cloud', () => { - describe('using legacy data role config', () => { - test('should hide data tier option on cloud', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - // On cloud, if using legacy config there will not be any "data_*" roles set. - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - const { actions, component, exists, find } = testBed; - - component.update(); - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - // Assert that custom and 'none' options exist - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeFalsy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - // Show the call-to-action for users to migrate their cluster to use node roles - expect(find('cloudDataTierCallout').exists()).toBeTruthy(); - }); - }); - - describe('using node role config', () => { - test('shows recommended, custom and "off" options on cloud with data roles', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - testBed.component.update(); - - const { actions, component, exists, find } = testBed; - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeTruthy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - // Do not show the call-to-action for users to migrate their cluster to use node roles - expect(find('cloudDataTierCallout').exists()).toBeFalsy(); - }); - test('do not show node allocation specific warnings on cloud', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - // No nodes with node roles like "data_hot" or "data_warm" - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - testBed.component.update(); - - const { actions, component, exists } = testBed; - await actions.warm.enable(true); - await actions.cold.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - expect(exists('cloudDataTierCallout')).toBeFalsy(); - expect(exists('defaultAllocationNotice')).toBeFalsy(); - expect(exists('defaultAllocationWarning')).toBeFalsy(); - }); - }); - }); - - describe('data allocation', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_MIGRATE_OFF]); - httpRequestsMockHelpers.setListNodes({ - nodesByRoles: {}, - nodesByAttributes: { test: ['123'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - httpRequestsMockHelpers.setLoadSnapshotPolicies([]); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('setting node_attr based allocation, but not selecting node attribute', async () => { - const { actions } = testBed; - await actions.warm.setDataAllocation('node_attrs'); - await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; - - expect(warmPhase.actions.migrate).toEqual({ enabled: false }); - }); - - describe('node roles', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ROLE_ALLOCATION]); - httpRequestsMockHelpers.setListNodes({ - isUsingDeprecatedDataRoleConfig: false, - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['123'] }, - }); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('detecting use of the recommended allocation type', () => { - const { find } = testBed; - const selectedDataAllocation = find( - 'warm-dataTierAllocationControls.dataTierSelect' - ).text(); - expect(selectedDataAllocation).toBe('Use warm nodes (recommended)'); - }); - - test('setting replicas serialization', async () => { - const { actions } = testBed; - await actions.warm.setReplicas('123'); - await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhaseActions = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm - .actions; - expect(warmPhaseActions).toMatchInlineSnapshot(` - Object { - "allocate": Object { - "number_of_replicas": 123, - }, - } - `); - }); - }); - - describe('node attr and none', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION]); - httpRequestsMockHelpers.setListNodes({ - isUsingDeprecatedDataRoleConfig: false, - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['123'] }, - }); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('detecting use of the custom allocation type', () => { - const { find } = testBed; - expect(find('warm-dataTierAllocationControls.dataTierSelect').text()).toBe('Custom'); - }); - - test('detecting use of the "off" allocation type', () => { - const { find } = testBed; - expect(find('cold-dataTierAllocationControls.dataTierSelect').text()).toContain('Off'); - }); - }); - }); -}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts new file mode 100644 index 0000000000000..ba376f9cddd93 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts @@ -0,0 +1,37 @@ +/* + * 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 { TestBedConfig } from '@kbn/test/jest'; + +import { AppServicesContext } from '../../../../../public/types'; +import { createEnablePhaseAction, createNodeAllocationActions } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type CloudNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupCloudNodeAllocation = async (arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; +}) => { + const testBed = await initTestBed(arg); + + return { + ...testBed, + actions: { + warm: { + enable: createEnablePhaseAction(testBed, 'warm'), + ...createNodeAllocationActions(testBed, 'warm'), + }, + cold: { + enable: createEnablePhaseAction(testBed, 'cold'), + ...createNodeAllocationActions(testBed, 'cold'), + }, + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts new file mode 100644 index 0000000000000..06b667f666808 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts @@ -0,0 +1,154 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { + CloudNodeAllocationTestBed, + setupCloudNodeAllocation, +} from './cloud_aware_behavior.helpers'; + +describe(' node allocation cloud-aware behavior', () => { + let testBed: CloudNodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async (isOnCloud?: boolean) => { + await act(async () => { + if (Boolean(isOnCloud)) { + testBed = await setupCloudNodeAllocation({ + appServicesContext: { cloud: { isCloudEnabled: true } }, + }); + } else { + testBed = await setupCloudNodeAllocation(); + } + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + describe('when not on cloud', () => { + test('shows all allocation options, even if using legacy config', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + + await setup(); + const { actions, component, exists } = testBed; + + component.update(); + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that default, custom and 'none' options exist + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeTruthy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + }); + }); + + describe('when on cloud', () => { + describe('using legacy data role config', () => { + test('should hide data tier option on cloud', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + // On cloud, if using legacy config there will not be any "data_*" roles set. + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + await setup(true); + const { actions, component, exists, find } = testBed; + + component.update(); + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that custom and 'none' options exist + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeFalsy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + // Show the call-to-action for users to migrate their cluster to use node roles + expect(find('cloudDataTierCallout').exists()).toBeTruthy(); + }); + }); + + describe('using node role config', () => { + test('shows recommended, custom and "off" options on cloud with data roles', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(true); + testBed.component.update(); + + const { actions, component, exists, find } = testBed; + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeTruthy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + // Do not show the call-to-action for users to migrate their cluster to use node roles + expect(find('cloudDataTierCallout').exists()).toBeFalsy(); + }); + + test('do not show node allocation specific warnings on cloud', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + // No nodes with node roles like "data_hot" or "data_warm" + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(true); + testBed.component.update(); + + const { actions, component, exists } = testBed; + await actions.warm.enable(true); + await actions.cold.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + expect(exists('cloudDataTierCallout')).toBeFalsy(); + expect( + actions.warm.hasWillUseFallbackTierNotice() || actions.cold.hasWillUseFallbackTierNotice() + ).toBeFalsy(); + expect( + actions.warm.hasNoTiersAvailableNotice() || actions.cold.hasNoTiersAvailableNotice() + ).toBeFalsy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts new file mode 100644 index 0000000000000..52593e67df768 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts @@ -0,0 +1,25 @@ +/* + * 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 { createEnablePhaseAction, createNodeAllocationActions } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupColdPhaseNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + enable: createEnablePhaseAction(testBed, 'cold'), + ...createNodeAllocationActions(testBed, 'cold'), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts new file mode 100644 index 0000000000000..f833c8e316117 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts @@ -0,0 +1,226 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { NodeAllocationTestBed, setupColdPhaseNodeAllocation } from './cold_phase.helpers'; + +describe(' node allocation in the cold phase', () => { + let testBed: NodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupColdPhaseNodeAllocation(); + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test(`doesn't offer allocation guidance when node with deprecated "data" role exists`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeFalsy(); + }); + + describe('when using node attributes', () => { + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; + + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + + // No notices will be shown. + expect(actions.hasDefaultToDataTiersNotice()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); + expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); + }); + + describe('and some are defined', () => { + test('shows the node attributes input', async () => { + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); + + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions } = testBed; + + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); + + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + }); + + describe('and none are defined', () => { + const commonSetupAndBaselineAssertions = async () => { + await setup(); + const { actions, component } = testBed; + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }; + + test('and data tiers are available, shows DefaultToDataTiersNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + }); + + test('and data tiers are available, but not for the cold phase, shows WillUseFallbackTierUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_warm: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeTruthy(); + }); + + test('when data nodes are in use, shows DefaultToDataNodesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + }); + + test('when no data tier node roles are defined, shows NoTiersAvailableUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + // data_content is a node role so they're technically in use, but it's not a data tier node role. + nodesByRoles: { data_content: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeTruthy(); + }); + }); + }); + + describe('when using node roles', () => { + test('when no node roles are defined, shows NoTiersAvailableNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); + }); + + [ + { + nodesByRoles: { data_hot: ['test'] }, + fallbackTier: 'hot', + }, + { + nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + fallbackTier: 'warm', + }, + ].forEach(({ nodesByRoles, fallbackTier }) => { + test(`when allocation will fallback to the ${fallbackTier} tier, shows WillUseFallbackTierNotice and defines the fallback tier`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.getWillUseFallbackTierNoticeText()).toContain( + `No nodes assigned to the cold tierIf no cold nodes are available, data is stored in the ${fallbackTier} tier.` + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts new file mode 100644 index 0000000000000..8dd5a75ac0d22 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts @@ -0,0 +1,26 @@ +/* + * 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 { createNodeAllocationActions, savePolicy, setReplicas } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type GeneralNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupGeneralNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + ...createNodeAllocationActions(testBed, 'warm'), + savePolicy: () => savePolicy(testBed), + setReplicas: (value: string) => setReplicas(testBed, 'warm', value), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts new file mode 100644 index 0000000000000..d45e1cfe9567b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts @@ -0,0 +1,128 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { + GeneralNodeAllocationTestBed, + setupGeneralNodeAllocation, +} from './general_behavior.helpers'; +import { + POLICY_WITH_MIGRATE_OFF, + POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, + POLICY_WITH_NODE_ROLE_ALLOCATION, +} from '../../constants'; + +describe(' node allocation general behavior', () => { + let testBed: GeneralNodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupGeneralNodeAllocation(); + }); + }; + + describe('data allocation', () => { + test('setting node_attr based allocation, but not selecting node attribute', async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_MIGRATE_OFF]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: {}, + nodesByAttributes: { test: ['123'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + httpRequestsMockHelpers.setLoadSnapshotPolicies([]); + + await setup(); + + const { component, actions } = testBed; + component.update(); + + await actions.setDataAllocation('node_attrs'); + await actions.savePolicy(); + const latestRequest = server.requests[server.requests.length - 1]; + const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; + + expect(warmPhase.actions.migrate).toEqual({ enabled: false }); + }); + + describe('node roles', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ROLE_ALLOCATION]); + httpRequestsMockHelpers.setListNodes({ + isUsingDeprecatedDataRoleConfig: false, + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['123'] }, + }); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('detecting use of the recommended allocation type', () => { + const { find } = testBed; + const selectedDataAllocation = find( + 'warm-dataTierAllocationControls.dataTierSelect' + ).text(); + expect(selectedDataAllocation).toBe('Use warm nodes (recommended)'); + }); + + test('setting replicas serialization', async () => { + const { actions } = testBed; + await actions.setReplicas('123'); + await actions.savePolicy(); + const latestRequest = server.requests[server.requests.length - 1]; + const warmPhaseActions = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm + .actions; + expect(warmPhaseActions).toMatchInlineSnapshot(` + Object { + "allocate": Object { + "number_of_replicas": 123, + }, + } + `); + }); + }); + + describe('node attr and none', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION]); + httpRequestsMockHelpers.setListNodes({ + isUsingDeprecatedDataRoleConfig: false, + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['123'] }, + }); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('detecting use of the custom allocation type', () => { + const { find } = testBed; + expect(find('warm-dataTierAllocationControls.dataTierSelect').text()).toBe('Custom'); + }); + + test('detecting use of the "off" allocation type', () => { + const { find } = testBed; + expect(find('cold-dataTierAllocationControls.dataTierSelect').text()).toContain('Off'); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts new file mode 100644 index 0000000000000..e39629da79cd8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts @@ -0,0 +1,25 @@ +/* + * 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 { createNodeAllocationActions, createEnablePhaseAction } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupWarmPhaseNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + enable: createEnablePhaseAction(testBed, 'warm'), + ...createNodeAllocationActions(testBed, 'warm'), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts new file mode 100644 index 0000000000000..0ab18c6e05736 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts @@ -0,0 +1,214 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { NodeAllocationTestBed, setupWarmPhaseNodeAllocation } from './warm_phase.helpers'; + +describe(' node allocation in the warm phase', () => { + let testBed: NodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupWarmPhaseNodeAllocation(); + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test(`doesn't offer allocation guidance when node with deprecated "data" role exists`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + }); + + describe('when using node attributes', () => { + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; + + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + + // No notices will be shown. + expect(actions.hasDefaultToDataTiersNotice()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); + expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); + }); + + describe('and some are defined', () => { + test('shows the node attributes input', async () => { + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); + + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); + + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + }); + + describe('and none are defined', () => { + const commonSetupAndBaselineAssertions = async () => { + await setup(); + const { actions, component } = testBed; + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }; + + test('and data tiers are available, shows DefaultToDataTiersNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + }); + + test('and data tiers are available, but not for the warm phase, shows WillUseFallbackTierUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeTruthy(); + }); + + test('when data nodes are in use, shows DefaultToDataNodesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + }); + + test('when no data tier node roles are defined, shows NoTiersAvailableUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + // data_content is a node role so they're technically in use, but it's not a data tier node role. + nodesByRoles: { data_content: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeTruthy(); + }); + }); + }); + + describe('when using node roles', () => { + test('when no node roles are defined, shows NoTiersAvailableNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); + }); + + test('when allocation will fallback to the hot tier, shows WillUseFallbackTierNotice and defines the fallback tier', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.getWillUseFallbackTierNoticeText()).toContain( + `No nodes assigned to the warm tierIf no warm nodes are available, data is stored in the hot tier.` + ); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx new file mode 100644 index 0000000000000..4f057e04c85d4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx @@ -0,0 +1,55 @@ +/* + * 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 React from 'react'; +import { registerTestBed, TestBedConfig } from '@kbn/test/jest'; + +import '../helpers/global_mocks'; + +import { licensingMock } from '../../../../licensing/public/mocks'; +import { EditPolicy } from '../../../public/application/sections/edit_policy'; +import { KibanaContextProvider } from '../../../public/shared_imports'; +import { AppServicesContext } from '../../../public/types'; +import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; +import { POLICY_NAME } from './constants'; + +const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { + return { + memoryRouter: { + initialEntries: [`/policies/edit/${POLICY_NAME}`], + componentRoutePath: `/policies/edit/:policyName`, + }, + defaultProps: { + getUrlForApp: () => {}, + }, + ...testBedConfigArgs, + }; +}; + +const breadcrumbService = createBreadcrumbsMock(); + +const EditPolicyContainer = ({ appServicesContext, ...rest }: any) => { + return ( + + + + ); +}; + +export const initTestBed = (arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; +}) => { + const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; + return registerTestBed(EditPolicyContainer, getTestBedConfig(testBedConfigArgs))(rest); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts new file mode 100644 index 0000000000000..e0988daf52192 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts @@ -0,0 +1,15 @@ +/* + * 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 { TestBed } from '@kbn/test/jest'; + +import { Phase } from './types'; +import { createFormToggleAction } from './create_form_toggle_action'; + +export const createEnablePhaseAction = (testBed: TestBed, phase: Phase) => { + return createFormToggleAction(testBed, `enablePhaseSwitch-${phase}`); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts new file mode 100644 index 0000000000000..95e90ac04192b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export function createFormSetValueAction( + testBed: TestBed, + dataTestSubject: string +) { + const { form, component } = testBed; + + return async (value: V) => { + await act(async () => { + form.setInputValue(dataTestSubject, value); + }); + component.update(); + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts new file mode 100644 index 0000000000000..579bb661871da --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts @@ -0,0 +1,21 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export const createFormToggleAction = (testBed: TestBed, dataTestSubject: string) => async ( + checked: boolean +) => { + const { form, component } = testBed; + + await act(async () => { + form.toggleEuiSwitch(dataTestSubject, checked); + }); + + component.update(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts new file mode 100644 index 0000000000000..416a2afa54d54 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts @@ -0,0 +1,83 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +import { Phase } from './types'; +import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; +import { createFormSetValueAction } from './create_form_set_value_action'; + +export const createNodeAllocationActions = (testBed: TestBed, phase: Phase) => { + const { component, find, exists } = testBed; + + const controlsSelector = `${phase}-dataTierAllocationControls`; + const dataTierSelector = `${controlsSelector}.dataTierSelect`; + const nodeAttrsSelector = `${phase}-selectedNodeAttrs`; + + const openNodeAttributesSection = async () => { + await act(async () => { + find(dataTierSelector).simulate('click'); + }); + component.update(); + }; + + const isAllocationLoading = () => find(`${phase}-phase.allocationLoadingSpinner`).exists(); + const hasDefaultToDataNodesNotice = () => exists(`${phase}-phase.defaultToDataNodesNotice`); + const hasDefaultToDataTiersNotice = () => exists(`${phase}-phase.defaultToDataTiersNotice`); + const hasDefaultAllocationBehaviorNotice = () => + hasDefaultToDataNodesNotice() && hasDefaultToDataTiersNotice(); + const hasNoTiersAvailableNotice = () => exists(`${phase}-phase.noTiersAvailableNotice`); + const hasNoTiersAvailableUsingNodeAttributesNotice = () => + exists(`${phase}-phase.noTiersAvailableUsingNodeAttributesNotice`); + const hasWillUseFallbackTierNotice = () => exists(`${phase}-phase.willUseFallbackTierNotice`); + const hasWillUseFallbackTierUsingNodeAttributesNotice = () => + exists(`${phase}-phase.willUseFallbackTierUsingNodeAttributesNotice`); + const getWillUseFallbackTierNoticeText = () => + find(`${phase}-phase.willUseFallbackTierNotice`).text(); + + return { + hasDataTierAllocationControls: () => exists(controlsSelector), + openNodeAttributesSection, + hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector), + getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'), + setDataAllocation: async (value: DataTierAllocationType) => { + await openNodeAttributesSection(); + + await act(async () => { + switch (value) { + case 'node_roles': + find(`${controlsSelector}.defaultDataAllocationOption`).simulate('click'); + break; + case 'node_attrs': + find(`${controlsSelector}.customDataAllocationOption`).simulate('click'); + break; + default: + find(`${controlsSelector}.noneDataAllocationOption`).simulate('click'); + } + }); + component.update(); + }, + setSelectedNodeAttribute: createFormSetValueAction(testBed, nodeAttrsSelector), + isAllocationLoading, + hasDefaultToDataNodesNotice, + hasDefaultToDataTiersNotice, + hasDefaultAllocationBehaviorNotice, + hasNoTiersAvailableNotice, + hasNoTiersAvailableUsingNodeAttributesNotice, + hasWillUseFallbackTierNotice, + hasWillUseFallbackTierUsingNodeAttributesNotice, + getWillUseFallbackTierNoticeText, + hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), + openNodeDetailsFlyout: async () => { + await act(async () => { + find(`${phase}-viewNodeDetailsFlyoutButton`).simulate('click'); + }); + component.update(); + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx new file mode 100644 index 0000000000000..5ed5e6e8fcce7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; + +// NOTE: Import this file for its side-effects. You must import it before the code that it mocks +// is imported. Typically this means just importing above your other imports. +// See https://jestjs.io/docs/manual-mocks for more info. + +window.scrollTo = jest.fn(); + +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + + return { + ...original, + // Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions, + // which does not produce a valid component wrapper + EuiComboBox: (props: any) => ( + { + props.onChange([syntheticEvent['0']]); + }} + /> + ), + EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. + }; +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index 56f5815633a1d..1388cf97d4e22 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -5,22 +5,11 @@ * 2.0. */ -export type TestSubjects = - | 'snapshotPolicyCombobox' - | 'savePolicyButton' - | 'customPolicyCallout' - | 'noPoliciesCallout' - | 'policiesErrorCallout' - | 'rolloverSwitch' - | 'rolloverSettingsRequired' - | 'hot-selectedMaxSizeStored' - | 'hot-selectedMaxSizeStoredUnits' - | 'hot-selectedMaxDocuments' - | 'hot-selectedMaxAge' - | 'hot-selectedMaxAgeUnits' - | 'policyTablePolicyNameLink' - | 'policyTitle' - | 'createPolicyButton' - | 'cold-freezeSwitch' - | 'frozen-freezeSwitch' - | string; +export { Phase } from './types'; + +export { createNodeAllocationActions } from './create_node_allocation_actions'; +export { createEnablePhaseAction } from './create_enable_phase_action'; +export { setReplicas } from './set_replicas_action'; +export { savePolicy } from './save_policy_action'; +export { createFormToggleAction } from './create_form_toggle_action'; +export { createFormSetValueAction } from './create_form_set_value_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts new file mode 100644 index 0000000000000..37ce4f84f03ba --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export const savePolicy = async (testBed: TestBed) => { + const { find, component } = testBed; + + await act(async () => { + find('savePolicyButton').simulate('click'); + }); + + component.update(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts new file mode 100644 index 0000000000000..049c1a205cbd4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts @@ -0,0 +1,21 @@ +/* + * 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 { TestBed } from '@kbn/test/jest'; + +import { Phase } from './types'; +import { createFormToggleAction } from './create_form_toggle_action'; +import { createFormSetValueAction } from './create_form_set_value_action'; + +export const setReplicas = async (testBed: TestBed, phase: Phase, value: string) => { + const { exists } = testBed; + + if (!exists(`${phase}-selectedReplicaCount`)) { + await createFormToggleAction(testBed, `${phase}-setReplicasSwitch`)(true); + } + await createFormSetValueAction(testBed, `${phase}-selectedReplicaCount`)(value); +}; diff --git a/x-pack/plugins/apm/server/lib/alerts/create_apm_lifecycle_rule_type.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts similarity index 53% rename from x-pack/plugins/apm/server/lib/alerts/create_apm_lifecycle_rule_type.ts rename to x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts index 8d250a5765cce..644ada96a9f05 100644 --- a/x-pack/plugins/apm/server/lib/alerts/create_apm_lifecycle_rule_type.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; -import { APMRuleRegistry } from '../../plugin'; +import { Phases } from '../../../common/types'; -export const createAPMLifecycleRuleType = createLifecycleRuleTypeFactory(); +export type Phase = keyof Phases; diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index 337a1be2b2fbc..385751f31d5bc 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -7,7 +7,7 @@ import { Index as IndexInterface } from '../../../index_management/common/types'; -export type PhaseWithAllocation = 'warm' | 'cold' | 'frozen'; +export type PhaseWithAllocation = 'warm' | 'cold'; export interface SerializedPolicy { name: string; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts index f2596cac8b920..d5ba7fb785069 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts @@ -13,8 +13,6 @@ import { import { phaseToNodePreferenceMap } from '../../../../common/constants'; -export type AllocationNodeRole = DataTierRole | 'none'; - /** * Given a phase and current cluster node roles, determine which nodes the phase * will allocate data to. For instance, for the warm phase, with warm @@ -26,7 +24,7 @@ export type AllocationNodeRole = DataTierRole | 'none'; export const getAvailableNodeRoleForPhase = ( phase: PhaseWithAllocation, nodesByRoles: ListNodesRouteResponse['nodesByRoles'] -): AllocationNodeRole => { +): DataTierRole | undefined => { const preferredNodeRoles = phaseToNodePreferenceMap[phase]; // The 'data' role covers all node roles, so if we have at least one node with the data role @@ -35,5 +33,5 @@ export const getAvailableNodeRoleForPhase = ( return preferredNodeRoles[0]; } - return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)) ?? 'none'; + return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx index 254063ac1a9ea..1eeb32d84f677 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx @@ -161,7 +161,20 @@ const getSelectOptions = (phase: PhaseWithAllocation, disableDataTierOption: boo ].filter(Boolean) as SelectOptions[]; export const DataTierAllocation: FunctionComponent = (props) => { - const { phase, hasNodeAttributes, disableDataTierOption, isLoading } = props; + const { + phase, + hasNodeAttributes, + isCloudEnabled, + isUsingDeprecatedDataRoleConfig, + isLoading, + } = props; + + /** + * On Cloud we want to disable the data tier allocation option when we detect that we are not + * using node roles in our Node config yet. See {@link ListNodesRouteResponse} for information about how this is + * detected. + */ + const disableDataTierOption = Boolean(isCloudEnabled && isUsingDeprecatedDataRoleConfig); const dataTierAllocationTypePath = `_meta.${phase}.dataTierAllocationType`; @@ -185,6 +198,7 @@ export const DataTierAllocation: FunctionComponent = (props) => { if (disableDataTierOption && field.value === 'node_roles') { field.setValue('node_attrs'); } + return ( = { - data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { - defaultMessage: 'hot', - }), - data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { - defaultMessage: 'warm', - }), - data_cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel', { - defaultMessage: 'cold', - }), - data_frozen: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierFrozenLabel', { - defaultMessage: 'frozen', - }), -}; - -const i18nTexts = { - notice: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm', { - defaultMessage: - 'This policy will move data in the warm phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - }), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold', { - defaultMessage: - 'This policy will move data in the cold phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - }), - }, - frozen: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen.title', - { defaultMessage: 'No nodes assigned to the frozen tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen', - { - defaultMessage: - 'This policy will move data in the frozen phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - } - ), - }, - }, - warning: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - }, -}; - -interface Props { - phase: PhaseWithAllocation; - targetNodeRole: DataTierRole; -} - -export const DefaultAllocationNotice: FunctionComponent = ({ phase, targetNodeRole }) => { - return ( - - {i18nTexts.notice[phase].body(targetNodeRole)} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx deleted file mode 100644 index 649eb9f2fcb7f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx +++ /dev/null @@ -1,72 +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 { i18n } from '@kbn/i18n'; -import React, { FunctionComponent } from 'react'; -import { EuiCallOut } from '@elastic/eui'; - -import { PhaseWithAllocation } from '../../../../../../../../../common/types'; - -const i18nTexts = { - warning: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - frozen: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the frozen tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the frozen, cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - }, -}; - -interface Props { - phase: PhaseWithAllocation; -} - -export const DefaultAllocationWarning: FunctionComponent = ({ phase }) => { - return ( - - {i18nTexts.warning[phase].body} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx new file mode 100644 index 0000000000000..b29ddb75c18cf --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx @@ -0,0 +1,38 @@ +/* + * 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 React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; + +export const DefaultToDataNodesNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ + phase, +}) => { + return ( + +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultToDataNodesDescription', + { defaultMessage: 'Data will be allocated to any available data node.' } + )} +

+ + {nodeAllocationMigrationGuidance} +
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx new file mode 100644 index 0000000000000..25414406e67b5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; + +const i18nTexts = { + body: { + warm: ( + <> +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableDescription', + { defaultMessage: 'Data will be allocated to the warm tier.' } + )} +

+ + {nodeAllocationMigrationGuidance} + + ), + cold: ( + <> +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableDescription', + { defaultMessage: 'Data will be allocated to the cold tier.' } + )} +

+ + {nodeAllocationMigrationGuidance} + + ), + }, +}; + +export const DefaultToDataTiersNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ + phase, +}) => { + return ( + + {i18nTexts.body[phase]} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts index e9c884a42fa93..ce5685ba98307 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts @@ -11,11 +11,17 @@ export { NodeAttrsDetails } from './node_attrs_details'; export { DataTierAllocation } from './data_tier_allocation'; -export { DefaultAllocationNotice } from './default_allocation_notice'; +export { WillUseFallbackTierNotice } from './will_use_fallback_tier_notice'; -export { DefaultAllocationWarning } from './default_allocation_warning'; +export { WillUseFallbackTierUsingNodeAttributesNotice } from './will_use_fallback_tier_using_node_attributes_notice'; -export { NoNodeAttributesWarning } from './no_node_attributes_warning'; +export { NoTiersAvailableNotice } from './no_tiers_available_notice'; + +export { NoTiersAvailableUsingNodeAttributesNotice } from './no_tiers_available_using_node_attributes_notice'; + +export { DefaultToDataTiersNotice } from './default_to_data_tiers_notice'; + +export { DefaultToDataNodesNotice } from './default_to_data_nodes_notice'; export { CloudDataTierCallout } from './cloud_data_tier_callout'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx new file mode 100644 index 0000000000000..4d948ac202eb8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx @@ -0,0 +1,37 @@ +/* + * 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 React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { getNodeAllocationMigrationLink } from '../../../../../../../services/documentation'; + +export const noCustomAttributesTitle = i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.noCustomAttributesTitle', + { defaultMessage: 'No custom attributes defined' } +); + +export const nodeAllocationMigrationGuidance = ( + + {i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesDescription.migrationGuidanceMessage', + { + defaultMessage: 'use role-based allocation', + } + )} +
+ ), + }} + /> +); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx deleted file mode 100644 index e6cadf7049962..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx +++ /dev/null @@ -1,60 +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 React, { FunctionComponent } from 'react'; -import { EuiCallOut } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { PhaseWithAllocation } from '../../../../../../../../../common/types'; - -const i18nTexts = { - title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel', { - defaultMessage: 'No custom node attributes configured', - }), - warm: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, - cold: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, - frozen: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.frozen.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, -}; - -export const NoNodeAttributesWarning: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ - phase, -}) => { - return ( - - {i18nTexts[phase].body} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx new file mode 100644 index 0000000000000..d4e8e98e9b2be --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx @@ -0,0 +1,49 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; + +const i18nTexts = { + warm: { + title: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.noTiersAvailableTitle', { + defaultMessage: 'No nodes assigned to the warm tier', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.noTiersAvailableBody', { + defaultMessage: + 'To use role-based allocation, assign one or more nodes to the warm or hot tiers. Allocation will fail if there are no available nodes.', + }), + }, + cold: { + title: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.noTiersAvailableTitle', { + defaultMessage: 'No nodes assigned to the cold tier', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.noTiersAvailableBody', { + defaultMessage: + 'To use role-based allocation, assign one or more nodes to the cold, warm, or hot tiers. Allocation will fail if there are no available nodes.', + }), + }, +}; + +interface Props { + phase: PhaseWithAllocation; +} + +export const NoTiersAvailableNotice: FunctionComponent = ({ phase }) => { + return ( + + {i18nTexts[phase].body} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx new file mode 100644 index 0000000000000..02dbd1baff25f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx @@ -0,0 +1,36 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; + +export const NoTiersAvailableUsingNodeAttributesNotice: FunctionComponent = () => { + return ( + +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.dataTier.noTiersAvailableUsingNodeAttributesDescription', + { + defaultMessage: 'Unable to allocate data: no available data nodes.', + } + )} +

+ +

{nodeAllocationMigrationGuidance}

+
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx index f8f77ed909a91..e7b1f7420d132 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx @@ -34,13 +34,19 @@ const learnMoreLink = ( ); const i18nTexts = { - doNotModifyAllocationOption: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption', - { defaultMessage: 'Do not modify allocation configuration' } + allocateToDataNodesOption: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.allocateToDataNodesOption', + { defaultMessage: 'Any data node' } ), }; -export const NodeAllocation: FunctionComponent = ({ phase, nodes, isLoading }) => { +export const NodeAllocation: FunctionComponent = ({ + phase, + nodes, + isLoading, + isCloudEnabled, + isUsingDeprecatedDataRoleConfig, +}) => { const allocationNodeAttributePath = `_meta.${phase}.allocationNodeAttribute`; const [formData] = useFormData({ @@ -60,6 +66,20 @@ export const NodeAllocation: FunctionComponent = ({ phase, nodes, i nodeOptions.sort((a, b) => a.value.localeCompare(b.value)); + let nodeAllocationOptions = []; + + // On Cloud, allocating to data tiers and allocating to data nodes is mutually exclusive. So we + // only let users select this option if they're using data nodes. Otherwise we remove it. + // + // On prem, users should have the freedom to choose this option, even if they're using node roles. + // So we always give them this option. + if (!isCloudEnabled || isUsingDeprecatedDataRoleConfig) { + const allocateToDataNodesOption = { text: i18nTexts.allocateToDataNodesOption, value: '' }; + nodeAllocationOptions.push(allocateToDataNodesOption); + } + + nodeAllocationOptions = nodeAllocationOptions.concat(nodeOptions); + return ( <> @@ -97,9 +117,7 @@ export const NodeAllocation: FunctionComponent = ({ phase, nodes, i ) : undefined, euiFieldProps: { 'data-test-subj': `${phase}-selectedNodeAttrs`, - options: [{ text: i18nTexts.doNotModifyAllocationOption, value: '' }].concat( - nodeOptions - ), + options: nodeAllocationOptions, hasNoInitialSelection: false, isLoading, }, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts new file mode 100644 index 0000000000000..ad17855f307c7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +import { DataTierRole } from '../../../../../../../../../common/types'; + +export const nodeRoleToFallbackTierMap: Partial> = { + data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { + defaultMessage: 'hot', + }), + data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { + defaultMessage: 'warm', + }), +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts index b78ab720618ef..aba044e1862ef 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts @@ -14,12 +14,8 @@ export interface SharedProps { phase: PhaseWithAllocation; nodes: ListNodesRouteResponse['nodesByAttributes']; hasNodeAttributes: boolean; - /** - * When on Cloud we want to disable the data tier allocation option when we detect that we are not - * using node roles in our Node config yet. See {@link ListNodesRouteResponse} for information about how this is - * detected. - */ - disableDataTierOption: boolean; + isCloudEnabled: boolean; + isUsingDeprecatedDataRoleConfig: boolean; /** * A flag to indicate whether input fields should be showing a loading spinner */ diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx new file mode 100644 index 0000000000000..90ac6e02ef9e2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx @@ -0,0 +1,49 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../../common/types'; +import { nodeRoleToFallbackTierMap } from './node_role_to_fallback_tier_map'; + +const i18nTexts = { + warm: { + title: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.willUseFallbackTierTitle', { + defaultMessage: 'No nodes assigned to the warm tier', + }), + body: (nodeRole: DataTierRole) => + i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.willUseFallbackTierDescription', { + defaultMessage: 'If no warm nodes are available, data is stored in the {tier} tier.', + values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, + }), + }, + cold: { + title: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.willUseFallbackTierTitle', { + defaultMessage: 'No nodes assigned to the cold tier', + }), + body: (nodeRole: DataTierRole) => + i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.willUseFallbackTierDescription', { + defaultMessage: 'If no cold nodes are available, data is stored in the {tier} tier.', + values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, + }), + }, +}; + +interface Props { + phase: PhaseWithAllocation; + targetNodeRole: DataTierRole; +} + +export const WillUseFallbackTierNotice: FunctionComponent = ({ phase, targetNodeRole }) => { + return ( + + {i18nTexts[phase].body(targetNodeRole)} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx new file mode 100644 index 0000000000000..199f1001f4276 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx @@ -0,0 +1,47 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../../common/types'; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; +import { nodeRoleToFallbackTierMap } from './node_role_to_fallback_tier_map'; + +interface Props { + phase: PhaseWithAllocation; + targetNodeRole: DataTierRole; +} + +export const WillUseFallbackTierUsingNodeAttributesNotice: FunctionComponent = ({ + phase, + targetNodeRole, +}) => { + return ( + +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.dataTier.willUseFallbackTierUsingNodeAttributesDescription', + { + defaultMessage: + 'No {phase} nodes available. Data will be allocated to the {fallbackTier} tier.', + values: { phase, fallbackTier: nodeRoleToFallbackTierMap[targetNodeRole] }, + } + )} +

+ +

{nodeAllocationMigrationGuidance}

+
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 8c90a738d2c09..c45e172868938 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -11,20 +11,19 @@ import { i18n } from '@kbn/i18n'; import { EuiDescribedFormGroup, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; import { useKibana, useFormData } from '../../../../../../../shared_imports'; - -import { PhaseWithAllocation } from '../../../../../../../../common/types'; - +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../common/types'; import { getAvailableNodeRoleForPhase, isNodeRoleFirstPreference } from '../../../../../../lib'; - import { useLoadNodes } from '../../../../../../services/api'; - import { DataTierAllocationType } from '../../../../types'; import { DataTierAllocation, - DefaultAllocationNotice, - DefaultAllocationWarning, - NoNodeAttributesWarning, + WillUseFallbackTierNotice, + WillUseFallbackTierUsingNodeAttributesNotice, + NoTiersAvailableNotice, + NoTiersAvailableUsingNodeAttributesNotice, + DefaultToDataNodesNotice, + DefaultToDataTiersNotice, CloudDataTierCallout, LoadingError, } from './components'; @@ -58,31 +57,37 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr const { nodesByRoles, nodesByAttributes, isUsingDeprecatedDataRoleConfig } = data!; - const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); + const hasNodeAttributes = Boolean(Object.keys(nodesByAttributes ?? {}).length); const isCloudEnabled = cloud?.isCloudEnabled ?? false; const cloudDeploymentUrl = cloud?.deploymentUrl; + const allocationNodeRoleForPhase = getAvailableNodeRoleForPhase(phase, nodesByRoles); + const noTiersAvailable = allocationNodeRoleForPhase === undefined; + const willUseFallbackTier = + allocationNodeRoleForPhase !== undefined && + !isNodeRoleFirstPreference(phase, allocationNodeRoleForPhase); + const renderNotice = () => { switch (allocationType) { case 'node_roles': /** - * On cloud most users should be using autoscaling which will provision tiers as they are needed. We do not surface any + * On Cloud most users should be using autoscaling which will provision tiers as they are needed. We do not surface any * of the notices below. */ if (isCloudEnabled) { return null; } + /** * Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting * stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even * this fallback will not succeed. */ - const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); - if (allocationNodeRole === 'none') { + if (noTiersAvailable) { return ( <> - + ); } @@ -91,26 +96,79 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr * If we are able to fallback to a data tier that does not map to this phase, we show a notice informing the user that their * data will not be assigned to a corresponding tier. */ - if (!isNodeRoleFirstPreference(phase, allocationNodeRole)) { + if (willUseFallbackTier) { return ( <> - + ); } break; + case 'node_attrs': - if (!hasNodeAttrs) { + /** + * If there are no node attributes, advise the user on the default allocation behavior. + */ + if (!hasNodeAttributes) { + /** + * If data nodes are available, default allocation behavior will be to those nodes. + */ + if (isUsingDeprecatedDataRoleConfig) { + return ( + <> + + + + ); + } + + /** + * Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting + * stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even + * this fallback will not succeed, for example if the user only has 'data' node roles, and no `data_` node roles. + */ + if (noTiersAvailable) { + return ( + <> + + + + ); + } + + /** + * If we are able to fallback to a data tier that does not map to this phase, we show a notice informing the user that their + * data will not be assigned to a corresponding tier. + */ + if (willUseFallbackTier) { + return ( + <> + + + + ); + } + + /** + * If using node roles, default allocation behavior will be to the preferred nodes, depending on the phase. + */ return ( <> - + ); } + /** - * Special cloud case: when deprecated data role configuration is in use, it means that this deployment is not using + * Special Cloud case: when deprecated data role configuration is in use, it means that this deployment is not using * the new node role based allocation. We drive users to the cloud console to migrate to node role based allocation * in that case. */ @@ -137,7 +195,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr {isLoading ? ( <> - + ) : ( error && ( @@ -154,10 +212,11 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr >
diff --git a/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts b/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts index bb14f1d03f31c..27e571248d66d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts @@ -22,3 +22,5 @@ export function init(esDocBasePath: string): void { } export const createDocLink = (docPath: string): string => `${_esDocBasePath}${docPath}`; +export const getNodeAllocationMigrationLink = () => + `${_esDocBasePath}migrate-index-allocation-filters.html`; diff --git a/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts index 15febee856b17..eef480e12aab0 100644 --- a/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts +++ b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts @@ -24,42 +24,10 @@ export const hostSystemOverview: TSVBMetricModelCreator = ( split_mode: 'everything', metrics: [ { - field: 'system.cpu.user.pct', - id: 'avg-cpu-user', + field: 'system.cpu.total.norm.pct', + id: 'avg-cpu-total', type: 'avg', }, - { - field: 'system.cpu.cores', - id: 'max-cpu-cores', - type: 'max', - }, - { - field: 'system.cpu.system.pct', - id: 'avg-cpu-system', - type: 'avg', - }, - { - id: 'calc-user-system-cores', - script: '(params.users + params.system) / params.cores', - type: 'calculation', - variables: [ - { - field: 'avg-cpu-user', - id: 'var-users', - name: 'users', - }, - { - field: 'avg-cpu-system', - id: 'var-system', - name: 'system', - }, - { - field: 'max-cpu-cores', - id: 'var-cores', - name: 'cores', - }, - ], - }, ], }, { diff --git a/x-pack/plugins/infra/public/hooks/use_http_request.tsx b/x-pack/plugins/infra/public/hooks/use_http_request.tsx index 7e72865214618..1d5c1ef4abfbb 100644 --- a/x-pack/plugins/infra/public/hooks/use_http_request.tsx +++ b/x-pack/plugins/infra/public/hooks/use_http_request.tsx @@ -6,12 +6,12 @@ */ import React, { useMemo, useState } from 'react'; -import { IHttpFetchError } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { HttpHandler } from 'src/core/public'; import { ToastInput } from 'src/core/public'; import { useTrackedPromise, CanceledPromiseError } from '../utils/use_tracked_promise'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { InfraHttpError } from '../types'; export function useHTTPRequest( pathname: string, @@ -25,7 +25,7 @@ export function useHTTPRequest( const fetchService = fetch ? fetch : kibana.services.http?.fetch; const toast = toastDanger ? toastDanger : kibana.notifications.toasts.danger; const [response, setResponse] = useState(null); - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [request, makeRequest] = useTrackedPromise( { cancelPreviousOn: 'resolution', @@ -40,7 +40,7 @@ export function useHTTPRequest( }, onResolve: (resp) => setResponse(decode(resp)), onReject: (e: unknown) => { - const err = e as IHttpFetchError; + const err = e as InfraHttpError; if (e && e instanceof CanceledPromiseError) { return; } diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx index 6b245c19e4dc9..d28b19046badc 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { last, first } from 'lodash'; import React, { useState, useCallback, useEffect } from 'react'; +import { EuiPopover } from '@elastic/eui'; import { createWaffleMapNode } from '../lib/nodes_to_wafflemap'; import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../lib/lib'; import { fieldToName } from '../lib/field_to_display_name'; @@ -81,20 +82,26 @@ export const TableView = (props: Props) => { // as well as the node name. There is the possibility that a node can be present in two // different groups and be on the screen at the same time. const uniqueID = [...item.node.path.map((p) => p.value), item.node.name].join(':'); + const button = ( + + {value} + + ); + return ( - - - {value} - - + + ); }, }, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx index 751f2e1913ee3..2a3371fe81459 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx @@ -43,18 +43,20 @@ export const GroupOfNodes: React.FC = ({ - {group.nodes.map((node) => ( - - ))} + {group.width + ? group.nodes.map((node) => ( + + )) + : null} ); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx index d6934c6846b79..e972f9ca4f345 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { first } from 'lodash'; +import { EuiPopover } from '@elastic/eui'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { InfraWaffleMapBounds, @@ -62,70 +63,81 @@ export class Node extends React.PureComponent { const nodeBorder = this.state.isOverlayOpen ? { border: 'solid 4px #000' } : undefined; + const button = ( + + ); + return ( <> - - - - - + setVisible={this.setAlertFlyoutVisible} + visible={isAlertFlyoutVisible} + /> + )} ); } diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index 95b78e222a72b..94b16448a6b61 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiPopoverProps, EuiCode } from '@elastic/eui'; +import { EuiCode } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -21,7 +21,6 @@ import { withTheme, EuiTheme } from '../../../../../../../../../src/plugins/kiba import { Section, SectionLinkProps, - ActionMenu, SectionTitle, SectionSubtitle, SectionLinks, @@ -34,25 +33,10 @@ interface Props { currentTime: number; node: InfraWaffleMapNode; nodeType: InventoryItemType; - isPopoverOpen: boolean; - closePopover: () => void; - popoverPosition: EuiPopoverProps['anchorPosition']; - openNewOverlay?: () => void; } export const NodeContextMenu: React.FC = withTheme( - ({ - options, - currentTime, - children, - node, - isPopoverOpen, - closePopover, - nodeType, - popoverPosition, - theme, - openNewOverlay, - }) => { + ({ options, currentTime, node, nodeType }) => { const [flyoutVisible, setFlyoutVisible] = useState(false); const inventoryModel = findInventoryModel(nodeType); const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000; @@ -164,54 +148,49 @@ export const NodeContextMenu: React.FC = withTheme return ( <> - -
-
- - - - {inventoryId.label && ( - -
- -
-
- )} - - - - - - - -
-
-
- +
+
+ + + + {inventoryId.label && ( + +
+ +
+
+ )} + + + + + + + +
+
+ + {flyoutVisible && ( + + )} ); } diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx index 6451206f8cc1a..a6665e0d244f2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx @@ -7,13 +7,14 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { IHttpFetchError } from 'src/core/public'; import { InvalidNodeError } from './invalid_node'; import { DocumentTitle } from '../../../../components/document_title'; import { ErrorPageBody } from '../../../error'; +import { InfraHttpError } from '../../../../types'; + interface Props { name: string; - error: IHttpFetchError; + error: InfraHttpError; } export const PageError = ({ error, name }: Props) => { diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 068abd0e0f20f..f4a4d41081a79 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -6,6 +6,7 @@ */ import type { CoreSetup, CoreStart, Plugin as PluginClass } from 'kibana/public'; +import { IHttpFetchError } from 'src/core/public'; import type { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import type { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; @@ -59,3 +60,10 @@ export type InfraClientPluginClass = PluginClass< InfraClientSetupDeps, InfraClientStartDeps >; + +export interface InfraHttpError extends IHttpFetchError { + readonly body?: { + statusCode: number; + message?: string; + }; +} diff --git a/x-pack/plugins/infra/server/index.ts b/x-pack/plugins/infra/server/index.ts index b283b690d6b07..a25bba48d673e 100644 --- a/x-pack/plugins/infra/server/index.ts +++ b/x-pack/plugins/infra/server/index.ts @@ -9,6 +9,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { config, InfraConfig, InfraServerPlugin, InfraPluginSetup } from './plugin'; export { config, InfraConfig, InfraPluginSetup }; +export { InfraRequestHandlerContext } from './types'; export function plugin(context: PluginInitializerContext) { return new InfraServerPlugin(context); diff --git a/x-pack/plugins/infra/server/routes/overview/lib/create_top_nodes_query.ts b/x-pack/plugins/infra/server/routes/overview/lib/create_top_nodes_query.ts index a9245a8c8ce75..7533f2801607c 100644 --- a/x-pack/plugins/infra/server/routes/overview/lib/create_top_nodes_query.ts +++ b/x-pack/plugins/infra/server/routes/overview/lib/create_top_nodes_query.ts @@ -60,7 +60,7 @@ export const createTopNodesQuery = ( }, cpu: { avg: { - field: 'system.cpu.total.pct', + field: 'system.cpu.total.norm.pct', }, }, iowait: { diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index bfcc20cc88b81..a5c19911f60b9 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -35,7 +35,6 @@ "savedObjects", "kibanaUtils", "kibanaReact", - "embeddable", - "usageCollection" + "embeddable" ] } diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 214ce6d11cff2..3c4412813bb83 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -25,7 +25,7 @@ import { DefaultInspectorAdapters, RenderMode } from 'src/plugins/expressions'; import { map, distinctUntilChanged, skip } from 'rxjs/operators'; import isEqual from 'fast-deep-equal'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; -import { METRIC_TYPE } from '../../../../../../src/plugins/usage_collection/public'; +import { METRIC_TYPE } from '@kbn/analytics'; import { ExpressionRendererEvent, ReactExpressionRendererType, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 2c503a7bd6967..b74e97df4a895 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -27,7 +27,6 @@ import { getOperationDisplay, insertOrReplaceColumn, replaceColumn, - deleteColumn, updateColumnParam, resetIncomplete, FieldBasedIndexPatternColumn, @@ -422,15 +421,6 @@ export function DimensionEditor(props: DimensionEditorProps) { : (selectedColumn as FieldBasedIndexPatternColumn)?.sourceField } incompleteOperation={incompleteOperation} - onDeleteColumn={() => { - setStateWrapper( - deleteColumn({ - layer: state.layers[layerId], - columnId, - indexPattern: currentIndexPattern, - }) - ); - }} onChoose={(choice) => { setStateWrapper( insertOrReplaceColumn({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 5e79fde0fa8fa..f80b12aecabde 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -1795,7 +1795,7 @@ describe('IndexPatternDimensionEditorPanel', () => { ); }); - it('should clear the dimension when removing the selection in field combobox', () => { + it('should keep the latest valid dimension when removing the selection in field combobox', () => { wrapper = mount(); act(() => { @@ -1805,20 +1805,7 @@ describe('IndexPatternDimensionEditorPanel', () => { .prop('onChange')!([]); }); - expect(setState).toHaveBeenCalledWith( - { - ...state, - layers: { - first: { - indexPatternId: '1', - columns: {}, - columnOrder: [], - incompleteColumns: {}, - }, - }, - }, - { shouldRemoveDimension: false, shouldReplaceDimension: false } - ); + expect(setState).not.toHaveBeenCalled(); }); it('allows custom format', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index ffd4ac2498133..b80d90ba78b1d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -39,7 +39,7 @@ export interface FieldSelectProps extends EuiComboBoxProps void; - onDeleteColumn: () => void; + onDeleteColumn?: () => void; existingFields: IndexPatternPrivateState['existingFields']; fieldIsInvalid: boolean; markAllFieldsCompatible?: boolean; @@ -195,7 +195,7 @@ export function FieldSelect({ singleSelection={{ asPlainText: true }} onChange={(choices) => { if (choices.length === 0) { - onDeleteColumn(); + onDeleteColumn?.(); return; } diff --git a/x-pack/plugins/lists/common/format_errors.ts b/x-pack/plugins/lists/common/format_errors.ts index 7b33612b4e887..16925699b0fcf 100644 --- a/x-pack/plugins/lists/common/format_errors.ts +++ b/x-pack/plugins/lists/common/format_errors.ts @@ -8,6 +8,9 @@ import * as t from 'io-ts'; import { isObject } from 'lodash/fp'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + */ export const formatErrors = (errors: t.Errors): string[] => { const err = errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index 7e43e7dd5f4ab..f223d56eb15cb 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -12,14 +12,44 @@ import * as t from 'io-ts'; import { DefaultNamespace } from '../types/default_namespace'; import { DefaultArray, DefaultStringArray, NonEmptyString } from '../../shared_imports'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const name = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Name = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const nameOrUndefined = t.union([name, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NameOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const description = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Description = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const descriptionOrUndefined = t.union([description, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type DescriptionOrUndefined = t.TypeOf; export const list_id = NonEmptyString; @@ -28,15 +58,47 @@ export const list_idOrUndefined = t.union([list_id, t.undefined]); export type ListIdOrUndefined = t.TypeOf; export const item = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const created_at = t.string; // TODO: Make this into an ISO Date string check + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updated_at = t.string; // TODO: Make this into an ISO Date string check + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updated_by = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const created_by = t.string; + export const file = t.object; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const id = NonEmptyString; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Id = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const idOrUndefined = t.union([id, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type IdOrUndefined = t.TypeOf; export const binary = t.string; @@ -93,6 +155,9 @@ export const valueOrUndefined = t.union([value, t.undefined]); export const tie_breaker_id = t.string; // TODO: Use UUID for this instead of a string for validation export const _index = t.string; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const type = t.keyof({ binary: null, boolean: null, @@ -122,9 +187,24 @@ export const type = t.keyof({ export const typeOrUndefined = t.union([type, t.undefined]); export type Type = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const meta = t.object; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Meta = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const metaOrUndefined = t.union([meta, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type MetaOrUndefined = t.TypeOf; export const esDataTypeRange = t.exact(t.type({ gte: t.string, lte: t.string })); @@ -207,28 +287,77 @@ export const esDataTypeUnion = t.union([ export type EsDataTypeUnion = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const tags = DefaultStringArray; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Tags = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const tagsOrUndefined = t.union([tags, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type TagsOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const exceptionListType = t.keyof({ detection: null, endpoint: null, endpoint_events: null, }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ExceptionListType = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ExceptionListTypeOrUndefined = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export enum ExceptionListTypeEnum { DETECTION = 'detection', ENDPOINT = 'endpoint', ENDPOINT_EVENTS = 'endpoint_events', } +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const exceptionListItemType = t.keyof({ simple: null }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const exceptionListItemTypeOrUndefined = t.union([exceptionListItemType, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ExceptionListItemType = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ExceptionListItemTypeOrUndefined = t.TypeOf; export const list_type = t.keyof({ item: null, list: null }); @@ -275,14 +404,32 @@ export type CursorOrUndefined = t.TypeOf; export const namespace_type = DefaultNamespace; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const operatorIncluded = t.keyof({ included: null }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const operator = t.keyof({ excluded: null, included: null }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Operator = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export enum OperatorEnum { INCLUDED = 'included', EXCLUDED = 'excluded', } +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export enum OperatorTypeEnum { NESTED = 'nested', MATCH = 'match', @@ -320,15 +467,36 @@ export type Immutable = t.TypeOf; export const immutableOrUndefined = t.union([immutable, t.undefined]); export type ImmutableOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const osType = t.keyof({ linux: null, macos: null, windows: null, }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type OsType = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const osTypeArray = DefaultArray(osType); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type OsTypeArray = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type OsTypeArrayOrUndefined = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/types/comment.ts b/x-pack/plugins/lists/common/schemas/types/comment.ts index eeb6688799cf8..016ef1b75edf8 100644 --- a/x-pack/plugins/lists/common/schemas/types/comment.ts +++ b/x-pack/plugins/lists/common/schemas/types/comment.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { created_at, created_by, id, updated_at, updated_by } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const comment = t.intersection([ t.exact( t.type({ @@ -27,8 +30,27 @@ export const comment = t.intersection([ ), ]); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const commentsArray = t.array(comment); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CommentsArray = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Comment = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CommentsArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/create_comment.ts b/x-pack/plugins/lists/common/schemas/types/create_comment.ts index 57c2b0598d9a1..070e860299f3d 100644 --- a/x-pack/plugins/lists/common/schemas/types/create_comment.ts +++ b/x-pack/plugins/lists/common/schemas/types/create_comment.ts @@ -9,15 +9,41 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const createComment = t.exact( t.type({ comment: NonEmptyString, }) ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CreateComment = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const createCommentsArray = t.array(createComment); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CreateCommentsArray = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CreateComments = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type CreateCommentsArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts index ea00a6c07f63b..b190bfb649a9f 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts @@ -13,6 +13,7 @@ import { CommentsArray, comment } from './comment'; /** * Types the DefaultCommentsArray as: * - If null or undefined, then a default array of type entry will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultCommentsArray = new t.Type( 'DefaultCommentsArray', diff --git a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts index ed03182346a9b..92121aaf05a6b 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts @@ -13,6 +13,7 @@ import { CreateCommentsArray, createComment } from './create_comment'; /** * Types the DefaultCreateComments as: * - If null or undefined, then a default array of type entry will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultCreateCommentsArray = new t.Type< CreateCommentsArray, diff --git a/x-pack/plugins/lists/common/schemas/types/default_namespace.ts b/x-pack/plugins/lists/common/schemas/types/default_namespace.ts index 667ddbb82253d..c5ae93b0a11a5 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_namespace.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_namespace.ts @@ -14,6 +14,7 @@ export type NamespaceType = t.TypeOf; /** * Types the DefaultNamespace as: * - If null or undefined, then a default string/enumeration of "single" will be used. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultNamespace = new t.Type( 'DefaultNamespace', diff --git a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts index a87c31b7d9abf..c27e4eade4b38 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts @@ -17,6 +17,7 @@ export type NamespaceTypeArray = t.TypeOf; * Types the DefaultNamespaceArray as: * - If null or undefined, then a default string array of "single" will be used. * - If it contains a string, then it is split along the commas and puts them into an array and validates it + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultNamespaceArray = new t.Type< NamespaceTypeArray, @@ -40,5 +41,12 @@ export const DefaultNamespaceArray = new t.Type< String ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type DefaultNamespaceArrayType = t.OutputOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type DefaultNamespaceArrayTypeDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/default_string_boolean_false.ts b/x-pack/plugins/lists/common/schemas/types/default_string_boolean_false.ts index 02b18b079e3dd..d409648b5435b 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_string_boolean_false.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_string_boolean_false.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultStringBooleanFalse as: * - If a string this will convert the string to a boolean * - If null or undefined, then a default false will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultStringBooleanFalse = new t.Type( 'DefaultStringBooleanFalse', @@ -30,4 +31,7 @@ export const DefaultStringBooleanFalse = new t.Type [] * - Example input converted to output: null -> [] * - Example input converted to output: "a,b,c" -> ["a", "b", "c"] + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const EmptyStringArray = new t.Type( 'EmptyStringArray', @@ -40,5 +41,12 @@ export const EmptyStringArray = new t.Type; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EmptyStringArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/entries.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/entries.ts index 393744cf1fbbf..4622a8a7d39b7 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/entries.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/entries.ts @@ -12,15 +12,22 @@ import { endpointEntryMatchAny } from './entry_match_any'; import { endpointEntryMatch } from './entry_match'; import { endpointEntryNested } from './entry_nested'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointEntriesArray = t.array( t.union([endpointEntryMatch, endpointEntryMatchAny, endpointEntryNested]) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointEntriesArray = t.TypeOf; /** * Types the nonEmptyEndpointEntriesArray as: * - An array of entries of length 1 or greater - * + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const nonEmptyEndpointEntriesArray = new t.Type< EndpointEntriesArray, @@ -39,5 +46,12 @@ export const nonEmptyEndpointEntriesArray = new t.Type< t.identity ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyEndpointEntriesArray = t.OutputOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyEndpointEntriesArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match.ts index fc085a45a519a..e4c24aa5e3560 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../../shared_imports'; import { operatorIncluded } from '../../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointEntryMatch = t.exact( t.type({ field: NonEmptyString, @@ -18,4 +21,8 @@ export const endpointEntryMatch = t.exact( value: NonEmptyString, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointEntryMatch = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_any.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_any.ts index 13559d6699e6e..ffafd0f786547 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_any.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_any.ts @@ -11,6 +11,9 @@ import { NonEmptyString } from '../../../shared_imports'; import { operatorIncluded } from '../../common/schemas'; import { nonEmptyOrNullableStringArray } from '../non_empty_or_nullable_string_array'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointEntryMatchAny = t.exact( t.type({ field: NonEmptyString, @@ -19,4 +22,8 @@ export const endpointEntryMatchAny = t.exact( value: nonEmptyOrNullableStringArray, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointEntryMatchAny = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_wildcard.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_wildcard.ts index dfcaa963666de..ca4894991664c 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_wildcard.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_match_wildcard.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../../shared_imports'; import { operatorIncluded } from '../../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointEntryMatchWildcard = t.exact( t.type({ field: NonEmptyString, @@ -18,4 +21,8 @@ export const endpointEntryMatchWildcard = t.exact( value: NonEmptyString, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointEntryMatchWildcard = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_nested.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_nested.ts index 23bf0097ecef8..4304b7cd06c37 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/entry_nested.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/entry_nested.ts @@ -11,6 +11,9 @@ import { NonEmptyString } from '../../../shared_imports'; import { nonEmptyEndpointNestedEntriesArray } from './non_empty_nested_entries_array'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointEntryNested = t.exact( t.type({ entries: nonEmptyEndpointNestedEntriesArray, @@ -18,4 +21,8 @@ export const endpointEntryNested = t.exact( type: t.keyof({ nested: null }), }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointEntryNested = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/endpoint/non_empty_nested_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/endpoint/non_empty_nested_entries_array.ts index 06b41791a3669..e6bd3d61f7d78 100644 --- a/x-pack/plugins/lists/common/schemas/types/endpoint/non_empty_nested_entries_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/endpoint/non_empty_nested_entries_array.ts @@ -11,15 +11,22 @@ import { Either } from 'fp-ts/lib/Either'; import { endpointEntryMatchAny } from './entry_match_any'; import { endpointEntryMatch } from './entry_match'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const endpointNestedEntriesArray = t.array( t.union([endpointEntryMatch, endpointEntryMatchAny]) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EndpointNestedEntriesArray = t.TypeOf; /** * Types the nonEmptyNestedEntriesArray as: * - An array of entries of length 1 or greater - * + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const nonEmptyEndpointNestedEntriesArray = new t.Type< EndpointNestedEntriesArray, @@ -38,9 +45,16 @@ export const nonEmptyEndpointNestedEntriesArray = new t.Type< t.identity ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyEndpointNestedEntriesArray = t.OutputOf< typeof nonEmptyEndpointNestedEntriesArray >; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyEndpointNestedEntriesArrayDecoded = t.TypeOf< typeof nonEmptyEndpointNestedEntriesArray >; diff --git a/x-pack/plugins/lists/common/schemas/types/entries.ts b/x-pack/plugins/lists/common/schemas/types/entries.ts index 26cfed568cea8..043348070031b 100644 --- a/x-pack/plugins/lists/common/schemas/types/entries.ts +++ b/x-pack/plugins/lists/common/schemas/types/entries.ts @@ -16,6 +16,10 @@ import { entriesMatchWildcard } from './entry_match_wildcard'; // NOTE: Type nested is not included here to denote it's non-recursive nature. // So a nested entry is really just a collection of `Entry` types. + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entry = t.union([ entriesMatch, entriesMatchAny, @@ -23,8 +27,15 @@ export const entry = t.union([ entriesExists, entriesMatchWildcard, ]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Entry = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesArray = t.array( t.union([ entriesMatch, @@ -35,7 +46,18 @@ export const entriesArray = t.array( entriesMatchWildcard, ]) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntriesArray = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntriesArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_exists.ts b/x-pack/plugins/lists/common/schemas/types/entry_exists.ts index da456603e7851..32fb8888c88dc 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_exists.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_exists.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { operator } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesExists = t.exact( t.type({ field: NonEmptyString, @@ -17,4 +20,8 @@ export const entriesExists = t.exact( type: t.keyof({ exists: null }), }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryExists = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_list.ts b/x-pack/plugins/lists/common/schemas/types/entry_list.ts index 23cf25336836f..9b2b345244805 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_list.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_list.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { operator, type } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesList = t.exact( t.type({ field: NonEmptyString, @@ -18,4 +21,8 @@ export const entriesList = t.exact( type: t.keyof({ list: null }), }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryList = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match.ts b/x-pack/plugins/lists/common/schemas/types/entry_match.ts index b572e550ba2cb..e3529f2d043be 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_match.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_match.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { operator } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesMatch = t.exact( t.type({ field: NonEmptyString, @@ -18,4 +21,8 @@ export const entriesMatch = t.exact( value: NonEmptyString, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryMatch = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match_any.ts b/x-pack/plugins/lists/common/schemas/types/entry_match_any.ts index 8f370898cc266..d4846c7949ebb 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_match_any.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_match_any.ts @@ -12,6 +12,9 @@ import { operator } from '../common/schemas'; import { nonEmptyOrNullableStringArray } from './non_empty_or_nullable_string_array'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesMatchAny = t.exact( t.type({ field: NonEmptyString, @@ -20,4 +23,8 @@ export const entriesMatchAny = t.exact( value: nonEmptyOrNullableStringArray, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryMatchAny = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.ts b/x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.ts index 14522256df354..1c8cb4dcdd2d8 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { operator } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesMatchWildcard = t.exact( t.type({ field: NonEmptyString, @@ -18,4 +21,8 @@ export const entriesMatchWildcard = t.exact( value: NonEmptyString, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryMatchWildcard = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/entry_nested.ts b/x-pack/plugins/lists/common/schemas/types/entry_nested.ts index 4ef3c9be61182..e0027dab7e071 100644 --- a/x-pack/plugins/lists/common/schemas/types/entry_nested.ts +++ b/x-pack/plugins/lists/common/schemas/types/entry_nested.ts @@ -11,6 +11,9 @@ import { NonEmptyString } from '../../shared_imports'; import { nonEmptyNestedEntriesArray } from './non_empty_nested_entries_array'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const entriesNested = t.exact( t.type({ entries: nonEmptyNestedEntriesArray, @@ -18,4 +21,8 @@ export const entriesNested = t.exact( type: t.keyof({ nested: null }), }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type EntryNested = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts index 8a9c5ed39e8a1..89d47c5742b08 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_entries_array.ts @@ -14,7 +14,7 @@ import { entriesList } from './entry_list'; /** * Types the nonEmptyEntriesArray as: * - An array of entries of length 1 or greater - * + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const nonEmptyEntriesArray = new t.Type( 'NonEmptyEntriesArray', @@ -37,5 +37,12 @@ export const nonEmptyEntriesArray = new t.Type; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyEntriesArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts index 722f5dd600eec..475183695a559 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts @@ -19,7 +19,7 @@ export type NestedEntriesArray = t.TypeOf; /** * Types the nonEmptyNestedEntriesArray as: * - An array of entries of length 1 or greater - * + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const nonEmptyNestedEntriesArray = new t.Type< NestedEntriesArray, @@ -38,5 +38,12 @@ export const nonEmptyNestedEntriesArray = new t.Type< t.identity ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyNestedEntriesArray = t.OutputOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyNestedEntriesArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_or_nullable_string_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_or_nullable_string_array.ts index 42fbce7e45b61..ae5a24a250f3a 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_or_nullable_string_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_or_nullable_string_array.ts @@ -12,7 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the nonEmptyOrNullableStringArray as: * - An array of non empty strings of length 1 or greater * - This differs from NonEmptyStringArray in that both input and output are type array - * + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const nonEmptyOrNullableStringArray = new t.Type( 'NonEmptyOrNullableStringArray', @@ -31,5 +31,12 @@ export const nonEmptyOrNullableStringArray = new t.Type; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyOrNullableStringArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts index 8fcd2c645be6c..0afb318a6b33a 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts @@ -13,6 +13,7 @@ import { Either } from 'fp-ts/lib/Either'; * - A string that is not empty (which will be turned into an array of size 1) * - A comma separated string that can turn into an array by splitting on it * - Example input converted to output: "a,b,c" -> ["a", "b", "c"] + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const NonEmptyStringArray = new t.Type( 'NonEmptyStringArray', @@ -36,5 +37,12 @@ export const NonEmptyStringArray = new t.Type( String ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyStringArray = t.OutputOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type NonEmptyStringArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/string_to_positive_number.ts b/x-pack/plugins/lists/common/schemas/types/string_to_positive_number.ts index c988a05d3954d..21acc88118039 100644 --- a/x-pack/plugins/lists/common/schemas/types/string_to_positive_number.ts +++ b/x-pack/plugins/lists/common/schemas/types/string_to_positive_number.ts @@ -15,6 +15,7 @@ export type StringToPositiveNumberC = t.Type; * - If a string this converts the string into a number * - Ensures it is a number (and not NaN) * - Ensures it is positive number + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const StringToPositiveNumber: StringToPositiveNumberC = new t.Type( 'StringToPositiveNumber', diff --git a/x-pack/plugins/lists/common/schemas/types/update_comment.ts b/x-pack/plugins/lists/common/schemas/types/update_comment.ts index a167b50ba6304..88d9d38688391 100644 --- a/x-pack/plugins/lists/common/schemas/types/update_comment.ts +++ b/x-pack/plugins/lists/common/schemas/types/update_comment.ts @@ -10,6 +10,9 @@ import * as t from 'io-ts'; import { NonEmptyString } from '../../shared_imports'; import { id } from '../common/schemas'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updateComment = t.intersection([ t.exact( t.type({ @@ -23,8 +26,27 @@ export const updateComment = t.intersection([ ), ]); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type UpdateComment = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updateCommentsArray = t.array(updateComment); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type UpdateCommentsArray = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updateCommentsArrayOrUndefined = t.union([updateCommentsArray, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type UpdateCommentsArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/test_utils.ts b/x-pack/plugins/lists/common/test_utils.ts index 84bf0d25057a2..dcf6a2747c3de 100644 --- a/x-pack/plugins/lists/common/test_utils.ts +++ b/x-pack/plugins/lists/common/test_utils.ts @@ -16,10 +16,16 @@ interface Message { schema: T | {}; } +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onLeft = (errors: t.Errors): Message => { return { errors, schema: {} }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onRight = (schema: T): Message => { return { errors: [], @@ -27,12 +33,16 @@ const onRight = (schema: T): Message => { }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ export const foldLeftRight = fold(onLeft, onRight); /** * Convenience utility to keep the error message handling within tests to be * very concise. * @param validation The validation to get the errors from + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts */ export const getPaths =
(validation: t.Validation): string[] => { return pipe( @@ -46,6 +56,7 @@ export const getPaths = (validation: t.Validation): string[] => { /** * Convenience utility to remove text appended to links by EUI + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts */ export const removeExternalLinkText = (str: string): string => str.replace(/\(opens in a new tab or window\)/g, ''); diff --git a/x-pack/plugins/lists/common/types.ts b/x-pack/plugins/lists/common/types.ts index 130e3ef1bae1a..a3cbb870faa5b 100644 --- a/x-pack/plugins/lists/common/types.ts +++ b/x-pack/plugins/lists/common/types.ts @@ -18,7 +18,7 @@ export type SavedObjectType = 'exception-list' | 'exception-list-agnostic'; * * will yield a type of: * type A = { a: undefined; b: number; } - * + * @deprecated This has no replacement. We should stop using/relying on this and just remove it. */ export type RequiredKeepUndefined = { [K in keyof T]-?: [T[K]] } extends infer U ? U extends Record diff --git a/x-pack/plugins/maps/public/embeddable/index.ts b/x-pack/plugins/maps/public/embeddable/index.ts index 97ab0f905530f..b73f9fb9de42a 100644 --- a/x-pack/plugins/maps/public/embeddable/index.ts +++ b/x-pack/plugins/maps/public/embeddable/index.ts @@ -6,4 +6,5 @@ */ export * from './map_embeddable'; +export * from './types'; export * from './map_embeddable_factory'; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index d11e1d59b28f7..643199dbf3933 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -78,7 +78,6 @@ import { MapEmbeddableInput, MapEmbeddableOutput, } from './types'; -export { MapEmbeddableInput, MapEmbeddableOutput }; function getIsRestore(searchSessionId?: string) { if (!searchSessionId) { diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index dc9cb2d594fe3..29cc20c706296 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -23,4 +23,6 @@ export type { RenderTooltipContentParams } from './classes/tooltips/tooltip_prop export { MapsStartApi } from './api'; -export type { MapEmbeddable, MapEmbeddableInput } from './embeddable'; +export type { MapEmbeddable, MapEmbeddableInput, MapEmbeddableOutput } from './embeddable'; + +export type { EMSTermJoinConfig, SampleValuesConfig } from './ems_autosuggest'; diff --git a/x-pack/plugins/ml/common/types/common.ts b/x-pack/plugins/ml/common/types/common.ts index a5253456252cd..fdeffe14ddaf7 100644 --- a/x-pack/plugins/ml/common/types/common.ts +++ b/x-pack/plugins/ml/common/types/common.ts @@ -46,3 +46,7 @@ export interface ListingPageUrlState { export type AppPageState = { [key in MlPages]?: Partial; }; + +type Without = { [P in Exclude]?: never }; + +export type XOR = T | U extends object ? (Without & U) | (Without & T) : T | U; diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts index 6b320d503b4c0..3c4c3af748645 100644 --- a/x-pack/plugins/ml/common/types/trained_models.ts +++ b/x-pack/plugins/ml/common/types/trained_models.ts @@ -7,6 +7,7 @@ import { DataFrameAnalyticsConfig } from './data_frame_analytics'; import { FeatureImportanceBaseline, TotalFeatureImportance } from './feature_importance'; +import { XOR } from './common'; export interface IngestStats { count: number; @@ -45,22 +46,54 @@ export interface TrainedModelStat { }; } +type TreeNode = object; + +export type PutTrainedModelConfig = { + description?: string; + metadata?: { + analytics_config: DataFrameAnalyticsConfig; + input: unknown; + total_feature_importance?: TotalFeatureImportance[]; + feature_importance_baseline?: FeatureImportanceBaseline; + model_aliases?: string[]; + } & Record; + tags?: string[]; + inference_config?: Record; + input: { field_names: string[] }; +} & XOR< + { compressed_definition: string }, + { + definition: { + preprocessors: object[]; + trained_model: { + tree: { + classification_labels?: string; + feature_names: string; + target_type: string; + tree_structure: TreeNode[]; + }; + tree_node: TreeNode; + ensemble?: object; + }; + }; + } +>; // compressed_definition and definition are mutually exclusive + export interface TrainedModelConfigResponse { - description: string; + description?: string; created_by: string; create_time: string; default_field_map: Record; estimated_heap_memory_usage_bytes: number; estimated_operations: number; license_level: string; - metadata?: - | { - analytics_config: DataFrameAnalyticsConfig; - input: any; - total_feature_importance?: TotalFeatureImportance[]; - feature_importance_baseline?: FeatureImportanceBaseline; - } - | Record; + metadata?: { + analytics_config: DataFrameAnalyticsConfig; + input: unknown; + total_feature_importance?: TotalFeatureImportance[]; + feature_importance_baseline?: FeatureImportanceBaseline; + model_aliases?: string[]; + } & Record; model_id: string; tags: string[]; version: string; diff --git a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx b/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx index b652312c12d14..0ca5389f1d423 100644 --- a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx @@ -10,13 +10,14 @@ import React, { useEffect, useRef, useState } from 'react'; import { htmlIdGenerator } from '@elastic/eui'; import { LayerDescriptor } from '../../../../../maps/common/descriptor_types'; import { INITIAL_LOCATION } from '../../../../../maps/common/constants'; -import { +import type { MapEmbeddable, MapEmbeddableInput, MapEmbeddableOutput, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../maps/public/embeddable'; -import { MAP_SAVED_OBJECT_TYPE, RenderTooltipContentParams } from '../../../../../maps/public'; + RenderTooltipContentParams, +} from '../../../../../maps/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/public'; + import { EmbeddableFactory, ErrorEmbeddable, diff --git a/x-pack/plugins/ml/public/application/components/vega_chart/vega_chart_view.tsx b/x-pack/plugins/ml/public/application/components/vega_chart/vega_chart_view.tsx index 5064cf6435227..93276067f9663 100644 --- a/x-pack/plugins/ml/public/application/components/vega_chart/vega_chart_view.tsx +++ b/x-pack/plugins/ml/public/application/components/vega_chart/vega_chart_view.tsx @@ -15,6 +15,7 @@ import type { TopLevelSpec } from 'vega-lite/build/vega-lite'; // @ts-ignore import { compile } from 'vega-lite/build/vega-lite'; import { parse, View, Warn } from 'vega'; +import { expressionInterpreter } from 'vega-interpreter'; import { Handler } from 'vega-tooltip'; import { htmlIdGenerator } from '@elastic/eui'; @@ -29,7 +30,7 @@ export const VegaChartView: FC = ({ vegaSpec }) => { useEffect(() => { const vgSpec = compile(vegaSpec).spec; - const view = new View(parse(vgSpec)) + const view = new View(parse(vgSpec, undefined, { ast: true }), { expr: expressionInterpreter }) .logLevel(Warn) .renderer('canvas') .tooltip(new Handler().call) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_navigation_bar/analytics_navigation_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_navigation_bar/analytics_navigation_bar.tsx index 32aa14559da0e..d26b5d5cfc16f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_navigation_bar/analytics_navigation_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_navigation_bar/analytics_navigation_bar.tsx @@ -31,6 +31,7 @@ export const AnalyticsNavigationBar: FC<{ defaultMessage: 'Jobs', }), path: '/data_frame_analytics', + testSubj: 'mlAnalyticsJobsTab', }, { id: 'models', @@ -38,6 +39,7 @@ export const AnalyticsNavigationBar: FC<{ defaultMessage: 'Models', }), path: '/data_frame_analytics/models', + testSubj: 'mlTrainedModelsTab', }, ]; if (jobId !== undefined || modelId !== undefined) { @@ -47,6 +49,7 @@ export const AnalyticsNavigationBar: FC<{ defaultMessage: 'Map', }), path: '/data_frame_analytics/map', + testSubj: '', }); } return navTabs; @@ -67,6 +70,7 @@ export const AnalyticsNavigationBar: FC<{ key={`tab-${tab.id}`} isSelected={tab.id === selectedTabId} onClick={onTabClick.bind(null, tab)} + data-test-subj={tab.testSubj} > {tab.name} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx index 5562fcbe093a1..88ffaa0da7fdc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx @@ -28,6 +28,8 @@ import { EuiDescriptionListProps } from '@elastic/eui/src/components/description import { ModelItemFull } from './models_list'; import { useMlKibana } from '../../../../../contexts/kibana'; import { timeFormatter } from '../../../../../../../common/util/date_utils'; +import { isDefined } from '../../../../../../../common/types/guards'; +import { isPopulatedObject } from '../../../../../../../common'; interface ExpandedRowProps { item: ModelItemFull; @@ -69,6 +71,8 @@ export const ExpandedRow: FC = ({ item }) => { description, } = item; + const { analytics_config: analyticsConfig, ...restMetaData } = metadata ?? {}; + const details = { description, tags, @@ -81,6 +85,7 @@ export const ExpandedRow: FC = ({ item }) => { function formatToListItems(items: Record): EuiDescriptionListProps['listItems'] { return Object.entries(items) + .filter(([, value]) => isDefined(value)) .map(([title, value]) => { if (title in formatterDictionary) { return { @@ -102,12 +107,9 @@ export const ExpandedRow: FC = ({ item }) => { {JSON.stringify(value, null, 2)} ) : ( - value + value.toString() ), }; - }) - .filter(({ description: d }) => { - return d !== undefined; }); } @@ -149,6 +151,26 @@ export const ExpandedRow: FC = ({ item }) => { /> + {isPopulatedObject(restMetaData) ? ( + + + +
+ +
+
+ + +
+
+ ) : null} ), @@ -187,7 +209,7 @@ export const ExpandedRow: FC = ({ item }) => { /> - {metadata?.analytics_config && ( + {analyticsConfig && ( @@ -202,7 +224,7 @@ export const ExpandedRow: FC = ({ item }) => { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx index 4c3da00ed7cad..b9803f1ea26e0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx @@ -53,6 +53,7 @@ import { timeFormatter } from '../../../../../../../common/util/date_utils'; import { ListingPageUrlState } from '../../../../../../../common/types/common'; import { usePageUrlState } from '../../../../../util/url_state'; import { BUILT_IN_MODEL_TAG } from '../../../../../../../common/constants/data_frame_analytics'; +import { useTableSettings } from '../analytics_list/use_table_settings'; type Stats = Omit; @@ -90,12 +91,6 @@ export const ModelsList: FC = () => { ); const searchQueryText = pageState.queryText ?? ''; - const setSearchQueryText = useCallback( - (value) => { - updatePageState({ queryText: value }); - }, - [updatePageState] - ); const canDeleteDataFrameAnalytics = capabilities.ml.canDeleteDataFrameAnalytics as boolean; @@ -297,7 +292,7 @@ export const ModelsList: FC = () => { }), icon: 'visTable', type: 'icon', - available: (item) => item.metadata?.analytics_config?.id, + available: (item) => !!item.metadata?.analytics_config?.id, onClick: async (item) => { if (item.metadata?.analytics_config === undefined) return; @@ -332,7 +327,7 @@ export const ModelsList: FC = () => { icon: 'graphApp', type: 'icon', isPrimary: true, - available: (item) => item.metadata?.analytics_config?.id, + available: (item) => !!item.metadata?.analytics_config?.id, onClick: async (item) => { const path = await mlUrlGenerator.createUrl({ page: ML_PAGES.DATA_FRAME_ANALYTICS_MAP, @@ -521,13 +516,19 @@ export const ModelsList: FC = () => { } : undefined; + const { onTableChange, pagination, sorting } = useTableSettings( + items, + pageState, + updatePageState + ); + const search: EuiSearchBarProps = { query: searchQueryText, onChange: (searchChange) => { if (searchChange.error !== null) { return false; } - setSearchQueryText(searchChange.queryText); + updatePageState({ queryText: searchChange.queryText, pageIndex: 0 }); return true; }, box: { @@ -572,6 +573,9 @@ export const ModelsList: FC = () => { rowProps={(item) => ({ 'data-test-subj': `mlModelsTableRow row-${item.model_id}`, })} + pagination={pagination} + onTableChange={onTableChange} + sorting={sorting} />
{modelsToDelete.length > 0 && ( diff --git a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx new file mode 100644 index 0000000000000..fcdd0a71a8f55 --- /dev/null +++ b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx @@ -0,0 +1,268 @@ +/* + * 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 React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { + EuiAccordion, + EuiIconTip, + EuiPanel, + EuiSpacer, + EuiTitle, + htmlIdGenerator, +} from '@elastic/eui'; +import { VectorLayerDescriptor } from '../../../../maps/common/descriptor_types'; +import { + FIELD_ORIGIN, + SOURCE_TYPES, + STYLE_TYPE, + COLOR_MAP_TYPE, +} from '../../../../maps/common/constants'; +import { useMlKibana } from '../contexts/kibana'; +import { MlEmbeddedMapComponent } from '../components/ml_embedded_map'; +import { EMSTermJoinConfig } from '../../../../maps/public'; +import { AnomaliesTableRecord } from '../../../common/types/anomalies'; + +const MAX_ENTITY_VALUES = 3; +const COMMON_EMS_LAYER_IDS = [ + 'world_countries', + 'administrative_regions_lvl2', + 'usa_zip_codes', + 'usa_states', +]; + +function getAnomalyRows(anomalies: AnomaliesTableRecord[], jobId: string) { + const anomalyRows: Record< + string, + { count: number; entityValue: string; max_severity: number } + > = {}; + for (let i = 0; i < anomalies.length; i++) { + const anomaly = anomalies[i]; + const location = anomaly.entityValue; + if (anomaly.jobId !== jobId) continue; + + if (anomalyRows[location] === undefined) { + // add it to the object and set count to 1 + anomalyRows[location] = { + count: 1, + entityValue: location, + max_severity: Math.floor(anomaly.severity), + }; + } else { + anomalyRows[location].count += 1; + if (anomaly.severity > anomalyRows[location].max_severity) { + anomalyRows[location].max_severity = Math.floor(anomaly.severity); + } + } + } + return Object.values(anomalyRows); +} + +export const getChoroplethAnomaliesLayer = ( + anomalies: AnomaliesTableRecord[], + { layerId, field, jobId }: MLEMSTermJoinConfig, + visible: boolean +): VectorLayerDescriptor => { + return { + id: htmlIdGenerator()(), + label: i18n.translate('xpack.ml.explorer.anomaliesMap.anomaliesCount', { + defaultMessage: 'Anomalies count: {jobId}', + values: { jobId }, + }), + joins: [ + { + // Left join is the id from the type of field (e.g. world_countries) + leftField: field, + right: { + id: 'anomaly_count', + type: SOURCE_TYPES.TABLE_SOURCE, + __rows: getAnomalyRows(anomalies, jobId), + __columns: [ + { + name: 'entityValue', + type: 'string', + }, + { + name: 'count', + type: 'number', + }, + { + name: 'max_severity', + type: 'number', + }, + ], + // Right join/term is the field in the doc you’re trying to join it to (foreign key - e.g. US) + term: 'entityValue', + }, + }, + ], + sourceDescriptor: { + type: 'EMS_FILE', + id: layerId, + }, + style: { + type: 'VECTOR', + // @ts-ignore missing style properties. Remove once 'VectorLayerDescriptor' type is updated + properties: { + icon: { type: STYLE_TYPE.STATIC, options: { value: 'marker' } }, + fillColor: { + type: STYLE_TYPE.DYNAMIC, + options: { + color: 'Blue to Red', + colorCategory: 'palette_0', + fieldMetaOptions: { isEnabled: true, sigma: 3 }, + type: COLOR_MAP_TYPE.ORDINAL, + field: { + name: 'count', + origin: FIELD_ORIGIN.JOIN, + }, + useCustomColorRamp: false, + }, + }, + lineColor: { + type: STYLE_TYPE.DYNAMIC, + options: { fieldMetaOptions: { isEnabled: true } }, + }, + lineWidth: { type: STYLE_TYPE.STATIC, options: { size: 1 } }, + }, + isTimeAware: true, + }, + visible, + type: 'VECTOR', + }; +}; + +interface Props { + anomalies: AnomaliesTableRecord[]; + jobIds: string[]; +} + +interface MLEMSTermJoinConfig extends EMSTermJoinConfig { + jobId: string; +} + +export const AnomaliesMap: FC = ({ anomalies, jobIds }) => { + const [EMSSuggestions, setEMSSuggestions] = useState< + Array | undefined + >(); + const { + services: { maps: mapsPlugin }, + } = useMlKibana(); + + const getEMSTermSuggestions = useCallback(async (): Promise => { + if (!mapsPlugin) return; + + const suggestions = await Promise.all( + jobIds.map(async (jobId) => { + const entityValues = new Set(); + let entityName; + for (let i = 0; i < anomalies.length; i++) { + if ( + jobId === anomalies[i].jobId && + anomalies[i].entityValue !== '' && + anomalies[i].entityValue !== undefined && + anomalies[i].entityName !== '' && + anomalies[i].entityName !== undefined + ) { + entityValues.add(anomalies[i].entityValue); + + if (!entityName) { + entityName = anomalies[i].entityName; + } + } + + if ( + // convert to set so it's unique values + entityValues.size === MAX_ENTITY_VALUES + ) + break; + } + + const suggestion: EMSTermJoinConfig | null = await mapsPlugin.suggestEMSTermJoinConfig({ + emsLayerIds: COMMON_EMS_LAYER_IDS, + sampleValues: Array.from(entityValues), + sampleValuesColumnName: entityName || '', + }); + if (suggestion) { + return { jobId, ...suggestion }; + } + return suggestion; + }) + ); + + setEMSSuggestions(suggestions.filter((s) => s !== null)); + }, [...jobIds]); + + useEffect( + function getInitialEMSTermSuggestions() { + if (anomalies && anomalies.length > 0) { + getEMSTermSuggestions(); + } + }, + [...jobIds] + ); + + const layerList = useMemo(() => { + let layers: VectorLayerDescriptor[] = []; + // Loop through suggestions list and make a layer for each + if (EMSSuggestions?.length) { + let count = 0; + layers = EMSSuggestions.reduce(function (result, suggestion) { + if (suggestion) { + const visible = count === 0; + result.push(getChoroplethAnomaliesLayer(anomalies, suggestion, visible)); + count++; + } + return result; + }, [] as VectorLayerDescriptor[]); + } + return layers; + }, [EMSSuggestions, anomalies]); + + if (EMSSuggestions?.length === 0) { + return null; + } + + return ( + <> + + +

+ + ), + }} + /> +

+ + } + > +
+ +
+
+
+ + + ); +}; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js index 47d3e154ad325..7cc1d0d86e2ff 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer.js @@ -70,6 +70,9 @@ import { ExplorerChartsContainer } from './explorer_charts/explorer_charts_conta // Anomalies Table import { AnomaliesTable } from '../components/anomalies_table/anomalies_table'; +// Anomalies Map +import { AnomaliesMap } from './anomalies_map'; + import { getToastNotifications } from '../util/dependency_cache'; import { ANOMALY_DETECTION_DEFAULT_TIME_RANGE } from '../../../common/constants/settings'; import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; @@ -399,6 +402,9 @@ export class ExplorerUI extends React.Component { )} + {loading === false && tableData.anomalies?.length && ( + + )} {annotationsData.length > 0 && ( <> diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts index 8bfece0e4c732..49d20d3f59b61 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts @@ -7,6 +7,7 @@ import { FIELD_ORIGIN, STYLE_TYPE } from '../../../../../maps/common/constants'; import { ANOMALY_THRESHOLD, SEVERITY_COLORS } from '../../../../common'; +import { AnomaliesTableData } from '../explorer_utils'; const FEATURE = 'Feature'; const POINT = 'Point'; @@ -29,7 +30,10 @@ const SEVERITY_COLOR_RAMP = [ }, ]; -function getAnomalyFeatures(anomalies: any[], type: 'actual_point' | 'typical_point') { +function getAnomalyFeatures( + anomalies: AnomaliesTableData['anomalies'], + type: 'actual_point' | 'typical_point' +) { const anomalyFeatures = []; for (let i = 0; i < anomalies.length; i++) { const anomaly = anomalies[i]; @@ -58,7 +62,7 @@ function getAnomalyFeatures(anomalies: any[], type: 'actual_point' | 'typical_po return anomalyFeatures; } -export const getMLAnomaliesTypicalLayer = (anomalies: any) => { +export const getMLAnomaliesTypicalLayer = (anomalies: AnomaliesTableData['anomalies']) => { return { id: 'anomalies_typical_layer', label: 'Typical', diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts index bafa5c300e79f..84f0fbaea0579 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts @@ -11,8 +11,8 @@ import { PipelineDefinition } from '../../../common/types/trained_models'; export function modelsProvider(client: IScopedClusterClient) { return { /** - * Retrieves the map of model ids and associated pipelines. - * @param modelIds + * Retrieves the map of model ids and aliases with associated pipelines. + * @param modelIds - Array of models ids and model aliases. */ async getModelsPipelines(modelIds: string[]) { const modelIdsMap = new Map | null>( diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index dbfc2195a12e1..c4b2d63b05d13 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -13,6 +13,7 @@ import { optionalModelIdSchema, } from './schemas/inference_schema'; import { modelsProvider } from '../models/data_frame_analytics'; +import { TrainedModelConfigResponse } from '../../common/types/trained_models'; export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) { /** @@ -42,14 +43,32 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) ...query, ...(modelId ? { model_id: modelId } : {}), }); - const result = body.trained_model_configs; + const result = body.trained_model_configs as TrainedModelConfigResponse[]; try { if (withPipelines) { + const modelIdsAndAliases: string[] = Array.from( + new Set( + result + .map(({ model_id: id, metadata }) => { + return [id, ...(metadata?.model_aliases ?? [])]; + }) + .flat() + ) + ); + const pipelinesResponse = await modelsProvider(client).getModelsPipelines( - result.map(({ model_id: id }: { model_id: string }) => id) + modelIdsAndAliases ); for (const model of result) { - model.pipelines = pipelinesResponse.get(model.model_id)!; + model.pipelines = { + ...(pipelinesResponse.get(model.model_id) ?? {}), + ...(model.metadata?.model_aliases ?? []).reduce((acc, alias) => { + return { + ...acc, + ...(pipelinesResponse.get(alias) ?? {}), + }; + }, {}), + }; } } } catch (e) { diff --git a/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx index dcfd3adf72168..1ec447ee2f84a 100644 --- a/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx +++ b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx @@ -65,6 +65,7 @@ const getHeading = (isFleetTypeMetric: boolean) => { defaultMessage="APM & Fleet Server" /> ); + return titles; } titles.title = i18n.translate('xpack.monitoring.apm.metrics.topCharts.title', { defaultMessage: 'APM Server - Resource Usage', diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index 64afe8988e8bf..6ab6ed758c050 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -50,6 +50,7 @@ const getServerTitle = (isFleetTypeMetric, total) => { values: { apmsTotal }, } ); + return linkLabel; } linkLabel.link = ( { if (!Legacy.shims.isCloud || !versions) { return false; } + let criteriaPassed = false; versions.forEach((version) => { const [major, minor] = version.split('.'); const majorInt = Number(major); if (majorInt > 7 || (majorInt === 7 && Number(minor) >= 13)) { - return true; + criteriaPassed = true; + return; } }); - return false; + return criteriaPassed; }; diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 8ea37d04c146c..45b3e07200680 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -32,6 +32,9 @@ describe('config schema', () => { "interval": "10s", }, "cluster_alerts": Object { + "allowedSpaces": Array [ + "default", + ], "email_notifications": Object { "email_address": "", "enabled": true, diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 860c564ce3249..8c411fb5c28a8 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -57,6 +57,7 @@ export const configSchema = schema.object({ }), }), cluster_alerts: schema.object({ + allowedSpaces: schema.arrayOf(schema.string(), { defaultValue: ['default'] }), enabled: schema.boolean({ defaultValue: true }), email_notifications: schema.object({ enabled: schema.boolean({ defaultValue: true }), diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts index fb67cd1805950..213e73a4b9534 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_ccr_read_exceptions.ts @@ -96,9 +96,9 @@ export async function fetchCCRReadExceptions( const { body: response } = await esClient.search(params); const stats: CCRReadExceptionsStats[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not specify buckets - const { buckets: remoteClusterBuckets = [] } = response.aggregations.remote_clusters; + const { buckets: remoteClusterBuckets = [] } = response.aggregations?.remote_clusters; - if (!remoteClusterBuckets.length) { + if (!remoteClusterBuckets?.length) { return stats; } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts index 30daee225fcb4..a51dccd727966 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts @@ -102,9 +102,9 @@ export async function fetchDiskUsageNodeStats( const { body: response } = await esClient.search(params); const stats: AlertDiskUsageNodeStats[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not define buckets - const { buckets: clusterBuckets = [] } = response.aggregations!.clusters; + const { buckets: clusterBuckets } = response.aggregations?.clusters; - if (!clusterBuckets.length) { + if (!clusterBuckets?.length) { return stats; } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_index_shard_size.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_index_shard_size.ts index c3e9f08c3b949..e1da45ab7d991 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_index_shard_size.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_index_shard_size.ts @@ -16,7 +16,7 @@ interface SourceNode { uuid: string; } type TopHitType = ElasticsearchResponseHit & { - _source: { index_stats: Partial; source_node: SourceNode }; + _source: { index_stats?: Partial; source_node?: SourceNode }; }; const memoizedIndexPatterns = (globPatterns: string) => { @@ -68,36 +68,32 @@ export async function fetchIndexShardSize( size, }, aggs: { - over_threshold: { + index: { + terms: { + field: 'index_stats.index', + size, + }, aggs: { - index: { - terms: { - field: 'index_stats.index', - size, - }, - aggs: { - hits: { - top_hits: { - sort: [ - { - timestamp: { - order: 'desc' as const, - unmapped_type: 'long' as const, - }, - }, - ], - _source: { - includes: [ - '_index', - 'index_stats.shards.primaries', - 'index_stats.primaries.store.size_in_bytes', - 'source_node.name', - 'source_node.uuid', - ], + hits: { + top_hits: { + sort: [ + { + timestamp: { + order: 'desc' as const, + unmapped_type: 'long' as const, }, - size: 1, }, + ], + _source: { + includes: [ + '_index', + 'index_stats.shards.primaries', + 'index_stats.primaries.store.size_in_bytes', + 'source_node.name', + 'source_node.uuid', + ], }, + size: 1, }, }, }, @@ -109,17 +105,16 @@ export async function fetchIndexShardSize( }; const { body: response } = await esClient.search(params); - const stats: IndexShardSizeStats[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not specify buckets - const { buckets: clusterBuckets = [] } = response.aggregations.clusters; - const validIndexPatterns = memoizedIndexPatterns(shardIndexPatterns); - - if (!clusterBuckets.length) { + const { buckets: clusterBuckets } = response.aggregations?.clusters; + const stats: IndexShardSizeStats[] = []; + if (!clusterBuckets?.length) { return stats; } + const validIndexPatterns = memoizedIndexPatterns(shardIndexPatterns); const thresholdBytes = threshold * gbMultiplier; for (const clusterBucket of clusterBuckets) { - const indexBuckets = clusterBucket.over_threshold.index.buckets; + const indexBuckets = clusterBucket.index.buckets; const clusterUuid = clusterBucket.key; for (const indexBucket of indexBuckets) { @@ -137,7 +132,7 @@ export async function fetchIndexShardSize( _source: { source_node: sourceNode, index_stats: indexStats }, } = topHit; - if (!indexStats || !indexStats.primaries) { + if (!indexStats || !indexStats.primaries || !sourceNode) { continue; } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts index aad4638bf8359..245838541d435 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_memory_usage_node_stats.ts @@ -95,9 +95,9 @@ export async function fetchMemoryUsageNodeStats( const { body: response } = await esClient.search(params); const stats: AlertMemoryUsageNodeStats[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not define buckets - const { buckets: clusterBuckets = [] } = response.aggregations.clusters; + const { buckets: clusterBuckets } = response.aggregations?.clusters; - if (!clusterBuckets.length) { + if (!clusterBuckets?.length) { return stats; } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_nodes_from_cluster_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_nodes_from_cluster_stats.ts index ff3a8d4aa7ef8..d1a343b9b3eef 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_nodes_from_cluster_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_nodes_from_cluster_stats.ts @@ -89,9 +89,12 @@ export async function fetchNodesFromClusterStats( }; const { body: response } = await esClient.search(params); - const nodes = []; + const nodes: AlertClusterStatsNodes[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not define buckets - const clusterBuckets = response.aggregations.clusters.buckets; + const clusterBuckets = response.aggregations?.clusters?.buckets; + if (!clusterBuckets?.length) { + return nodes; + } for (const clusterBucket of clusterBuckets) { const clusterUuid = clusterBucket.key; const hits = clusterBucket.top.hits.hits; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts index b63244dab719d..db5943ca67031 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_thread_pool_rejections_stats.ts @@ -97,9 +97,9 @@ export async function fetchThreadPoolRejectionStats( const { body: response } = await esClient.search(params); const stats: AlertThreadPoolRejectionsStats[] = []; // @ts-expect-error @elastic/elasticsearch Aggregate does not specify buckets - const { buckets: clusterBuckets = [] } = response.aggregations.clusters; + const { buckets: clusterBuckets } = response.aggregations?.clusters; - if (!clusterBuckets.length) { + if (!clusterBuckets?.length) { return stats; } diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts index e825c11566135..8b89416f14e9f 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts @@ -108,7 +108,7 @@ export function getMlJobsForCluster( const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); return callWithRequest(req, 'search', params).then((response: ElasticsearchResponse) => { - return response.aggregations.jobs_count.value ?? 0; + return response.aggregations?.jobs_count.value ?? 0; }); } diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts index f7e2775328e20..5219a2e9286f5 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts @@ -43,7 +43,7 @@ export function handleResponse( 'elasticsearch.cluster.stats.state.master_node', get(cluster, 'cluster_state.master_node') ); - nodes = resp.aggregations.nodes.buckets.reduce(normalizeNodeShards(masterNode), {}); + nodes = resp.aggregations?.nodes.buckets.reduce(normalizeNodeShards(masterNode), {}) ?? []; } return { diff --git a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.ts b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.ts index 6708e6a8423bb..b17f7d27c6c9b 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.ts +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.ts @@ -93,7 +93,7 @@ export function _enrichStateWithStatsAggregation( const totalProcessorsDurationInMillis = totalDurationStats.max - totalDurationStats.min; const verticesWithStatsBuckets = - statsAggregation.aggregations.pipelines.scoped.vertices.vertex_id.buckets; + statsAggregation.aggregations?.pipelines.scoped.vertices.vertex_id.buckets ?? []; verticesWithStatsBuckets.forEach((vertexStatsBucket: any) => { // Each vertexStats bucket contains a list of stats for a single vertex within a single timeseries interval const vertexId = vertexStatsBucket.key; @@ -142,7 +142,7 @@ export async function getPipeline( getPipelineStatsAggregation(req, lsIndexPattern, timeseriesInterval, options), ]); - if (stateDocument === null) { + if (stateDocument === null || !statsAggregation) { return boom.notFound( `Pipeline [${pipelineId} @ ${version.hash}] not found in the selected time range for cluster [${clusterUuid}].` ); diff --git a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_vertex.ts b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_vertex.ts index 1d64b9f63ad9f..3d657e8344e3c 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_vertex.ts +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_vertex.ts @@ -99,7 +99,7 @@ export function _enrichVertexStateWithStatsAggregation( } // Next, iterate over timeseries metrics and attach them to vertex - const timeSeriesBuckets = vertexStatsAggregation.aggregations.timeseries.buckets; + const timeSeriesBuckets = vertexStatsAggregation.aggregations?.timeseries.buckets ?? []; timeSeriesBuckets.forEach((timeSeriesBucket: any) => { // each bucket calculates stats for total pipeline CPU time for the associated timeseries const totalDurationStats = timeSeriesBucket.pipelines.scoped.total_processor_duration_stats; @@ -160,7 +160,7 @@ export async function getPipelineVertex( getPipelineVertexStatsAggregation(req, lsIndexPattern, timeseriesInterval, options), ]); - if (stateDocument === null) { + if (stateDocument === null || !statsAggregation) { return boom.notFound( `Pipeline [${pipelineId} @ ${version.hash}] not found in the selected time range for cluster [${clusterUuid}].` ); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index 01ab392e0563c..e48f424a3d8ee 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -8,7 +8,7 @@ // @ts-ignore import { handleError } from '../../../../lib/errors'; import { AlertsFactory } from '../../../../alerts'; -import { RouteDependencies } from '../../../../types'; +import { LegacyServer, RouteDependencies } from '../../../../types'; import { ALERT_ACTION_TYPE_LOG } from '../../../../../common/constants'; import { ActionResult } from '../../../../../../actions/common'; import { AlertingSecurity } from '../../../../lib/elasticsearch/verify_alerting_security'; @@ -17,7 +17,7 @@ import { AlertTypeParams, SanitizedAlert } from '../../../../../../alerting/comm const DEFAULT_SERVER_LOG_NAME = 'Monitoring: Write to Kibana log'; -export function enableAlertsRoute(_server: unknown, npRoute: RouteDependencies) { +export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependencies) { npRoute.router.post( { path: '/api/monitoring/v1/alerts/enable', @@ -25,6 +25,17 @@ export function enableAlertsRoute(_server: unknown, npRoute: RouteDependencies) }, async (context, request, response) => { try { + // Check to ensure the space is listed in monitoring.cluster_alerts.allowedSpaces + const config = server.config(); + const allowedSpaces = + config.get('monitoring.cluster_alerts.allowedSpaces') || ([] as string[]); + if (!allowedSpaces.includes(context.infra.spaceId)) { + server.log.info( + `Skipping alert creation for "${context.infra.spaceId}" space; add space ID to 'monitoring.cluster_alerts.allowedSpaces' in your kibana.yml` + ); + return response.ok({ body: undefined }); + } + const alerts = AlertsFactory.getAll(); if (alerts.length) { const { @@ -33,6 +44,9 @@ export function enableAlertsRoute(_server: unknown, npRoute: RouteDependencies) } = await AlertingSecurity.getSecurityHealth(context, npRoute.encryptedSavedObjects); if (!isSufficientlySecure || !hasPermanentEncryptionKey) { + server.log.info( + `Skipping alert creation for "${context.infra.spaceId}" space; Stack monitoring alerts require Transport Layer Security between Kibana and Elasticsearch, and an encryption key in your kibana.yml file.` + ); return response.ok({ body: { isSufficientlySecure, @@ -89,6 +103,10 @@ export function enableAlertsRoute(_server: unknown, npRoute: RouteDependencies) ); } + server.log.info( + `Created ${createdAlerts.length} alerts for "${context.infra.spaceId}" space` + ); + return response.ok({ body: { createdAlerts, disabledWatcherClusterAlerts } }); } catch (err) { throw handleError(err); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts index 4d36e36d7cd84..5cc7046777e26 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts @@ -259,7 +259,7 @@ export function ccrRoute(server: { }; }, {}) ?? {}; - const buckets = response.aggregations.by_follower_index.buckets; + const buckets = response.aggregations?.by_follower_index.buckets ?? []; const data = buckets.reduce((accum: any, bucket: any) => { const leaderIndex = get(bucket, 'leader_index.buckets[0].key'); const remoteCluster = get( diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 799214a2931ed..3dcf6862b7232 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -25,7 +25,7 @@ import { PluginStartContract as AlertingPluginStartContract, PluginSetupContract as AlertingPluginSetupContract, } from '../../alerting/server'; -import { InfraPluginSetup } from '../../infra/server'; +import { InfraPluginSetup, InfraRequestHandlerContext } from '../../infra/server'; import { LicensingPluginStart } from '../../licensing/server'; import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; @@ -57,6 +57,7 @@ export interface PluginsSetup { export interface RequestHandlerContextMonitoringPlugin extends RequestHandlerContext { actions?: ActionsApiRequestHandlerContext; alerting?: AlertingApiRequestHandlerContext; + infra: InfraRequestHandlerContext; } export interface PluginsStart { diff --git a/x-pack/plugins/observability/common/rules/observability_rule_field_map.ts b/x-pack/plugins/observability/common/rules/observability_rule_field_map.ts deleted file mode 100644 index 370f5d4ef79f2..0000000000000 --- a/x-pack/plugins/observability/common/rules/observability_rule_field_map.ts +++ /dev/null @@ -1,22 +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 { ecsFieldMap, pickWithPatterns } from '../../../rule_registry/common'; - -export const observabilityRuleFieldMap = { - ...pickWithPatterns(ecsFieldMap, 'host.name', 'service.name'), - 'kibana.observability.evaluation.value': { - type: 'scaled_float' as const, - scaling_factor: 1000, - }, - 'kibana.observability.evaluation.threshold': { - type: 'scaled_float' as const, - scaling_factor: 1000, - }, -}; - -export type ObservabilityRuleFieldMap = typeof observabilityRuleFieldMap; diff --git a/x-pack/plugins/observability/common/utils/formatters/duration.ts b/x-pack/plugins/observability/common/utils/formatters/duration.ts index 6bbeb44ef06af..481005332cc30 100644 --- a/x-pack/plugins/observability/common/utils/formatters/duration.ts +++ b/x-pack/plugins/observability/common/utils/formatters/duration.ts @@ -201,6 +201,9 @@ export function asDuration( const formatter = getDurationFormatter(value); return formatter(value, { defaultValue, extended }).formatted; } + +export type AsDuration = typeof asDuration; + /** * Convert a microsecond value to decimal milliseconds. Normally we use * `asDuration`, but this is used in places like tables where we always want diff --git a/x-pack/plugins/observability/common/utils/formatters/formatters.ts b/x-pack/plugins/observability/common/utils/formatters/formatters.ts index 3c307f64fa0a9..9bdccc7e9edfe 100644 --- a/x-pack/plugins/observability/common/utils/formatters/formatters.ts +++ b/x-pack/plugins/observability/common/utils/formatters/formatters.ts @@ -47,6 +47,8 @@ export function asPercent( return numeral(decimal).format('0.0%'); } +export type AsPercent = typeof asPercent; + export function asDecimalOrInteger(value: number) { // exact 0 or above 10 should not have decimal if (value === 0 || value >= 10) { diff --git a/x-pack/plugins/observability/kibana.json b/x-pack/plugins/observability/kibana.json index 0ee978c75d6c0..52d5493ae69a4 100644 --- a/x-pack/plugins/observability/kibana.json +++ b/x-pack/plugins/observability/kibana.json @@ -15,7 +15,8 @@ "requiredPlugins": [ "data", "alerting", - "ruleRegistry" + "ruleRegistry", + "triggersActionsUi" ], "ui": true, "server": true, diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx index c0b51652a7d0e..9182a0e8196c8 100644 --- a/x-pack/plugins/observability/public/application/application.test.tsx +++ b/x-pack/plugins/observability/public/application/application.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { Observable } from 'rxjs'; import { AppMountParameters, CoreStart } from 'src/core/public'; import { ObservabilityPublicPluginsStart } from '../plugin'; -import { createObservabilityRuleRegistryMock } from '../rules/observability_rule_registry_mock'; +import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; import { renderApp } from './'; describe('renderApp', () => { @@ -58,7 +58,7 @@ describe('renderApp', () => { core, plugins, appMountParameters: params, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), }); unmount(); }).not.toThrowError(); diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 8607b57b42666..460aa6c35bdb8 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -18,11 +18,12 @@ import { import { PluginContext } from '../context/plugin_context'; import { usePluginContext } from '../hooks/use_plugin_context'; import { useRouteParams } from '../hooks/use_route_params'; -import { ObservabilityPublicPluginsStart, ObservabilityRuleRegistry } from '../plugin'; +import { ObservabilityPublicPluginsStart } from '../plugin'; import { HasDataContextProvider } from '../context/has_data_context'; import { Breadcrumbs, routes } from '../routes'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { ConfigSchema } from '..'; +import { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry'; function getTitleFromBreadCrumbs(breadcrumbs: Breadcrumbs) { return breadcrumbs.map(({ text }) => text).reverse(); @@ -72,12 +73,12 @@ export const renderApp = ({ core, plugins, appMountParameters, - observabilityRuleRegistry, + observabilityRuleTypeRegistry, }: { config: ConfigSchema; core: CoreStart; plugins: ObservabilityPublicPluginsStart; - observabilityRuleRegistry: ObservabilityRuleRegistry; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; appMountParameters: AppMountParameters; }) => { const { element, history } = appMountParameters; @@ -94,7 +95,7 @@ export const renderApp = ({ ReactDOM.render( diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx index d41f131ef521b..e2669d87d6776 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx @@ -14,7 +14,8 @@ import * as hasDataHook from '../../../../hooks/use_has_data'; import * as pluginContext from '../../../../hooks/use_plugin_context'; import { HasDataContextValue } from '../../../../context/has_data_context'; import { AppMountParameters, CoreStart } from 'kibana/public'; -import { ObservabilityPublicPluginsStart, ObservabilityRuleRegistry } from '../../../../plugin'; +import { ObservabilityPublicPluginsStart } from '../../../../plugin'; +import { createObservabilityRuleTypeRegistryMock } from '../../../../rules/observability_rule_type_registry_mock'; jest.mock('react-router-dom', () => ({ useLocation: () => ({ @@ -41,10 +42,7 @@ describe('APMSection', () => { } as unknown) as CoreStart, appMountParameters: {} as AppMountParameters, config: { unsafe: { alertingExperience: { enabled: true } } }, - observabilityRuleRegistry: ({ - registerType: jest.fn(), - getTypeByRuleId: jest.fn(), - } as unknown) as ObservabilityRuleRegistry, + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), plugins: ({ data: { query: { diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx index fa4d1a744e3ea..b4227cc122dde 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx @@ -7,7 +7,6 @@ import { AppMountParameters, CoreStart } from 'kibana/public'; import React from 'react'; -import { createObservabilityRuleRegistryMock } from '../../../../rules/observability_rule_registry_mock'; import { HasDataContextValue } from '../../../../context/has_data_context'; import * as fetcherHook from '../../../../hooks/use_fetcher'; import * as hasDataHook from '../../../../hooks/use_has_data'; @@ -16,6 +15,7 @@ import { ObservabilityPublicPluginsStart } from '../../../../plugin'; import { render } from '../../../../utils/test_helper'; import { UXSection } from './'; import { response } from './mock_data/ux.mock'; +import { createObservabilityRuleTypeRegistryMock } from '../../../../rules/observability_rule_type_registry_mock'; jest.mock('react-router-dom', () => ({ useLocation: () => ({ @@ -55,7 +55,7 @@ describe('UXSection', () => { }, }, } as unknown) as ObservabilityPublicPluginsStart, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), })); }); it('renders with core web vitals', () => { diff --git a/x-pack/plugins/observability/public/context/plugin_context.tsx b/x-pack/plugins/observability/public/context/plugin_context.tsx index eea259b36d5b9..9b0bacc4c1174 100644 --- a/x-pack/plugins/observability/public/context/plugin_context.tsx +++ b/x-pack/plugins/observability/public/context/plugin_context.tsx @@ -7,15 +7,16 @@ import { createContext } from 'react'; import { AppMountParameters, CoreStart } from 'kibana/public'; -import { ObservabilityPublicPluginsStart, ObservabilityRuleRegistry } from '../plugin'; +import { ObservabilityPublicPluginsStart } from '../plugin'; import { ConfigSchema } from '..'; +import { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry'; export interface PluginContextValue { appMountParameters: AppMountParameters; config: ConfigSchema; core: CoreStart; plugins: ObservabilityPublicPluginsStart; - observabilityRuleRegistry: ObservabilityRuleRegistry; + observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; } export const PluginContext = createContext({} as PluginContextValue); diff --git a/x-pack/plugins/observability/public/hooks/use_time_range.test.ts b/x-pack/plugins/observability/public/hooks/use_time_range.test.ts index 43fbc18062b75..8808d6390e365 100644 --- a/x-pack/plugins/observability/public/hooks/use_time_range.test.ts +++ b/x-pack/plugins/observability/public/hooks/use_time_range.test.ts @@ -10,7 +10,7 @@ import * as pluginContext from './use_plugin_context'; import { AppMountParameters, CoreStart } from 'kibana/public'; import { ObservabilityPublicPluginsStart } from '../plugin'; import * as kibanaUISettings from './use_kibana_ui_settings'; -import { createObservabilityRuleRegistryMock } from '../rules/observability_rule_registry_mock'; +import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; jest.mock('react-router-dom', () => ({ useLocation: () => ({ @@ -39,7 +39,7 @@ describe('useTimeRange', () => { }, }, } as unknown) as ObservabilityPublicPluginsStart, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), })); jest.spyOn(kibanaUISettings, 'useKibanaUISettings').mockImplementation(() => ({ from: '2020-10-08T05:00:00.000Z', @@ -81,7 +81,7 @@ describe('useTimeRange', () => { }, }, } as unknown) as ObservabilityPublicPluginsStart, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), })); }); it('returns ranges and absolute times from kibana default settings', () => { diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index a011d1fc2c414..8dd2f6a57eefe 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -62,4 +62,5 @@ export { getApmTraceUrl } from './utils/get_apm_trace_url'; export { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/utils'; export type { SeriesUrl } from './components/shared/exploratory_view/types'; -export { FormatterRuleRegistry } from './rules/formatter_rule_registry'; +export type { ObservabilityRuleTypeRegistry } from './rules/create_observability_rule_type_registry'; +export { createObservabilityRuleTypeRegistryMock } from './rules/observability_rule_type_registry_mock'; diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.stories.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.stories.tsx index 6940f6aaad692..0d47f3da89d36 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.stories.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.stories.tsx @@ -13,7 +13,7 @@ import { AlertsPage } from '.'; import { HttpSetup } from '../../../../../../src/core/public'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { PluginContext, PluginContextValue } from '../../context/plugin_context'; -import { createObservabilityRuleRegistryMock } from '../../rules/observability_rule_registry_mock'; +import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock'; import { createCallObservabilityApi } from '../../services/call_observability_api'; import type { ObservabilityAPIReturnType } from '../../services/call_observability_api/types'; import { apmAlertResponseExample, dynamicIndexPattern } from './example_data'; @@ -62,7 +62,7 @@ export default { core: { http: { basePath: { prepend: (_: string) => '' } }, }, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), } as unknown) as PluginContextValue } > diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/alerts_flyout.stories.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/alerts_flyout.stories.tsx index 96d3c1fc9c390..90c75a70c0813 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/alerts_flyout.stories.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/alerts_flyout.stories.tsx @@ -62,25 +62,26 @@ Example.args = { reason: 'Error count for opbeans-java was above the threshold', active: true, start: 1618235449493, - - 'rule.id': 'apm.error_rate', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'rule.name': 'Error count threshold | opbeans-java (smith test)', - 'kibana.rac.alert.duration.us': 61787000, - 'kibana.observability.evaluation.threshold': 0, - 'kibana.rac.alert.status': 'open', - tags: ['apm', 'service.name:opbeans-java'], - 'kibana.rac.alert.uuid': 'c50fbc70-0d77-462d-ac0a-f2bd0b8512e4', - 'rule.uuid': '474920d0-93e9-11eb-ac86-0b455460de81', - 'event.action': 'active', - '@timestamp': '2021-04-14T21:43:42.966Z', - 'kibana.rac.alert.id': 'apm.error_rate_opbeans-java_production', - 'processor.event': 'error', - 'kibana.rac.alert.start': '2021-04-14T21:42:41.179Z', - 'kibana.rac.producer': 'apm', - 'event.kind': 'state', - 'rule.category': 'Error count threshold', - 'kibana.observability.evaluation.value': 1, + fields: { + 'rule.id': 'apm.error_rate', + 'service.environment': ['production'], + 'service.name': ['opbeans-java'], + 'rule.name': 'Error count threshold | opbeans-java (smith test)', + 'kibana.rac.alert.duration.us': 61787000, + 'kibana.rac.alert.evaluation.threshold': 0, + 'kibana.rac.alert.status': 'open', + tags: ['apm', 'service.name:opbeans-java'], + 'kibana.rac.alert.uuid': 'c50fbc70-0d77-462d-ac0a-f2bd0b8512e4', + 'rule.uuid': '474920d0-93e9-11eb-ac86-0b455460de81', + 'event.action': 'active', + '@timestamp': '2021-04-14T21:43:42.966Z', + 'kibana.rac.alert.id': 'apm.error_rate_opbeans-java_production', + 'processor.event': ['error'], + 'kibana.rac.alert.start': '2021-04-14T21:42:41.179Z', + 'kibana.rac.producer': 'apm', + 'event.kind': 'state', + 'rule.category': 'Error count threshold', + 'kibana.rac.alert.evaluation.value': 1, + }, }, } as Args; diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx index 892274b2fb8b0..61bd5c67b97ee 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx @@ -16,17 +16,24 @@ import { EuiFlyoutHeader, EuiFlyoutProps, EuiSpacer, - EuiTabbedContent, EuiText, EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; import React from 'react'; +import { + ALERT_DURATION, + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_SEVERITY_LEVEL, + RULE_CATEGORY, + RULE_NAME, +} from '../../../../../rule_registry/common/technical_rule_data_field_names'; +import { TopAlert } from '../'; import { useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; import { asDuration } from '../../../../common/utils/formatters'; import { usePluginContext } from '../../../hooks/use_plugin_context'; -import { TopAlert } from '../'; import { SeverityBadge } from '../severity_badge'; type AlertsFlyoutProps = { alert: TopAlert } & EuiFlyoutProps; @@ -47,7 +54,7 @@ export function AlertsFlyout({ onClose, alert }: AlertsFlyoutProps) { title: i18n.translate('xpack.observability.alertsFlyout.severityLabel', { defaultMessage: 'Severity', }), - description: , + description: , }, { title: i18n.translate('xpack.observability.alertsFlyout.triggeredLabel', { @@ -61,54 +68,44 @@ export function AlertsFlyout({ onClose, alert }: AlertsFlyoutProps) { title: i18n.translate('xpack.observability.alertsFlyout.durationLabel', { defaultMessage: 'Duration', }), - description: asDuration(alert['kibana.rac.alert.duration.us'], { extended: true }), + description: asDuration(alert.fields[ALERT_DURATION], { extended: true }), }, { title: i18n.translate('xpack.observability.alertsFlyout.expectedValueLabel', { defaultMessage: 'Expected value', }), - description: alert['kibana.observability.evaluation.threshold'] ?? '-', + description: alert.fields[ALERT_EVALUATION_THRESHOLD] ?? '-', }, { title: i18n.translate('xpack.observability.alertsFlyout.actualValueLabel', { defaultMessage: 'Actual value', }), - description: alert['kibana.observability.evaluation.value'] ?? '-', + description: alert.fields[ALERT_EVALUATION_VALUE] ?? '-', }, { title: i18n.translate('xpack.observability.alertsFlyout.ruleTypeLabel', { defaultMessage: 'Rule type', }), - description: alert['rule.category'] ?? '-', - }, - ]; - - const tabs = [ - { - id: 'overview', - name: i18n.translate('xpack.observability.alerts.flyoutOverviewTabTitle', { - defaultMessage: 'Overview', - }), - content: ( - <> - - - - ), + description: alert.fields[RULE_CATEGORY] ?? '-', }, ]; return ( - + -

{alert['rule.name']}

+

{alert.fields[RULE_NAME]}

{alert.reason}
- + + {alert.link && ( diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_table.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_table.tsx index b0ff156fde377..5a7317da95f88 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_table.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_table.tsx @@ -16,6 +16,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; +import { + ALERT_DURATION, + ALERT_SEVERITY_LEVEL, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; import { asDuration } from '../../../common/utils/formatters'; import { TimestampTooltip } from '../../components/shared/timestamp_tooltip'; import { usePluginContext } from '../../hooks/use_plugin_context'; @@ -94,9 +98,7 @@ export function AlertsTable(props: AlertsTableProps) { }), render: (_, alert) => { const { active } = alert; - return active - ? null - : asDuration(alert['kibana.rac.alert.duration.us'], { extended: true }); + return active ? null : asDuration(alert.fields[ALERT_DURATION], { extended: true }); }, }, { @@ -105,7 +107,7 @@ export function AlertsTable(props: AlertsTableProps) { defaultMessage: 'Severity', }), render: (_, alert) => { - return ; + return ; }, }, { diff --git a/x-pack/plugins/observability/public/pages/alerts/example_data.ts b/x-pack/plugins/observability/public/pages/alerts/example_data.ts index dba6f1e9aaa2f..5318fce82c1d3 100644 --- a/x-pack/plugins/observability/public/pages/alerts/example_data.ts +++ b/x-pack/plugins/observability/public/pages/alerts/example_data.ts @@ -7,42 +7,42 @@ export const apmAlertResponseExample = [ { - 'rule.id': 'apm.error_rate', - 'service.name': 'opbeans-java', - 'rule.name': 'Error count threshold | opbeans-java (smith test)', - 'kibana.rac.alert.duration.us': 180057000, - 'kibana.rac.alert.status': 'open', - 'kibana.rac.alert.severity.level': 'warning', + 'rule.id': ['apm.error_rate'], + 'service.name': ['opbeans-java'], + 'rule.name': ['Error count threshold | opbeans-java (smith test)'], + 'kibana.rac.alert.duration.us': [180057000], + 'kibana.rac.alert.status': ['open'], + 'kibana.rac.alert.severity.level': ['warning'], tags: ['apm', 'service.name:opbeans-java'], - 'kibana.rac.alert.uuid': '0175ec0a-a3b1-4d41-b557-e21c2d024352', - 'rule.uuid': '474920d0-93e9-11eb-ac86-0b455460de81', - 'event.action': 'active', - '@timestamp': '2021-04-12T13:53:49.550Z', - 'kibana.rac.alert.id': 'apm.error_rate_opbeans-java_production', - 'kibana.rac.alert.start': '2021-04-12T13:50:49.493Z', - 'kibana.rac.producer': 'apm', - 'event.kind': 'state', - 'rule.category': 'Error count threshold', + 'kibana.rac.alert.uuid': ['0175ec0a-a3b1-4d41-b557-e21c2d024352'], + 'rule.uuid': ['474920d0-93e9-11eb-ac86-0b455460de81'], + 'event.action': ['active'], + '@timestamp': ['2021-04-12T13:53:49.550Z'], + 'kibana.rac.alert.id': ['apm.error_rate_opbeans-java_production'], + 'kibana.rac.alert.start': ['2021-04-12T13:50:49.493Z'], + 'kibana.rac.producer': ['apm'], + 'event.kind': ['state'], + 'rule.category': ['Error count threshold'], 'service.environment': ['production'], 'processor.event': ['error'], }, { - 'rule.id': 'apm.error_rate', - 'service.name': 'opbeans-java', - 'rule.name': 'Error count threshold | opbeans-java (smith test)', - 'kibana.rac.alert.duration.us': 2419005000, - 'kibana.rac.alert.end': '2021-04-12T13:49:49.446Z', - 'kibana.rac.alert.status': 'closed', + 'rule.id': ['apm.error_rate'], + 'service.name': ['opbeans-java'], + 'rule.name': ['Error count threshold | opbeans-java (smith test)'], + 'kibana.rac.alert.duration.us': [2419005000], + 'kibana.rac.alert.end': ['2021-04-12T13:49:49.446Z'], + 'kibana.rac.alert.status': ['closed'], tags: ['apm', 'service.name:opbeans-java'], - 'kibana.rac.alert.uuid': '32b940e1-3809-4c12-8eee-f027cbb385e2', - 'rule.uuid': '474920d0-93e9-11eb-ac86-0b455460de81', - 'event.action': 'close', - '@timestamp': '2021-04-12T13:49:49.446Z', - 'kibana.rac.alert.id': 'apm.error_rate_opbeans-java_production', - 'kibana.rac.alert.start': '2021-04-12T13:09:30.441Z', - 'kibana.rac.producer': 'apm', - 'event.kind': 'state', - 'rule.category': 'Error count threshold', + 'kibana.rac.alert.uuid': ['32b940e1-3809-4c12-8eee-f027cbb385e2'], + 'rule.uuid': ['474920d0-93e9-11eb-ac86-0b455460de81'], + 'event.action': ['close'], + '@timestamp': ['2021-04-12T13:49:49.446Z'], + 'kibana.rac.alert.id': ['apm.error_rate_opbeans-java_production'], + 'kibana.rac.alert.start': ['2021-04-12T13:09:30.441Z'], + 'kibana.rac.producer': ['apm'], + 'event.kind': ['state'], + 'rule.category': ['Error count threshold'], 'service.environment': ['production'], 'processor.event': ['error'], }, diff --git a/x-pack/plugins/observability/public/pages/alerts/index.tsx b/x-pack/plugins/observability/public/pages/alerts/index.tsx index 76e5d62369029..6df3f915ae690 100644 --- a/x-pack/plugins/observability/public/pages/alerts/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/index.tsx @@ -11,27 +11,37 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, - EuiPage, - EuiPageHeader, + EuiPageTemplate, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useHistory } from 'react-router-dom'; import { format, parse } from 'url'; -import type { ObservabilityAPIReturnType } from '../../services/call_observability_api/types'; +import { + ParsedTechnicalFields, + parseTechnicalFields, +} from '../../../../rule_registry/common/parse_technical_fields'; +import { + ALERT_START, + EVENT_ACTION, + RULE_ID, + RULE_NAME, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { asDuration, asPercent } from '../../../common/utils/formatters'; import { ExperimentalBadge } from '../../components/shared/experimental_badge'; import { useFetcher } from '../../hooks/use_fetcher'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { RouteParams } from '../../routes'; import { callObservabilityApi } from '../../services/call_observability_api'; +import type { ObservabilityAPIReturnType } from '../../services/call_observability_api/types'; import { getAbsoluteDateRange } from '../../utils/date'; -import { asDuration, asPercent } from '../../../common/utils/formatters'; import { AlertsSearchBar } from './alerts_search_bar'; import { AlertsTable } from './alerts_table'; export type TopAlertResponse = ObservabilityAPIReturnType<'GET /api/observability/rules/alerts/top'>[number]; -export interface TopAlert extends TopAlertResponse { +export interface TopAlert { + fields: ParsedTechnicalFields; start: number; reason: string; link?: string; @@ -43,7 +53,7 @@ interface AlertsPageProps { } export function AlertsPage({ routeParams }: AlertsPageProps) { - const { core, observabilityRuleRegistry } = usePluginContext(); + const { core, observabilityRuleTypeRegistry } = usePluginContext(); const { prepend } = core.http.basePath; const history = useHistory(); const { @@ -75,18 +85,19 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { }, }).then((alerts) => { return alerts.map((alert) => { - const ruleType = observabilityRuleRegistry.getTypeByRuleId(alert['rule.id']); + const parsedFields = parseTechnicalFields(alert); + const formatter = observabilityRuleTypeRegistry.getFormatter(parsedFields[RULE_ID]!); const formatted = { link: undefined, - reason: alert['rule.name'], - ...(ruleType?.format?.({ alert, formatters: { asDuration, asPercent } }) ?? {}), + reason: parsedFields[RULE_NAME]!, + ...(formatter?.({ fields: parsedFields, formatters: { asDuration, asPercent } }) ?? {}), }; const parsedLink = formatted.link ? parse(formatted.link, true) : undefined; return { - ...alert, ...formatted, + fields: parsedFields, link: parsedLink ? format({ ...parsedLink, @@ -97,82 +108,81 @@ export function AlertsPage({ routeParams }: AlertsPageProps) { }, }) : undefined, - active: alert['event.action'] !== 'close', - start: new Date(alert['kibana.rac.alert.start']).getTime(), + active: parsedFields[EVENT_ACTION] !== 'close', + start: new Date(parsedFields[ALERT_START]!).getTime(), }; }); }); }, - [kuery, observabilityRuleRegistry, rangeFrom, rangeTo] + [kuery, observabilityRuleTypeRegistry, rangeFrom, rangeTo] ); return ( - - {i18n.translate('xpack.observability.alertsTitle', { defaultMessage: 'Alerts' })}{' '} - } - rightSideItems={[ + ), + + rightSideItems: [ {i18n.translate('xpack.observability.alerts.manageDetectionRulesButtonLabel', { defaultMessage: 'Manage detection rules', })} , - ]} - > - - - + + + +

+ {i18n.translate('xpack.observability.alertsDisclaimerText', { + defaultMessage: + 'This page shows an experimental alerting view. The data shown here will probably not be an accurate representation of alerts. A non-experimental list of alerts is available in the Alerts and Actions settings in Stack Management.', })} - color="warning" - iconType="beaker" - > -

- {i18n.translate('xpack.observability.alertsDisclaimerText', { - defaultMessage: - 'This page shows an experimental alerting view. The data shown here will probably not be an accurate representation of alerts. A non-experimental list of alerts is available in the Alerts and Actions settings in Stack Management.', +

+

+ + {i18n.translate('xpack.observability.alertsDisclaimerLinkText', { + defaultMessage: 'Alerts and Actions', })} -

-

- - {i18n.translate('xpack.observability.alertsDisclaimerLinkText', { - defaultMessage: 'Alerts and Actions', - })} - -

-
-
- - { - const nextSearchParams = new URLSearchParams(history.location.search); + +

+
+
+ + { + const nextSearchParams = new URLSearchParams(history.location.search); - nextSearchParams.set('rangeFrom', dateRange.from); - nextSearchParams.set('rangeTo', dateRange.to); - nextSearchParams.set('kuery', query ?? ''); + nextSearchParams.set('rangeFrom', dateRange.from); + nextSearchParams.set('rangeTo', dateRange.to); + nextSearchParams.set('kuery', query ?? ''); - history.push({ - ...history.location, - search: nextSearchParams.toString(), - }); - }} - /> - - - - -
-
-
+ history.push({ + ...history.location, + search: nextSearchParams.toString(), + }); + }} + /> + + + + + + ); } diff --git a/x-pack/plugins/observability/public/pages/cases/index.tsx b/x-pack/plugins/observability/public/pages/cases/index.tsx index 4af5de274dd65..dd7f7875b568e 100644 --- a/x-pack/plugins/observability/public/pages/cases/index.tsx +++ b/x-pack/plugins/observability/public/pages/cases/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageHeader } from '@elastic/eui'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiPageTemplate } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { ExperimentalBadge } from '../../components/shared/experimental_badge'; @@ -17,33 +17,33 @@ interface CasesProps { export function CasesPage(props: CasesProps) { return ( - - {i18n.translate('xpack.observability.casesTitle', { defaultMessage: 'Cases' })}{' '} - } - > - - - + + + +

+ {i18n.translate('xpack.observability.casesDisclaimerText', { + defaultMessage: 'This is the future home of cases.', })} - color="warning" - iconType="beaker" - > -

- {i18n.translate('xpack.observability.casesDisclaimerText', { - defaultMessage: 'This is the future home of cases.', - })} -

-
-
-
-
-
+

+ + + + ); } diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index 559aa8d5884a9..ebd1c73859169 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -23,7 +23,7 @@ import { emptyResponse as emptyLogsResponse, fetchLogsData } from './mock/logs.m import { emptyResponse as emptyMetricsResponse, fetchMetricsData } from './mock/metrics.mock'; import { newsFeedFetchData } from './mock/news_feed.mock'; import { emptyResponse as emptyUptimeResponse, fetchUptimeData } from './mock/uptime.mock'; -import { createObservabilityRuleRegistryMock } from '../../rules/observability_rule_registry_mock'; +import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock'; function unregisterAll() { unregisterDataHandler({ appName: 'apm' }); @@ -54,7 +54,7 @@ const withCore = makeDecorator({ }, }, } as unknown) as ObservabilityPublicPluginsStart, - observabilityRuleRegistry: createObservabilityRuleRegistryMock(), + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), }} > diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 517675fe1d525..6856bc97b4a36 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -7,6 +7,10 @@ import { i18n } from '@kbn/i18n'; import { BehaviorSubject } from 'rxjs'; +import { + TriggersAndActionsUIPublicPluginSetup, + TriggersAndActionsUIPublicPluginStart, +} from '../../triggers_actions_ui/public'; import { AppMountParameters, AppUpdater, @@ -25,26 +29,23 @@ import type { HomePublicPluginStart, } from '../../../../src/plugins/home/public'; import type { LensPublicStart } from '../../lens/public'; -import type { RuleRegistryPublicPluginSetupContract } from '../../rule_registry/public'; -import type { ObservabilityRuleFieldMap } from '../common/rules/observability_rule_field_map'; -import { observabilityRuleRegistrySettings } from '../common/rules/observability_rule_registry_settings'; import { registerDataHandler } from './data_handler'; -import { FormatterRuleRegistry } from './rules/formatter_rule_registry'; import { createCallObservabilityApi } from './services/call_observability_api'; import { toggleOverviewLinkInNav } from './toggle_overview_link_in_nav'; import { ConfigSchema } from '.'; +import { createObservabilityRuleTypeRegistry } from './rules/create_observability_rule_type_registry'; export type ObservabilityPublicSetup = ReturnType; -export type ObservabilityRuleRegistry = ObservabilityPublicSetup['ruleRegistry']; export interface ObservabilityPublicPluginsSetup { data: DataPublicPluginSetup; - ruleRegistry: RuleRegistryPublicPluginSetupContract; + triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; home?: HomePublicPluginSetup; } export interface ObservabilityPublicPluginsStart { home?: HomePublicPluginStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; data: DataPublicPluginStart; lens: LensPublicStart; } @@ -75,11 +76,9 @@ export class Plugin createCallObservabilityApi(coreSetup.http); - const observabilityRuleRegistry = pluginsSetup.ruleRegistry.registry.create({ - ...observabilityRuleRegistrySettings, - fieldMap: {} as ObservabilityRuleFieldMap, - ctor: FormatterRuleRegistry, - }); + const observabilityRuleTypeRegistry = createObservabilityRuleTypeRegistry( + pluginsSetup.triggersActionsUi.alertTypeRegistry + ); const mount = async (params: AppMountParameters) => { // Load application bundle @@ -92,7 +91,7 @@ export class Plugin core: coreStart, plugins: pluginsStart, appMountParameters: params, - observabilityRuleRegistry, + observabilityRuleTypeRegistry, }); }; @@ -165,7 +164,7 @@ export class Plugin return { dashboard: { register: registerDataHandler }, - ruleRegistry: observabilityRuleRegistry, + observabilityRuleTypeRegistry, isAlertingExperienceEnabled: () => config.unsafe.alertingExperience.enabled, }; } diff --git a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts new file mode 100644 index 0000000000000..cba9df83c6fe3 --- /dev/null +++ b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts @@ -0,0 +1,31 @@ +/* + * 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 { AlertTypeModel, AlertTypeRegistryContract } from '../../../triggers_actions_ui/public'; +import { ParsedTechnicalFields } from '../../../rule_registry/common/parse_technical_fields'; +import { AsDuration, AsPercent } from '../../common/utils/formatters'; + +type Formatter = (options: { + fields: ParsedTechnicalFields & Record; + formatters: { asDuration: AsDuration; asPercent: AsPercent }; +}) => { reason: string; link: string }; + +export function createObservabilityRuleTypeRegistry(alertTypeRegistry: AlertTypeRegistryContract) { + const formatters: Array<{ typeId: string; fn: Formatter }> = []; + return { + register: (type: AlertTypeModel & { format: Formatter }) => { + const { format, ...rest } = type; + formatters.push({ typeId: type.id, fn: format }); + alertTypeRegistry.register(rest); + }, + getFormatter: (typeId: string) => { + return formatters.find((formatter) => formatter.typeId === typeId)?.fn; + }, + }; +} + +export type ObservabilityRuleTypeRegistry = ReturnType; diff --git a/x-pack/plugins/observability/public/rules/formatter_rule_registry.ts b/x-pack/plugins/observability/public/rules/formatter_rule_registry.ts deleted file mode 100644 index 0d0d22cf750fb..0000000000000 --- a/x-pack/plugins/observability/public/rules/formatter_rule_registry.ts +++ /dev/null @@ -1,30 +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 { RuleType } from '../../../rule_registry/public'; -import type { BaseRuleFieldMap, OutputOfFieldMap } from '../../../rule_registry/common'; -import { RuleRegistry } from '../../../rule_registry/public'; -import type { asDuration, asPercent } from '../../common/utils/formatters'; - -type AlertTypeOf = OutputOfFieldMap; - -type FormattableRuleType = RuleType & { - format?: (options: { - alert: AlertTypeOf; - formatters: { - asDuration: typeof asDuration; - asPercent: typeof asPercent; - }; - }) => { - reason?: string; - link?: string; - }; -}; - -export class FormatterRuleRegistry extends RuleRegistry< - TFieldMap, - FormattableRuleType -> {} diff --git a/x-pack/plugins/observability/public/rules/observability_rule_registry_mock.ts b/x-pack/plugins/observability/public/rules/observability_rule_registry_mock.ts deleted file mode 100644 index 389b581b5fb60..0000000000000 --- a/x-pack/plugins/observability/public/rules/observability_rule_registry_mock.ts +++ /dev/null @@ -1,17 +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 { ObservabilityRuleRegistry } from '../plugin'; - -const createRuleRegistryMock = () => ({ - registerType: () => {}, - getTypeByRuleId: () => ({ format: () => ({ link: '/test/example' }) }), - create: () => createRuleRegistryMock(), -}); - -export const createObservabilityRuleRegistryMock = () => - createRuleRegistryMock() as ObservabilityRuleRegistry & ReturnType; diff --git a/x-pack/plugins/observability/public/rules/observability_rule_type_registry_mock.ts b/x-pack/plugins/observability/public/rules/observability_rule_type_registry_mock.ts new file mode 100644 index 0000000000000..b2cf48f8e1c32 --- /dev/null +++ b/x-pack/plugins/observability/public/rules/observability_rule_type_registry_mock.ts @@ -0,0 +1,16 @@ +/* + * 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 { ObservabilityRuleTypeRegistry } from './create_observability_rule_type_registry'; + +const createRuleTypeRegistryMock = () => ({ + registerFormatter: () => {}, +}); + +export const createObservabilityRuleTypeRegistryMock = () => + createRuleTypeRegistryMock() as ObservabilityRuleTypeRegistry & + ReturnType; diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index 63e34b018aed0..ef7c62a143f25 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -15,7 +15,7 @@ import translations from '../../../translations/translations/ja-JP.json'; import { PluginContext } from '../context/plugin_context'; import { ObservabilityPublicPluginsStart } from '../plugin'; import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; -import { createObservabilityRuleRegistryMock } from '../rules/observability_rule_registry_mock'; +import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; const appMountParameters = ({ setHeaderActionMenu: () => {} } as unknown) as AppMountParameters; @@ -37,14 +37,14 @@ const plugins = ({ data: { query: { timefilter: { timefilter: { setTime: jest.fn() } } } }, } as unknown) as ObservabilityPublicPluginsStart; -const observabilityRuleRegistry = createObservabilityRuleRegistryMock(); +const observabilityRuleTypeRegistry = createObservabilityRuleTypeRegistryMock(); export const render = (component: React.ReactNode) => { return testLibRender( {component} diff --git a/x-pack/plugins/observability/server/lib/rules/get_top_alerts.ts b/x-pack/plugins/observability/server/lib/rules/get_top_alerts.ts index 0045c0f0c6757..52043efc506e8 100644 --- a/x-pack/plugins/observability/server/lib/rules/get_top_alerts.ts +++ b/x-pack/plugins/observability/server/lib/rules/get_top_alerts.ts @@ -4,24 +4,27 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { Required } from 'utility-types'; -import { ObservabilityRuleRegistryClient } from '../../types'; +import { + ALERT_UUID, + TIMESTAMP, +} from '../../../../rule_registry/common/technical_rule_data_field_names'; +import { RuleDataClient } from '../../../../rule_registry/server'; import { kqlQuery, rangeQuery } from '../../utils/queries'; export async function getTopAlerts({ - ruleRegistryClient, + ruleDataClient, start, end, kuery, size, }: { - ruleRegistryClient: ObservabilityRuleRegistryClient; + ruleDataClient: RuleDataClient; start: number; end: number; kuery?: string; size: number; }) { - const response = await ruleRegistryClient.search({ + const response = await ruleDataClient.getReader().search({ body: { query: { bool: { @@ -30,26 +33,18 @@ export async function getTopAlerts({ }, fields: ['*'], collapse: { - field: 'kibana.rac.alert.uuid', + field: ALERT_UUID, }, size, sort: { - '@timestamp': 'desc', + [TIMESTAMP]: 'desc', }, _source: false, }, + allow_no_indices: true, }); - return response.events.map((event) => { - return event as Required< - typeof event, - | 'rule.id' - | 'rule.name' - | 'kibana.rac.alert.start' - | 'event.action' - | 'rule.category' - | 'rule.name' - | 'kibana.rac.alert.duration.us' - >; + return response.hits.hits.map((hit) => { + return hit.fields; }); } diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index b5208260297d0..046a9a62d5fa7 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -6,6 +6,7 @@ */ import { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server'; +import { RuleDataClient } from '../../rule_registry/server'; import { ObservabilityConfig } from '.'; import { bootstrapAnnotations, @@ -16,11 +17,8 @@ import type { RuleRegistryPluginSetupContract } from '../../rule_registry/server import { uiSettings } from './ui_settings'; import { registerRoutes } from './routes/register_routes'; import { getGlobalObservabilityServerRouteRepository } from './routes/get_global_observability_server_route_repository'; -import { observabilityRuleRegistrySettings } from '../common/rules/observability_rule_registry_settings'; -import { observabilityRuleFieldMap } from '../common/rules/observability_rule_field_map'; export type ObservabilityPluginSetup = ReturnType; -export type ObservabilityRuleRegistry = ObservabilityPluginSetup['ruleRegistry']; export class ObservabilityPlugin implements Plugin { constructor(private readonly initContext: PluginInitializerContext) { @@ -51,19 +49,25 @@ export class ObservabilityPlugin implements Plugin { }); } - const observabilityRuleRegistry = plugins.ruleRegistry.create({ - ...observabilityRuleRegistrySettings, - fieldMap: observabilityRuleFieldMap, + const start = () => core.getStartServices().then(([coreStart]) => coreStart); + + const ruleDataClient = new RuleDataClient({ + getClusterClient: async () => { + const coreStart = await start(); + return coreStart.elasticsearch.client.asInternalUser; + }, + ready: () => Promise.resolve(), + alias: plugins.ruleRegistry.getFullAssetName(), }); registerRoutes({ core: { setup: core, - start: () => core.getStartServices().then(([coreStart]) => coreStart), + start, }, - ruleRegistry: observabilityRuleRegistry, logger: this.initContext.logger.get(), repository: getGlobalObservabilityServerRouteRepository(), + ruleDataClient, }); return { @@ -71,7 +75,6 @@ export class ObservabilityPlugin implements Plugin { const api = await annotationsApiPromise; return api?.getScopedAnnotationsClient(...args); }, - ruleRegistry: observabilityRuleRegistry, }; } diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index 85ee456b812b8..75b6703cc64de 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -13,23 +13,23 @@ import { import { CoreSetup, CoreStart, Logger, RouteRegistrar } from 'kibana/server'; import Boom from '@hapi/boom'; import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors'; -import { ObservabilityRuleRegistry } from '../plugin'; +import { RuleDataClient } from '../../../rule_registry/server'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; export function registerRoutes({ - ruleRegistry, repository, core, logger, + ruleDataClient, }: { core: { setup: CoreSetup; start: () => Promise; }; - ruleRegistry: ObservabilityRuleRegistry; repository: AbstractObservabilityServerRouteRepository; logger: Logger; + ruleDataClient: RuleDataClient; }) { const routes = repository.getRoutes(); @@ -59,10 +59,10 @@ export function registerRoutes({ const data = (await handler({ context, request, - ruleRegistry, core, logger, params: decodedParams, + ruleDataClient, })) as any; return response.ok({ body: data }); diff --git a/x-pack/plugins/observability/server/routes/rules.ts b/x-pack/plugins/observability/server/routes/rules.ts index cd3f4976e0af3..1f500adff5dcf 100644 --- a/x-pack/plugins/observability/server/routes/rules.ts +++ b/x-pack/plugins/observability/server/routes/rules.ts @@ -6,7 +6,6 @@ */ import * as t from 'io-ts'; import { isoToEpochRt, toNumberRt } from '@kbn/io-ts-utils'; -import Boom from '@hapi/boom'; import { createObservabilityServerRoute } from './create_observability_server_route'; import { createObservabilityServerRouteRepository } from './create_observability_server_route_repository'; import { getTopAlerts } from '../lib/rules/get_top_alerts'; @@ -28,22 +27,13 @@ const alertsListRoute = createObservabilityServerRoute({ }), ]), }), - handler: async ({ ruleRegistry, context, params }) => { - const ruleRegistryClient = await ruleRegistry.createScopedRuleRegistryClient({ - context, - alertsClient: context.alerting.getAlertsClient(), - }); - - if (!ruleRegistryClient) { - throw Boom.failedDependency('xpack.ruleRegistry.unsafe.write.enabled is set to false'); - } - + handler: async ({ ruleDataClient, context, params }) => { const { query: { start, end, kuery, size = 100 }, } = params; return getTopAlerts({ - ruleRegistryClient, + ruleDataClient, start, end, kuery, @@ -57,17 +47,10 @@ const alertsDynamicIndexPatternRoute = createObservabilityServerRoute({ options: { tags: [], }, - handler: async ({ ruleRegistry, context }) => { - const ruleRegistryClient = await ruleRegistry.createScopedRuleRegistryClient({ - context, - alertsClient: context.alerting.getAlertsClient(), - }); - - if (!ruleRegistryClient) { - throw Boom.failedDependency(); - } + handler: async ({ ruleDataClient }) => { + const reader = ruleDataClient.getReader({ namespace: 'observability' }); - return ruleRegistryClient.getDynamicIndexPattern(); + return reader.getDynamicIndexPattern(); }, }); diff --git a/x-pack/plugins/observability/server/routes/types.ts b/x-pack/plugins/observability/server/routes/types.ts index 0588bf8df2292..1fa7229c6cf62 100644 --- a/x-pack/plugins/observability/server/routes/types.ts +++ b/x-pack/plugins/observability/server/routes/types.ts @@ -12,7 +12,7 @@ import type { ServerRouteRepository, } from '@kbn/server-route-repository'; import { CoreSetup, CoreStart, KibanaRequest, Logger } from 'kibana/server'; -import { ObservabilityRuleRegistry } from '../plugin'; +import { RuleDataClient } from '../../../rule_registry/server'; import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository'; import { ObservabilityRequestHandlerContext } from '../types'; @@ -24,7 +24,7 @@ export interface ObservabilityRouteHandlerResources { start: () => Promise; setup: CoreSetup; }; - ruleRegistry: ObservabilityRuleRegistry; + ruleDataClient: RuleDataClient; request: KibanaRequest; context: ObservabilityRequestHandlerContext; logger: Logger; diff --git a/x-pack/plugins/observability/server/types.ts b/x-pack/plugins/observability/server/types.ts index 81b32b3f8db7b..da13e60804a60 100644 --- a/x-pack/plugins/observability/server/types.ts +++ b/x-pack/plugins/observability/server/types.ts @@ -7,9 +7,7 @@ import type { IRouter, RequestHandlerContext } from 'src/core/server'; import type { AlertingApiRequestHandlerContext } from '../../alerting/server'; -import type { ScopedRuleRegistryClient, FieldMapOf } from '../../rule_registry/server'; import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; -import type { ObservabilityRuleRegistry } from './plugin'; export type { ObservabilityRouteCreateOptions, @@ -31,7 +29,3 @@ export interface ObservabilityRequestHandlerContext extends RequestHandlerContex * @internal */ export type ObservabilityPluginRouter = IRouter; - -export type ObservabilityRuleRegistryClient = ScopedRuleRegistryClient< - FieldMapOf ->; diff --git a/x-pack/plugins/osquery/common/exact_check.ts b/x-pack/plugins/osquery/common/exact_check.ts index b10328a4db233..5334989ea085b 100644 --- a/x-pack/plugins/osquery/common/exact_check.ts +++ b/x-pack/plugins/osquery/common/exact_check.ts @@ -25,6 +25,7 @@ import { isObject, get } from 'lodash/fp'; * @param original The original to check if it has additional keys * @param decoded The decoded either which has either an existing error or the * decoded object which could have additional keys stripped from it. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts */ export const exactCheck = ( original: unknown, diff --git a/x-pack/plugins/osquery/common/format_errors.ts b/x-pack/plugins/osquery/common/format_errors.ts index 7b33612b4e887..16925699b0fcf 100644 --- a/x-pack/plugins/osquery/common/format_errors.ts +++ b/x-pack/plugins/osquery/common/format_errors.ts @@ -8,6 +8,9 @@ import * as t from 'io-ts'; import { isObject } from 'lodash/fp'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + */ export const formatErrors = (errors: t.Errors): string[] => { const err = errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/osquery/common/test_utils.ts b/x-pack/plugins/osquery/common/test_utils.ts index 84bf0d25057a2..dcf6a2747c3de 100644 --- a/x-pack/plugins/osquery/common/test_utils.ts +++ b/x-pack/plugins/osquery/common/test_utils.ts @@ -16,10 +16,16 @@ interface Message { schema: T | {}; } +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onLeft = (errors: t.Errors): Message => { return { errors, schema: {} }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onRight = (schema: T): Message => { return { errors: [], @@ -27,12 +33,16 @@ const onRight = (schema: T): Message => { }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ export const foldLeftRight = fold(onLeft, onRight); /** * Convenience utility to keep the error message handling within tests to be * very concise. * @param validation The validation to get the errors from + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts */ export const getPaths =
(validation: t.Validation): string[] => { return pipe( @@ -46,6 +56,7 @@ export const getPaths = (validation: t.Validation): string[] => { /** * Convenience utility to remove text appended to links by EUI + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts */ export const removeExternalLinkText = (str: string): string => str.replace(/\(opens in a new tab or window\)/g, ''); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/list/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/list/remote_clusters_list.test.js index e002652aa3aef..c91732019f79f 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/list/remote_clusters_list.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/list/remote_clusters_list.test.js @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { act } from 'react-dom/test-utils'; import { getRouter } from '../../../public/application/services'; @@ -16,6 +17,19 @@ import { setupEnvironment, getRandomString, findTestSubject } from '../helpers'; import { setup } from './remote_clusters_list.helpers'; +jest.mock('@elastic/eui/lib/components/search_bar/search_box', () => { + return { + EuiSearchBox: (props) => ( + { + props.onSearch(event.target.value); + }} + /> + ), + }; +}); + describe('', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); @@ -64,7 +78,6 @@ describe('', () => { }); describe('when there are multiple pages of remote clusters', () => { - let find; let table; let actions; let component; @@ -88,7 +101,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse(remoteClusters); await act(async () => { - ({ find, table, actions, form, component } = setup()); + ({ table, actions, component, form } = setup()); }); component.update(); @@ -103,9 +116,8 @@ describe('', () => { expect(tableCellsValues.length).toBe(10); }); - // Skipped until we can figure out how to get this test to work. - test.skip('search works', () => { - form.setInputValue(find('remoteClusterSearch'), 'unique'); + test('search works', () => { + form.setInputValue('remoteClusterSearch', 'unique'); const { tableCellsValues } = table.getMetaData('remoteClusterListTable'); expect(tableCellsValues.length).toBe(1); }); diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index bf0fc11e882cc..37c06c83f87e1 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -8,13 +8,17 @@ import { PROXY_MODE } from '../constants'; // Values returned from ES GET /_remote/info +/** + * TODO: This interface needs to be updated with values from {@link RemoteInfo} provided + * by the @elastic/elasticsearch client + */ export interface ClusterInfoEs { seeds?: string[]; mode?: 'proxy' | 'sniff'; connected?: boolean; num_nodes_connected?: number; - max_connections_per_cluster?: number; - initial_connect_timeout?: string; + max_connections_per_cluster?: string | number; + initial_connect_timeout: string | number; skip_unavailable?: boolean; transport?: { ping_schedule?: string; @@ -39,8 +43,8 @@ export interface Cluster { transportPingSchedule?: string; transportCompress?: boolean; connectedNodesCount?: number; - maxConnectionsPerCluster?: number; - initialConnectTimeout?: string; + maxConnectionsPerCluster?: string | number; + initialConnectTimeout?: string | number; connectedSocketsCount?: number; hasDeprecatedProxySetting?: boolean; } diff --git a/x-pack/plugins/remote_clusters/server/lib/does_cluster_exist.ts b/x-pack/plugins/remote_clusters/server/lib/does_cluster_exist.ts index 23cbebe8ac98e..f76854bead41d 100644 --- a/x-pack/plugins/remote_clusters/server/lib/does_cluster_exist.ts +++ b/x-pack/plugins/remote_clusters/server/lib/does_cluster_exist.ts @@ -5,9 +5,14 @@ * 2.0. */ -export async function doesClusterExist(callAsCurrentUser: any, clusterName: string): Promise { +import { IScopedClusterClient } from 'src/core/server'; + +export async function doesClusterExist( + clusterClient: IScopedClusterClient, + clusterName: string +): Promise { try { - const clusterInfoByName = await callAsCurrentUser('cluster.remoteInfo'); + const { body: clusterInfoByName } = await clusterClient.asCurrentUser.cluster.remoteInfo(); return Boolean(clusterInfoByName && clusterInfoByName[clusterName]); } catch (err) { throw new Error('Unable to check if cluster already exists.'); diff --git a/x-pack/plugins/remote_clusters/server/plugin.ts b/x-pack/plugins/remote_clusters/server/plugin.ts index 2b8d9afe979e8..01cb0fa892316 100644 --- a/x-pack/plugins/remote_clusters/server/plugin.ts +++ b/x-pack/plugins/remote_clusters/server/plugin.ts @@ -19,6 +19,8 @@ import { registerDeleteRoute, } from './routes/api'; +import { handleEsError } from './shared_imports'; + export interface RemoteClustersPluginSetup { isUiEnabled: boolean; } @@ -44,6 +46,9 @@ export class RemoteClustersServerPlugin config: { isCloudEnabled: Boolean(cloud?.isCloudEnabled), }, + lib: { + handleEsError, + }, }; features.registerElasticsearchFeature({ diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts index 9348fd1eb20df..b648554842a5c 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts @@ -5,10 +5,9 @@ * 2.0. */ +import { RequestHandler } from 'src/core/server'; + import { kibanaResponseFactory } from '../../../../../../src/core/server'; -import { register } from './add_route'; -import { API_BASE_PATH } from '../../../common/constants'; -import { LicenseStatus } from '../../types'; import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; @@ -19,6 +18,16 @@ import { coreMock, } from '../../../../../../src/core/server/mocks'; +import { API_BASE_PATH } from '../../../common/constants'; + +import { handleEsError } from '../../shared_imports'; + +import { register } from './add_route'; + +import { ScopedClusterClientMock } from './types'; + +const { createApiResponse } = elasticsearchServiceMock; + // Re-implement the mock that was imported directly from `x-pack/mocks` function createCoreRequestHandlerContextMock() { return { @@ -30,264 +39,235 @@ function createCoreRequestHandlerContextMock() { const xpackMocks = { createRequestHandlerContext: createCoreRequestHandlerContextMock, }; -interface TestOptions { - licenseCheckResult?: LicenseStatus; - apiResponses?: Array<() => Promise>; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; - payload?: Record; -} describe('ADD remote clusters', () => { - const addRemoteClustersTest = ( - description: string, - { licenseCheckResult = { valid: true }, apiResponses = [], asserts, payload }: TestOptions - ) => { - test(description, async () => { - const elasticsearchMock = elasticsearchServiceMock.createLegacyClusterClient(); - - const mockRouteDependencies = { - router: httpServiceMock.createRouter(), - getLicenseStatus: () => licenseCheckResult, - elasticsearchService: elasticsearchServiceMock.createInternalSetup(), - elasticsearch: elasticsearchMock, - config: { - isCloudEnabled: false, - }, - }; + let handler: RequestHandler; + let mockRouteDependencies: ReturnType; + let mockContext: ReturnType; + let scopedClusterClientMock: ScopedClusterClientMock; + let remoteInfoMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['remoteInfo']; + let putSettingsMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['putSettings']; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + const createMockRouteDependencies = () => ({ + router: httpServiceMock.createRouter(), + getLicenseStatus: () => ({ valid: true }), + lib: { + handleEsError, + }, + config: { + isCloudEnabled: false, + }, + }); - elasticsearchServiceMock - .createLegacyClusterClient() - .asScoped.mockReturnValue(mockScopedClusterClient); + const createMockRequest = ( + body: Record = { + name: 'test', + seeds: ['127.0.0.1:9300'], + mode: 'sniff', + skipUnavailable: false, + } + ) => + httpServerMock.createKibanaRequest({ + method: 'post', + path: API_BASE_PATH, + body, + headers: { authorization: 'foo' }, + }); - for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); - } + beforeEach(() => { + mockContext = xpackMocks.createRequestHandlerContext(); + scopedClusterClientMock = mockContext.core.elasticsearch.client; + remoteInfoMockFn = scopedClusterClientMock.asCurrentUser.cluster.remoteInfo; + putSettingsMockFn = scopedClusterClientMock.asCurrentUser.cluster.putSettings; + mockRouteDependencies = createMockRouteDependencies(); - register(mockRouteDependencies); - const [[{ validate }, handler]] = mockRouteDependencies.router.post.mock.calls; + register(mockRouteDependencies); + const [[, handlerFn]] = mockRouteDependencies.router.post.mock.calls; + handler = handlerFn; + }); - const mockRequest = httpServerMock.createKibanaRequest({ - method: 'post', - path: API_BASE_PATH, - body: payload !== undefined ? (validate as any).body.validate(payload) : undefined, - headers: { authorization: 'foo' }, - }); + describe('success', () => { + test(`adds remote cluster with "sniff" mode`, async () => { + remoteInfoMockFn.mockResolvedValueOnce(createApiResponse({ body: {} })); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, + }, + }, + }, + transient: {}, + }, + }) + ); - const mockContext = xpackMocks.createRequestHandlerContext(); - mockContext.core.elasticsearch.legacy.client = mockScopedClusterClient; + const mockRequest = createMockRequest(); const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - expect(response.status).toBe(asserts.statusCode); - expect(response.payload).toEqual(asserts.result); + expect(response.status).toBe(200); + expect(response.payload).toEqual({ acknowledged: true }); - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); - } - }); - }; - - describe('success', () => { - addRemoteClustersTest(`adds remote cluster with "sniff" mode`, { - apiResponses: [ - async () => ({}), - async () => ({ - acknowledged: true, + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { persistent: { cluster: { remote: { test: { - connected: true, - mode: 'sniff', seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, }, }, }, }, - transient: {}, - }), - ], - payload: { - name: 'test', - seeds: ['127.0.0.1:9300'], - mode: 'sniff', - skipUnavailable: false, - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', - node_connections: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - }, - }, - }, - }, - }, - }, - ], - ], - statusCode: 200, - result: { - acknowledged: true, }, - }, + }); }); - addRemoteClustersTest(`adds remote cluster with "proxy" mode`, { - apiResponses: [ - async () => ({}), - async () => ({ - acknowledged: true, - persistent: { - cluster: { - remote: { - test: { - connected: true, - mode: 'proxy', - seeds: ['127.0.0.1:9300'], - num_sockets_connected: 1, - max_socket_connections: 18, - initial_connect_timeout: '30s', - skip_unavailable: false, + + test(`adds remote cluster with "proxy" mode`, async () => { + remoteInfoMockFn.mockResolvedValueOnce(createApiResponse({ body: {} })); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, }, }, }, + transient: {}, }, - transient: {}, - }), - ], - payload: { + }) + ); + + const mockRequest = createMockRequest({ name: 'test', proxyAddress: '127.0.0.1:9300', mode: 'proxy', skipUnavailable: false, serverName: 'foobar', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: null, - skip_unavailable: false, - mode: 'proxy', - node_connections: null, - proxy_address: '127.0.0.1:9300', - proxy_socket_connections: null, - server_name: 'foobar', - }, - }, - }, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ acknowledged: true }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: null, + skip_unavailable: false, + mode: 'proxy', + node_connections: null, + proxy_address: '127.0.0.1:9300', + proxy_socket_connections: null, + server_name: 'foobar', }, }, }, - ], - ], - statusCode: 200, - result: { - acknowledged: true, + }, }, - }, + }); }); }); describe('failure', () => { - addRemoteClustersTest('returns 409 if remote cluster already exists', { - apiResponses: [ - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, + test('returns 409 if remote cluster already exists', async () => { + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, }, - }), - ], - payload: { - name: 'test', - seeds: ['127.0.0.1:9300'], - skipUnavailable: false, - mode: 'sniff', - }, - asserts: { - apiArguments: [['cluster.remoteInfo']], - statusCode: 409, - result: { - message: 'There is already a remote cluster with that name.', - }, - }, + }) + ); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(409); + expect(response.payload).toEqual({ + message: 'There is already a remote cluster with that name.', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).not.toHaveBeenCalled(); }); - addRemoteClustersTest('returns 400 ES did not acknowledge remote cluster', { - apiResponses: [async () => ({}), async () => ({})], - payload: { - name: 'test', - seeds: ['127.0.0.1:9300'], - skipUnavailable: false, - mode: 'sniff', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', - node_connections: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - }, - }, - }, + test('returns 400 ES did not acknowledge remote cluster', async () => { + remoteInfoMockFn.mockResolvedValueOnce(createApiResponse({ body: {} })); + putSettingsMockFn.mockResolvedValueOnce(createApiResponse({ body: {} as any })); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(400); + expect(response.payload).toEqual({ + message: 'Unable to add cluster, no response returned from ES.', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, }, }, }, - ], - ], - statusCode: 400, - result: { - message: 'Unable to add cluster, no response returned from ES.', + }, }, - }, + }); }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index 685aee16dc665..3b6ba618c8975 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -14,7 +14,6 @@ import { serializeCluster, Cluster } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { API_BASE_PATH, PROXY_MODE, SNIFF_MODE } from '../../../common/constants'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { isEsError } from '../../shared_imports'; import { RouteDependencies } from '../../types'; const bodyValidation = schema.object({ @@ -31,18 +30,23 @@ const bodyValidation = schema.object({ type RouteBody = TypeOf; export const register = (deps: RouteDependencies): void => { + const { + router, + lib: { handleEsError }, + } = deps; + const addHandler: RequestHandler = async ( ctx, request, response ) => { try { - const callAsCurrentUser = ctx.core.elasticsearch.legacy.client.callAsCurrentUser; + const { client: clusterClient } = ctx.core.elasticsearch; const { name } = request.body; // Check if cluster already exists. - const existingCluster = await doesClusterExist(callAsCurrentUser, name); + const existingCluster = await doesClusterExist(clusterClient, name); if (existingCluster) { return response.conflict({ body: { @@ -57,9 +61,11 @@ export const register = (deps: RouteDependencies): void => { } const addClusterPayload = serializeCluster(request.body as Cluster); - const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { - body: addClusterPayload, - }); + const { body: updateClusterResponse } = await clusterClient.asCurrentUser.cluster.putSettings( + { + body: addClusterPayload, + } + ); const acknowledged = get(updateClusterResponse, 'acknowledged'); const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`); @@ -85,13 +91,10 @@ export const register = (deps: RouteDependencies): void => { }, }); } catch (error) { - if (isEsError(error)) { - return response.customError({ statusCode: error.statusCode, body: error }); - } - throw error; + return handleEsError({ error, response }); } }; - deps.router.post( + router.post( { path: API_BASE_PATH, validate: { diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts index ce94f45bb8443..d83472fc564b0 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts @@ -5,10 +5,9 @@ * 2.0. */ -import { kibanaResponseFactory } from '../../../../../../src/core/server'; +import { kibanaResponseFactory, RequestHandler } from '../../../../../../src/core/server'; import { register } from './delete_route'; import { API_BASE_PATH } from '../../../common/constants'; -import { LicenseStatus } from '../../types'; import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; @@ -19,6 +18,12 @@ import { coreMock, } from '../../../../../../src/core/server/mocks'; +import { handleEsError } from '../../shared_imports'; + +import { ScopedClusterClientMock } from './types'; + +const { createApiResponse } = elasticsearchServiceMock; + // Re-implement the mock that was imported directly from `x-pack/mocks` function createCoreRequestHandlerContextMock() { return { @@ -30,192 +35,193 @@ function createCoreRequestHandlerContextMock() { const xpackMocks = { createRequestHandlerContext: createCoreRequestHandlerContextMock, }; -interface TestOptions { - licenseCheckResult?: LicenseStatus; - apiResponses?: Array<() => Promise>; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; - params: { - nameOrNames: string; - }; -} describe('DELETE remote clusters', () => { - const deleteRemoteClustersTest = ( - description: string, - { licenseCheckResult = { valid: true }, apiResponses = [], asserts, params }: TestOptions - ) => { - test(description, async () => { - const elasticsearchMock = elasticsearchServiceMock.createLegacyClusterClient(); + let handler: RequestHandler; + let mockRouteDependencies: ReturnType; + let mockContext: ReturnType; + let scopedClusterClientMock: ScopedClusterClientMock; - const mockRouteDependencies = { - router: httpServiceMock.createRouter(), - getLicenseStatus: () => licenseCheckResult, - elasticsearchService: elasticsearchServiceMock.createInternalSetup(), - elasticsearch: elasticsearchMock, - config: { - isCloudEnabled: false, - }, - }; + let remoteInfoMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['remoteInfo']; + let getSettingsMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['getSettings']; + let putSettingsMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['putSettings']; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + const createMockRequest = ( + body: Record = { + name: 'test', + proxyAddress: '127.0.0.1:9300', + mode: 'proxy', + skipUnavailable: false, + serverName: 'foobar', + } + ) => + httpServerMock.createKibanaRequest({ + method: 'delete', + path: `${API_BASE_PATH}/{nameOrNames}`, + params: { + nameOrNames: 'test', + }, + body, + headers: { authorization: 'foo' }, + }); - elasticsearchServiceMock - .createLegacyClusterClient() - .asScoped.mockReturnValue(mockScopedClusterClient); + const createMockRouteDependencies = () => ({ + router: httpServiceMock.createRouter(), + getLicenseStatus: () => ({ valid: true }), + lib: { + handleEsError, + }, + config: { + isCloudEnabled: false, + }, + }); - for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); - } + beforeEach(() => { + mockContext = xpackMocks.createRequestHandlerContext(); + scopedClusterClientMock = mockContext.core.elasticsearch.client; + remoteInfoMockFn = scopedClusterClientMock.asCurrentUser.cluster.remoteInfo; + getSettingsMockFn = scopedClusterClientMock.asCurrentUser.cluster.getSettings; + putSettingsMockFn = scopedClusterClientMock.asCurrentUser.cluster.putSettings; + mockRouteDependencies = createMockRouteDependencies(); - register(mockRouteDependencies); - const [[{ validate }, handler]] = mockRouteDependencies.router.delete.mock.calls; + register(mockRouteDependencies); + const [[, handlerFn]] = mockRouteDependencies.router.delete.mock.calls; + handler = handlerFn; + }); - const mockRequest = httpServerMock.createKibanaRequest({ - method: 'delete', - path: `${API_BASE_PATH}/{nameOrNames}`, - params: (validate as any).params.validate(params), - headers: { authorization: 'foo' }, - }); + describe('success', () => { + test('deletes remote cluster', async () => { + getSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + }, + }, + }, + }, + transient: {}, + }, + }) + ); + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, + }, + }) + ); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + acknowledged: true, + persistent: {}, + transient: {}, + }, + }) + ); - const mockContext = xpackMocks.createRequestHandlerContext(); - mockContext.core.elasticsearch.legacy.client = mockScopedClusterClient; - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const mockRequest = createMockRequest(); - expect(response.status).toBe(asserts.statusCode); - expect(response.payload).toEqual(asserts.result); + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); - } - }); - }; + expect(response.status).toBe(200); + expect(response.payload).toEqual({ + itemsDeleted: ['test'], + errors: [], + }); - describe('success', () => { - deleteRemoteClustersTest('deletes remote cluster', { - apiResponses: [ - async () => ({ + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { persistent: { cluster: { remote: { test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', + seeds: null, + skip_unavailable: null, + mode: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + node_connections: null, }, }, }, }, - transient: {}, - }), - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - }, - }), - async () => ({ - acknowledged: true, - persistent: {}, - transient: {}, - }), - ], - params: { - nameOrNames: 'test', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: null, - skip_unavailable: null, - mode: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - node_connections: null, - }, - }, - }, - }, - }, - }, - ], - ], - statusCode: 200, - result: { - itemsDeleted: ['test'], - errors: [], }, - }, + }); }); }); describe('failure', () => { - deleteRemoteClustersTest( - 'returns errors array with 404 error if remote cluster does not exist', - { - apiResponses: [ - async () => ({ + test('returns errors array with 404 error if remote cluster does not exist', async () => { + getSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { persistent: { cluster: { remote: {}, }, }, transient: {}, - }), - async () => ({}), - ], - params: { - nameOrNames: 'test', - }, - asserts: { - apiArguments: [['cluster.remoteInfo']], - statusCode: 200, - result: { - errors: [ - { - error: { - options: { - body: { - message: 'There is no remote cluster with that name.', - }, - statusCode: 404, - }, - payload: { - message: 'There is no remote cluster with that name.', - }, - status: 404, + }, + }) + ); + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: {}, + }) + ); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ + errors: [ + { + error: { + options: { + body: { + message: 'There is no remote cluster with that name.', }, - name: 'test', + statusCode: 404, + }, + payload: { + message: 'There is no remote cluster with that name.', }, - ], - itemsDeleted: [], + status: 404, + }, + name: 'test', }, - }, - } - ); + ], + itemsDeleted: [], + }); - deleteRemoteClustersTest( - 'returns errors array with 400 error if ES still returns cluster information', - { - apiResponses: [ - async () => ({ + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + }); + + test('returns errors array with 400 error if ES still returns cluster information', async () => { + getSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { persistent: { cluster: { remote: { @@ -228,8 +234,12 @@ describe('DELETE remote clusters', () => { }, }, transient: {}, - }), - async () => ({ + }, + }) + ); + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { test: { connected: true, mode: 'sniff', @@ -239,8 +249,13 @@ describe('DELETE remote clusters', () => { initial_connect_timeout: '30s', skip_unavailable: false, }, - }), - async () => ({ + }, + }) + ); + + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { acknowledged: true, persistent: { cluster: { @@ -258,60 +273,57 @@ describe('DELETE remote clusters', () => { }, }, transient: {}, - }), - ], - params: { - nameOrNames: 'test', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { + }, + }) + ); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ + errors: [ + { + error: { + options: { body: { - persistent: { - cluster: { - remote: { - test: { - seeds: null, - skip_unavailable: null, - mode: null, - node_connections: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - }, - }, - }, - }, + message: 'Unable to delete cluster, information still returned from ES.', }, + statusCode: 400, }, - ], - ], - statusCode: 200, - result: { - errors: [ - { - error: { - options: { - body: { - message: 'Unable to delete cluster, information still returned from ES.', - }, - statusCode: 400, - }, - payload: { - message: 'Unable to delete cluster, information still returned from ES.', - }, - status: 400, + payload: { + message: 'Unable to delete cluster, information still returned from ES.', + }, + status: 400, + }, + name: 'test', + }, + ], + itemsDeleted: [], + }); + + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: null, + skip_unavailable: null, + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, }, - name: 'test', }, - ], - itemsDeleted: [], + }, }, }, - } - ); + }); + }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts index 89df5255d19e2..18aa9154fba42 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts @@ -15,7 +15,6 @@ import { serializeCluster } from '../../../common/lib'; import { API_BASE_PATH } from '../../../common/constants'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { isEsError } from '../../shared_imports'; const paramsValidation = schema.object({ nameOrNames: schema.string(), @@ -24,13 +23,18 @@ const paramsValidation = schema.object({ type RouteParams = TypeOf; export const register = (deps: RouteDependencies): void => { + const { + router, + lib: { handleEsError }, + } = deps; + const deleteHandler: RequestHandler = async ( ctx, request, response ) => { try { - const callAsCurrentUser = ctx.core.elasticsearch.legacy.client.callAsCurrentUser; + const { client: clusterClient } = ctx.core.elasticsearch; const { nameOrNames } = request.params; const names = nameOrNames.split(','); @@ -38,12 +42,12 @@ export const register = (deps: RouteDependencies): void => { const itemsDeleted: any[] = []; const errors: any[] = []; - const clusterSettings = await callAsCurrentUser('cluster.getSettings'); + const { body: clusterSettings } = await clusterClient.asCurrentUser.cluster.getSettings(); // Validator that returns an error if the remote cluster does not exist. const validateClusterDoesExist = async (name: string) => { try { - const existingCluster = await doesClusterExist(callAsCurrentUser, name); + const existingCluster = await doesClusterExist(clusterClient, name); if (!existingCluster) { return response.customError({ statusCode: 404, @@ -69,7 +73,12 @@ export const register = (deps: RouteDependencies): void => { ) => { try { const body = serializeCluster({ name, hasDeprecatedProxySetting }); - const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body }); + + const { + body: updateClusterResponse, + } = await clusterClient.asCurrentUser.cluster.putSettings({ + body, + }); const acknowledged = get(updateClusterResponse, 'acknowledged'); const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`); @@ -92,10 +101,7 @@ export const register = (deps: RouteDependencies): void => { }, }); } catch (error) { - if (isEsError(error)) { - return response.customError({ statusCode: error.statusCode, body: error }); - } - throw error; + return handleEsError({ error, response }); } }; @@ -129,14 +135,11 @@ export const register = (deps: RouteDependencies): void => { }, }); } catch (error) { - if (isEsError(error)) { - return response.customError({ statusCode: error.statusCode, body: error }); - } - throw error; + return handleEsError({ error, response }); } }; - deps.router.delete( + router.delete( { path: `${API_BASE_PATH}/{nameOrNames}`, validate: { diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts index cfec01da943ab..b4c8d6d304b12 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts @@ -5,14 +5,9 @@ * 2.0. */ -import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; -import { kibanaResponseFactory } from '../../../../../../src/core/server'; -import { register } from './get_route'; -import { API_BASE_PATH } from '../../../common/constants'; -import { LicenseStatus } from '../../types'; - -import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; +import { RequestHandler } from 'src/core/server'; import { elasticsearchServiceMock, @@ -21,6 +16,18 @@ import { coreMock, } from '../../../../../../src/core/server/mocks'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; +import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; + +import { API_BASE_PATH } from '../../../common/constants'; + +import { handleEsError } from '../../shared_imports'; + +import { register } from './get_route'; +import { ScopedClusterClientMock } from './types'; + +const { createApiResponse } = elasticsearchServiceMock; + // Re-implement the mock that was imported directly from `x-pack/mocks` function createCoreRequestHandlerContextMock() { return { @@ -32,182 +39,183 @@ function createCoreRequestHandlerContextMock() { const xpackMocks = { createRequestHandlerContext: createCoreRequestHandlerContextMock, }; -interface TestOptions { - licenseCheckResult?: LicenseStatus; - apiResponses?: Array<() => Promise>; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; -} - describe('GET remote clusters', () => { - const getRemoteClustersTest = ( - description: string, - { licenseCheckResult = { valid: true }, apiResponses = [], asserts }: TestOptions - ) => { - test(description, async () => { - const elasticsearchMock = elasticsearchServiceMock.createLegacyClusterClient(); - - const mockRouteDependencies = { - router: httpServiceMock.createRouter(), - getLicenseStatus: () => licenseCheckResult, - elasticsearchService: elasticsearchServiceMock.createInternalSetup(), - elasticsearch: elasticsearchMock, - config: { - isCloudEnabled: false, - }, - }; - - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - - elasticsearchServiceMock - .createLegacyClusterClient() - .asScoped.mockReturnValue(mockScopedClusterClient); - - for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); - } + let handler: RequestHandler; + let mockRouteDependencies: ReturnType; + let mockContext: ReturnType; + let scopedClusterClientMock: ScopedClusterClientMock; + + let remoteInfoMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['remoteInfo']; + let getSettingsMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['getSettings']; + + const createMockRequest = () => + httpServerMock.createKibanaRequest({ + method: 'get', + path: API_BASE_PATH, + headers: { authorization: 'foo' }, + }); - register(mockRouteDependencies); - const [[, handler]] = mockRouteDependencies.router.get.mock.calls; + const createMockRouteDependencies = () => ({ + router: httpServiceMock.createRouter(), + getLicenseStatus: () => ({ valid: true }), + lib: { + handleEsError, + }, + config: { + isCloudEnabled: false, + }, + }); - const mockRequest = httpServerMock.createKibanaRequest({ - method: 'get', - path: API_BASE_PATH, - headers: { authorization: 'foo' }, - }); + beforeEach(() => { + mockContext = xpackMocks.createRequestHandlerContext(); + scopedClusterClientMock = mockContext.core.elasticsearch.client; + remoteInfoMockFn = scopedClusterClientMock.asCurrentUser.cluster.remoteInfo; + getSettingsMockFn = scopedClusterClientMock.asCurrentUser.cluster.getSettings; + mockRouteDependencies = createMockRouteDependencies(); - const mockContext = xpackMocks.createRequestHandlerContext(); - mockContext.core.elasticsearch.legacy.client = mockScopedClusterClient; - - if (asserts.statusCode === 500) { - await expect(handler(mockContext, mockRequest, kibanaResponseFactory)).rejects.toThrowError( - asserts.result as Error - ); - } else { - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - - expect(response.status).toBe(asserts.statusCode); - expect(response.payload).toEqual(asserts.result); - } - - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); - } - }); - }; + register(mockRouteDependencies); + const [[, handlerFn]] = mockRouteDependencies.router.get.mock.calls; + handler = handlerFn; + }); describe('success', () => { - getRemoteClustersTest('returns remote clusters', { - apiResponses: [ - async () => ({ - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', + test('returns remote clusters', async () => { + getSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + }, }, }, }, + transient: {}, }, - transient: {}, - }), - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - }, - }), - ], - asserts: { - apiArguments: [['cluster.getSettings'], ['cluster.remoteInfo']], - statusCode: 200, - result: [ - { - name: 'test', - seeds: ['127.0.0.1:9300'], - isConnected: true, - connectedNodesCount: 1, - maxConnectionsPerCluster: 3, - initialConnectTimeout: '30s', - skipUnavailable: false, - isConfiguredByNode: false, - mode: 'sniff', + }) + ); + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, }, - ], - }, + }) + ); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual([ + { + name: 'test', + seeds: ['127.0.0.1:9300'], + isConnected: true, + connectedNodesCount: 1, + maxConnectionsPerCluster: 3, + initialConnectTimeout: '30s', + skipUnavailable: false, + isConfiguredByNode: false, + mode: 'sniff', + }, + ]); + + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); }); - getRemoteClustersTest('returns an empty array when ES responds with an empty object', { - apiResponses: [async () => ({}), async () => ({})], - asserts: { - apiArguments: [['cluster.getSettings'], ['cluster.remoteInfo']], - statusCode: 200, - result: [], - }, + + test('returns an empty array when ES responds with an empty object', async () => { + getSettingsMockFn.mockResolvedValueOnce(createApiResponse({ body: {} as any })); + remoteInfoMockFn.mockResolvedValueOnce(createApiResponse({ body: {} })); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual([]); + + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); }); }); describe('failure', () => { - const error = Boom.notAcceptable('test error'); + const error = new errors.ResponseError({ + body: { message: 'test error' }, + statusCode: 406, + headers: {}, + meta: {} as any, + warnings: [], + }); + + test('returns an error if failure to get cluster settings', async () => { + getSettingsMockFn.mockRejectedValueOnce(error); + + const mockRequest = createMockRequest(); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - getRemoteClustersTest('returns an error if failure to get cluster settings', { - apiResponses: [ - async () => { - throw error; + expect(response.status).toBe(406); + expect(response.payload).toEqual({ + attributes: { + causes: undefined, + error: undefined, }, - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - }, - }), - ], - asserts: { - apiArguments: [['cluster.getSettings']], - statusCode: 500, - result: error, - }, + message: 'Response Error', + }); + + expect(getSettingsMockFn).toHaveBeenCalled(); + expect(remoteInfoMockFn).not.toHaveBeenCalled(); }); - getRemoteClustersTest('returns an error if failure to get cluster remote info', { - apiResponses: [ - async () => ({ - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', + test('returns an error if failure to get cluster remote info', async () => { + getSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + }, }, }, }, + transient: {}, }, - transient: {}, - }), - async () => { - throw error; - }, - ], - asserts: { - apiArguments: [['cluster.getSettings'], ['cluster.remoteInfo']], - statusCode: 500, - result: error, - }, + }) + ); + + remoteInfoMockFn.mockRejectedValueOnce(error); + + const mockRequest = httpServerMock.createKibanaRequest({ + method: 'get', + path: API_BASE_PATH, + headers: { authorization: 'foo' }, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(406); + + expect(getSettingsMockFn).toHaveBeenCalledWith(); + expect(remoteInfoMockFn).toHaveBeenCalledWith(); }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts index fbb345203e48a..c98f523450686 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts @@ -11,14 +11,18 @@ import { RequestHandler } from 'src/core/server'; import { deserializeCluster } from '../../../common/lib'; import { API_BASE_PATH } from '../../../common/constants'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { isEsError } from '../../shared_imports'; import { RouteDependencies } from '../../types'; export const register = (deps: RouteDependencies): void => { + const { + router, + lib: { handleEsError }, + } = deps; + const allHandler: RequestHandler = async (ctx, request, response) => { try { - const callAsCurrentUser = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser; - const clusterSettings = await callAsCurrentUser('cluster.getSettings'); + const { client: clusterClient } = ctx.core.elasticsearch; + const { body: clusterSettings } = await clusterClient.asCurrentUser.cluster.getSettings(); const transientClusterNames = Object.keys( get(clusterSettings, 'transient.cluster.remote') || {} @@ -27,7 +31,7 @@ export const register = (deps: RouteDependencies): void => { get(clusterSettings, 'persistent.cluster.remote') || {} ); - const clustersByName = await callAsCurrentUser('cluster.remoteInfo'); + const { body: clustersByName } = await clusterClient.asCurrentUser.cluster.remoteInfo(); const clusterNames = (clustersByName && Object.keys(clustersByName)) || []; const body = clusterNames.map((clusterName: string): any => { @@ -60,14 +64,11 @@ export const register = (deps: RouteDependencies): void => { return response.ok({ body }); } catch (error) { - if (isEsError(error)) { - return response.customError({ statusCode: error.statusCode, body: error }); - } - throw error; + return handleEsError({ error, response }); } }; - deps.router.get( + router.get( { path: API_BASE_PATH, validate: false, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/types.ts b/x-pack/plugins/remote_clusters/server/routes/api/types.ts new file mode 100644 index 0000000000000..5a2fd20ba4d35 --- /dev/null +++ b/x-pack/plugins/remote_clusters/server/routes/api/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; + +export type ScopedClusterClientMock = ReturnType< + typeof elasticsearchServiceMock.createScopedClusterClient +>; diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts index 22c87786a585c..129326dea95ec 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts @@ -4,13 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { RequestHandler } from 'src/core/server'; import { kibanaResponseFactory } from '../../../../../../src/core/server'; -import { register } from './update_route'; -import { API_BASE_PATH } from '../../../common/constants'; -import { LicenseStatus } from '../../types'; - -import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; import { elasticsearchServiceMock, @@ -19,6 +14,17 @@ import { coreMock, } from '../../../../../../src/core/server/mocks'; +import { licensingMock } from '../../../../../plugins/licensing/server/mocks'; + +import { API_BASE_PATH } from '../../../common/constants'; + +import { handleEsError } from '../../shared_imports'; + +import { register } from './update_route'; +import { ScopedClusterClientMock } from './types'; + +const { createApiResponse } = elasticsearchServiceMock; + // Re-implement the mock that was imported directly from `x-pack/mocks` function createCoreRequestHandlerContextMock() { return { @@ -30,315 +36,296 @@ function createCoreRequestHandlerContextMock() { const xpackMocks = { createRequestHandlerContext: createCoreRequestHandlerContextMock, }; -interface TestOptions { - licenseCheckResult?: LicenseStatus; - apiResponses?: Array<() => Promise>; - asserts: { statusCode: number; result?: Record; apiArguments?: unknown[][] }; - payload?: Record; - params: { - name: string; - }; -} describe('UPDATE remote clusters', () => { - const updateRemoteClustersTest = ( - description: string, - { - licenseCheckResult = { valid: true }, - apiResponses = [], - asserts, - payload, - params, - }: TestOptions - ) => { - test(description, async () => { - const elasticsearchMock = elasticsearchServiceMock.createLegacyClusterClient(); - - const mockRouteDependencies = { - router: httpServiceMock.createRouter(), - getLicenseStatus: () => licenseCheckResult, - elasticsearchService: elasticsearchServiceMock.createInternalSetup(), - elasticsearch: elasticsearchMock, - config: { - isCloudEnabled: false, - }, - }; + let handler: RequestHandler; + let mockRouteDependencies: ReturnType; + let mockContext: ReturnType; + let scopedClusterClientMock: ScopedClusterClientMock; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + let remoteInfoMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['remoteInfo']; + let putSettingsMockFn: ScopedClusterClientMock['asCurrentUser']['cluster']['putSettings']; - elasticsearchServiceMock - .createLegacyClusterClient() - .asScoped.mockReturnValue(mockScopedClusterClient); + const createMockRequest = ( + body: Record = { seeds: ['127.0.0.1:9300'], skipUnavailable: true, mode: 'sniff' } + ) => + httpServerMock.createKibanaRequest({ + method: 'put', + path: `${API_BASE_PATH}/{name}`, + params: { + name: 'test', + }, + body, + headers: { authorization: 'foo' }, + }); - for (const apiResponse of apiResponses) { - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(apiResponse); - } + const createMockRouteDependencies = () => ({ + router: httpServiceMock.createRouter(), + getLicenseStatus: () => ({ valid: true }), + lib: { + handleEsError, + }, + config: { + isCloudEnabled: false, + }, + }); - register(mockRouteDependencies); - const [[{ validate }, handler]] = mockRouteDependencies.router.put.mock.calls; + beforeEach(() => { + mockContext = xpackMocks.createRequestHandlerContext(); + scopedClusterClientMock = mockContext.core.elasticsearch.client; + remoteInfoMockFn = scopedClusterClientMock.asCurrentUser.cluster.remoteInfo; + putSettingsMockFn = scopedClusterClientMock.asCurrentUser.cluster.putSettings; + mockRouteDependencies = createMockRouteDependencies(); - const mockRequest = httpServerMock.createKibanaRequest({ - method: 'put', - path: `${API_BASE_PATH}/{name}`, - params: (validate as any).params.validate(params), - body: payload !== undefined ? (validate as any).body.validate(payload) : undefined, - headers: { authorization: 'foo' }, - }); + register(mockRouteDependencies); + const [[, handlerFn]] = mockRouteDependencies.router.put.mock.calls; + handler = handlerFn; + }); - const mockContext = xpackMocks.createRequestHandlerContext(); - mockContext.core.elasticsearch.legacy.client = mockScopedClusterClient; - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + describe('success', () => { + test('updates remote cluster', async () => { + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, + }, + }) + ); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: true, + }, + }, + }, + }, + transient: {}, + }, + }) + ); - expect(response.status).toBe(asserts.statusCode); - expect(response.payload).toEqual(asserts.result); + const mockRequest = createMockRequest(); - if (Array.isArray(asserts.apiArguments)) { - for (const apiArguments of asserts.apiArguments) { - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith(...apiArguments); - } - } else { - expect(mockScopedClusterClient.callAsCurrentUser).not.toHaveBeenCalled(); - } - }); - }; + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); - describe('success', () => { - updateRemoteClustersTest('updates remote cluster', { - apiResponses: [ - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - }, - }), - async () => ({ - acknowledged: true, + expect(response.status).toBe(200); + expect(response.payload).toEqual({ + connectedNodesCount: 1, + initialConnectTimeout: '30s', + isConfiguredByNode: false, + isConnected: true, + maxConnectionsPerCluster: 3, + name: 'test', + seeds: ['127.0.0.1:9300'], + skipUnavailable: true, + mode: 'sniff', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { persistent: { cluster: { remote: { test: { - connected: true, - mode: 'sniff', seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', skip_unavailable: true, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, }, }, }, }, - transient: {}, - }), - ], - params: { - name: 'test', - }, - payload: { - seeds: ['127.0.0.1:9300'], - skipUnavailable: true, - mode: 'sniff', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: true, - mode: 'sniff', - node_connections: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - }, - }, + }, + }); + }); + + test('updates v1 proxy cluster', async () => { + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + initial_connect_timeout: '30s', + skip_unavailable: false, + seeds: ['127.0.0.1:9300'], + max_connections_per_cluster: 20, + num_nodes_connected: 1, + }, + }, + }) + ); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + proxy_address: '127.0.0.1:9300', + initial_connect_timeout: '30s', + skip_unavailable: true, + mode: 'proxy', + proxy_socket_connections: 18, }, }, }, }, - ], - ], - statusCode: 200, - result: { - connectedNodesCount: 1, - initialConnectTimeout: '30s', - isConfiguredByNode: false, - isConnected: true, - maxConnectionsPerCluster: 3, - name: 'test', - seeds: ['127.0.0.1:9300'], - skipUnavailable: true, - mode: 'sniff', - }, - }, - }); - updateRemoteClustersTest('updates v1 proxy cluster', { - apiResponses: [ - async () => ({ - test: { - connected: true, - initial_connect_timeout: '30s', - skip_unavailable: false, - seeds: ['127.0.0.1:9300'], + transient: {}, }, - }), - async () => ({ - acknowledged: true, + }) + ); + + const mockRequest = createMockRequest({ + proxyAddress: '127.0.0.1:9300', + skipUnavailable: true, + mode: 'proxy', + hasDeprecatedProxySetting: true, + serverName: '', + proxySocketConnections: 18, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(200); + expect(response.payload).toEqual({ + initialConnectTimeout: '30s', + isConfiguredByNode: false, + isConnected: true, + proxyAddress: '127.0.0.1:9300', + name: 'test', + skipUnavailable: true, + mode: 'proxy', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { persistent: { cluster: { remote: { test: { - connected: true, proxy_address: '127.0.0.1:9300', - initial_connect_timeout: '30s', skip_unavailable: true, mode: 'proxy', + node_connections: null, + seeds: null, proxy_socket_connections: 18, + server_name: null, + proxy: null, }, }, }, }, - transient: {}, - }), - ], - params: { - name: 'test', - }, - payload: { - proxyAddress: '127.0.0.1:9300', - skipUnavailable: true, - mode: 'proxy', - hasDeprecatedProxySetting: true, - serverName: '', - proxySocketConnections: 18, - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - proxy_address: '127.0.0.1:9300', - skip_unavailable: true, - mode: 'proxy', - node_connections: null, - seeds: null, - proxy_socket_connections: 18, - server_name: null, - proxy: null, - }, - }, - }, - }, - }, - }, - ], - ], - statusCode: 200, - result: { - initialConnectTimeout: '30s', - isConfiguredByNode: false, - isConnected: true, - proxyAddress: '127.0.0.1:9300', - name: 'test', - skipUnavailable: true, - mode: 'proxy', }, - }, + }); }); }); describe('failure', () => { - updateRemoteClustersTest('returns 404 if remote cluster does not exist', { - apiResponses: [async () => ({})], - payload: { + test('returns 404 if remote cluster does not exist', async () => { + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: {} as any, + }) + ); + + const mockRequest = createMockRequest({ seeds: ['127.0.0.1:9300'], skipUnavailable: false, mode: 'sniff', - }, - params: { - name: 'test', - }, - asserts: { - apiArguments: [['cluster.remoteInfo']], - statusCode: 404, - result: { - message: 'There is no remote cluster with that name.', - }, - }, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(404); + expect(response.payload).toEqual({ + message: 'There is no remote cluster with that name.', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).not.toHaveBeenCalled(); }); - updateRemoteClustersTest('returns 400 if ES did not acknowledge remote cluster', { - apiResponses: [ - async () => ({ - test: { - connected: true, - mode: 'sniff', - seeds: ['127.0.0.1:9300'], - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, + test('returns 400 if ES did not acknowledge remote cluster', async () => { + remoteInfoMockFn.mockResolvedValueOnce( + createApiResponse({ + body: { + test: { + connected: true, + mode: 'sniff', + seeds: ['127.0.0.1:9300'], + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, }, - }), - async () => ({}), - ], - payload: { + }) + ); + putSettingsMockFn.mockResolvedValueOnce( + createApiResponse({ + body: {} as any, + }) + ); + + const mockRequest = createMockRequest({ seeds: ['127.0.0.1:9300'], skipUnavailable: false, mode: 'sniff', - }, - params: { - name: 'test', - }, - asserts: { - apiArguments: [ - ['cluster.remoteInfo'], - [ - 'cluster.putSettings', - { - body: { - persistent: { - cluster: { - remote: { - test: { - seeds: ['127.0.0.1:9300'], - skip_unavailable: false, - mode: 'sniff', - node_connections: null, - proxy_address: null, - proxy_socket_connections: null, - server_name: null, - }, - }, - }, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(400); + expect(response.payload).toEqual({ + message: 'Unable to edit cluster, no response returned from ES.', + }); + + expect(remoteInfoMockFn).toHaveBeenCalledWith(); + expect(putSettingsMockFn).toHaveBeenCalledWith({ + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, }, }, }, - ], - ], - statusCode: 400, - result: { - message: 'Unable to edit cluster, no response returned from ES.', + }, }, - }, + }); }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index 99fb7dd01adb1..efe259b40ec25 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -15,7 +15,6 @@ import { serializeCluster, deserializeCluster, Cluster, ClusterInfoEs } from '.. import { doesClusterExist } from '../../lib/does_cluster_exist'; import { RouteDependencies } from '../../types'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; -import { isEsError } from '../../shared_imports'; const bodyValidation = schema.object({ skipUnavailable: schema.boolean(), @@ -37,18 +36,23 @@ type RouteParams = TypeOf; type RouteBody = TypeOf; export const register = (deps: RouteDependencies): void => { + const { + router, + lib: { handleEsError }, + } = deps; + const updateHandler: RequestHandler = async ( ctx, request, response ) => { try { - const callAsCurrentUser = ctx.core.elasticsearch.legacy.client.callAsCurrentUser; + const { client: clusterClient } = ctx.core.elasticsearch; const { name } = request.params; // Check if cluster does exist. - const existingCluster = await doesClusterExist(callAsCurrentUser, name); + const existingCluster = await doesClusterExist(clusterClient, name); if (!existingCluster) { return response.notFound({ body: { @@ -65,9 +69,11 @@ export const register = (deps: RouteDependencies): void => { // Update cluster as new settings const updateClusterPayload = serializeCluster({ ...request.body, name } as Cluster); - const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { - body: updateClusterPayload, - }); + const { body: updateClusterResponse } = await clusterClient.asCurrentUser.cluster.putSettings( + { + body: updateClusterPayload, + } + ); const acknowledged = get(updateClusterResponse, 'acknowledged'); const cluster = get( @@ -97,14 +103,11 @@ export const register = (deps: RouteDependencies): void => { }, }); } catch (error) { - if (isEsError(error)) { - return response.customError({ statusCode: error.statusCode, body: error }); - } - throw error; + return handleEsError({ error, response }); } }; - deps.router.put( + router.put( { path: `${API_BASE_PATH}/{name}`, validate: { diff --git a/x-pack/plugins/remote_clusters/server/shared_imports.ts b/x-pack/plugins/remote_clusters/server/shared_imports.ts index df9b3dd53cc1f..7f55d189457c7 100644 --- a/x-pack/plugins/remote_clusters/server/shared_imports.ts +++ b/x-pack/plugins/remote_clusters/server/shared_imports.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { isEsError } from '../../../../src/plugins/es_ui_shared/server'; +export { handleEsError } from '../../../../src/plugins/es_ui_shared/server'; diff --git a/x-pack/plugins/remote_clusters/server/types.ts b/x-pack/plugins/remote_clusters/server/types.ts index 00b73d614ec96..89d1ebfb5cf4e 100644 --- a/x-pack/plugins/remote_clusters/server/types.ts +++ b/x-pack/plugins/remote_clusters/server/types.ts @@ -6,10 +6,13 @@ */ import { IRouter } from 'kibana/server'; + import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { CloudSetup } from '../../cloud/server'; +import { handleEsError } from './shared_imports'; + export interface Dependencies { licensing: LicensingPluginSetup; cloud: CloudSetup; @@ -22,6 +25,9 @@ export interface RouteDependencies { config: { isCloudEnabled: boolean; }; + lib: { + handleEsError: typeof handleEsError; + }; } export interface LicenseStatus { diff --git a/x-pack/plugins/rule_registry/common/assets.ts b/x-pack/plugins/rule_registry/common/assets.ts new file mode 100644 index 0000000000000..ddc425f068c5a --- /dev/null +++ b/x-pack/plugins/rule_registry/common/assets.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const DEFAULT_ASSET_NAMESPACE = 'alerts'; + +export const TECHNICAL_COMPONENT_TEMPLATE_NAME = `technical-mappings`; +export const ECS_COMPONENT_TEMPLATE_NAME = `ecs-mappings`; +export const DEFAULT_ILM_POLICY_ID = 'ilm-policy'; diff --git a/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts b/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts new file mode 100644 index 0000000000000..7acbe0bc1227b --- /dev/null +++ b/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { merge } from 'lodash'; +import { mappingFromFieldMap } from '../../mapping_from_field_map'; +import { ClusterPutComponentTemplateBody } from '../../types'; +import { ecsFieldMap } from '../field_maps/ecs_field_map'; +import { technicalRuleFieldMap } from '../field_maps/technical_rule_field_map'; + +export const ecsComponentTemplate: ClusterPutComponentTemplateBody = { + template: { + settings: { + number_of_shards: 1, + }, + mappings: merge( + {}, + mappingFromFieldMap(ecsFieldMap), + mappingFromFieldMap(technicalRuleFieldMap) + ), + }, +}; diff --git a/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts b/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts new file mode 100644 index 0000000000000..cc096faba387e --- /dev/null +++ b/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mappingFromFieldMap } from '../../mapping_from_field_map'; +import { ClusterPutComponentTemplateBody } from '../../types'; +import { technicalRuleFieldMap } from '../field_maps/technical_rule_field_map'; + +export const technicalComponentTemplate: ClusterPutComponentTemplateBody = { + template: { + settings: { + number_of_shards: 1, + }, + mappings: mappingFromFieldMap(technicalRuleFieldMap), + }, +}; diff --git a/x-pack/plugins/rule_registry/common/field_map/ecs_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts similarity index 100% rename from x-pack/plugins/rule_registry/common/field_map/ecs_field_map.ts rename to x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts new file mode 100644 index 0000000000000..a946e9523548c --- /dev/null +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { pickWithPatterns } from '../../../common/pick_with_patterns'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_ID, + ALERT_SEVERITY_LEVEL, + ALERT_SEVERITY_VALUE, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + EVENT_ACTION, + EVENT_KIND, + PRODUCER, + RULE_CATEGORY, + RULE_ID, + RULE_NAME, + RULE_UUID, + TAGS, + TIMESTAMP, +} from '../../../common/technical_rule_data_field_names'; +import { ecsFieldMap } from './ecs_field_map'; + +export const technicalRuleFieldMap = { + ...pickWithPatterns( + ecsFieldMap, + TIMESTAMP, + EVENT_KIND, + EVENT_ACTION, + RULE_UUID, + RULE_ID, + RULE_NAME, + RULE_CATEGORY, + TAGS + ), + [PRODUCER]: { type: 'keyword' }, + [ALERT_UUID]: { type: 'keyword' }, + [ALERT_ID]: { type: 'keyword' }, + [ALERT_START]: { type: 'date' }, + [ALERT_END]: { type: 'date' }, + [ALERT_DURATION]: { type: 'long' }, + [ALERT_SEVERITY_LEVEL]: { type: 'keyword' }, + [ALERT_SEVERITY_VALUE]: { type: 'long' }, + [ALERT_STATUS]: { type: 'keyword' }, + [ALERT_EVALUATION_THRESHOLD]: { type: 'scaled_float', scaling_factor: 100 }, + [ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100 }, +} as const; + +export type TechnicalRuleFieldMaps = typeof technicalRuleFieldMap; diff --git a/x-pack/plugins/rule_registry/common/assets/index_templates/base_index_template.ts b/x-pack/plugins/rule_registry/common/assets/index_templates/base_index_template.ts new file mode 100644 index 0000000000000..ee2e45640c149 --- /dev/null +++ b/x-pack/plugins/rule_registry/common/assets/index_templates/base_index_template.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const baseIndexTemplate = { + template: { + settings: { + number_of_shards: 1, + number_of_replicas: 0, + }, + }, +}; diff --git a/x-pack/plugins/rule_registry/server/rule_registry/defaults/ilm_policy.ts b/x-pack/plugins/rule_registry/common/assets/lifecycle_policies/default_lifecycle_policy.ts similarity index 86% rename from x-pack/plugins/rule_registry/server/rule_registry/defaults/ilm_policy.ts rename to x-pack/plugins/rule_registry/common/assets/lifecycle_policies/default_lifecycle_policy.ts index c80f7e772f308..f207087f7aa19 100644 --- a/x-pack/plugins/rule_registry/server/rule_registry/defaults/ilm_policy.ts +++ b/x-pack/plugins/rule_registry/common/assets/lifecycle_policies/default_lifecycle_policy.ts @@ -5,9 +5,7 @@ * 2.0. */ -import { ILMPolicy } from '../types'; - -export const defaultIlmPolicy: ILMPolicy = { +export const defaultLifecyclePolicy = { policy: { phases: { hot: { diff --git a/x-pack/plugins/rule_registry/common/field_map/base_rule_field_map.ts b/x-pack/plugins/rule_registry/common/field_map/base_rule_field_map.ts deleted file mode 100644 index 7e0e2a9c83965..0000000000000 --- a/x-pack/plugins/rule_registry/common/field_map/base_rule_field_map.ts +++ /dev/null @@ -1,36 +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 { ecsFieldMap } from './ecs_field_map'; -import { pickWithPatterns } from '../pick_with_patterns'; - -export const baseRuleFieldMap = { - ...pickWithPatterns( - ecsFieldMap, - '@timestamp', - 'event.kind', - 'event.action', - 'rule.uuid', - 'rule.id', - 'rule.name', - 'rule.category', - 'tags' - ), - 'kibana.rac.producer': { type: 'keyword' }, - 'kibana.rac.alert.uuid': { type: 'keyword' }, - 'kibana.rac.alert.id': { type: 'keyword' }, - 'kibana.rac.alert.start': { type: 'date' }, - 'kibana.rac.alert.end': { type: 'date' }, - 'kibana.rac.alert.duration.us': { type: 'long' }, - 'kibana.rac.alert.severity.level': { type: 'keyword' }, - 'kibana.rac.alert.severity.value': { type: 'long' }, - 'kibana.rac.alert.status': { type: 'keyword' }, - 'kibana.rac.alert.ancestors': { array: true, type: 'keyword' }, - 'kibana.rac.alert.depth': { type: 'long' }, - 'kibana.rac.alert.building_block_type': { type: 'keyword' }, -} as const; - -export type BaseRuleFieldMap = typeof baseRuleFieldMap; diff --git a/x-pack/plugins/rule_registry/common/field_map/index.ts b/x-pack/plugins/rule_registry/common/field_map/index.ts index 8db5c2738439b..fac8575b8af48 100644 --- a/x-pack/plugins/rule_registry/common/field_map/index.ts +++ b/x-pack/plugins/rule_registry/common/field_map/index.ts @@ -5,8 +5,6 @@ * 2.0. */ -export * from './base_rule_field_map'; -export * from './ecs_field_map'; export * from './merge_field_maps'; export * from './runtime_type_from_fieldmap'; export * from './types'; diff --git a/x-pack/plugins/rule_registry/common/index.ts b/x-pack/plugins/rule_registry/common/index.ts index b614feebc974a..5d36cd8cad7be 100644 --- a/x-pack/plugins/rule_registry/common/index.ts +++ b/x-pack/plugins/rule_registry/common/index.ts @@ -4,5 +4,4 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export * from './field_map'; -export * from './pick_with_patterns'; +export { parseTechnicalFields } from './parse_technical_fields'; diff --git a/x-pack/plugins/rule_registry/server/rule_registry/field_map/mapping_from_field_map.ts b/x-pack/plugins/rule_registry/common/mapping_from_field_map.ts similarity index 79% rename from x-pack/plugins/rule_registry/server/rule_registry/field_map/mapping_from_field_map.ts rename to x-pack/plugins/rule_registry/common/mapping_from_field_map.ts index f1d7126906431..17eb5ae8967af 100644 --- a/x-pack/plugins/rule_registry/server/rule_registry/field_map/mapping_from_field_map.ts +++ b/x-pack/plugins/rule_registry/common/mapping_from_field_map.ts @@ -5,11 +5,11 @@ * 2.0. */ +import { TypeMapping } from '@elastic/elasticsearch/api/types'; import { set } from '@elastic/safer-lodash-set'; -import { FieldMap } from '../../../common'; -import { Mappings } from '../types'; +import { FieldMap } from './field_map/types'; -export function mappingFromFieldMap(fieldMap: FieldMap): Mappings { +export function mappingFromFieldMap(fieldMap: FieldMap): TypeMapping { const mappings = { dynamic: 'strict' as const, properties: {}, diff --git a/x-pack/plugins/rule_registry/common/parse_technical_fields.ts b/x-pack/plugins/rule_registry/common/parse_technical_fields.ts new file mode 100644 index 0000000000000..9d92c657468a3 --- /dev/null +++ b/x-pack/plugins/rule_registry/common/parse_technical_fields.ts @@ -0,0 +1,25 @@ +/* + * 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 { isLeft } from 'fp-ts/lib/Either'; +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { technicalRuleFieldMap } from './assets/field_maps/technical_rule_field_map'; +import { runtimeTypeFromFieldMap } from './field_map'; + +const technicalFieldRuntimeType = runtimeTypeFromFieldMap(technicalRuleFieldMap); + +export const parseTechnicalFields = (input: unknown) => { + const validate = technicalFieldRuntimeType.decode(input); + + if (isLeft(validate)) { + throw new Error(PathReporter.report(validate).join('\n')); + } + + return technicalFieldRuntimeType.encode(validate.right); +}; + +export type ParsedTechnicalFields = ReturnType; diff --git a/x-pack/plugins/rule_registry/common/technical_rule_data_field_names.ts b/x-pack/plugins/rule_registry/common/technical_rule_data_field_names.ts new file mode 100644 index 0000000000000..e6ee7d1ea5376 --- /dev/null +++ b/x-pack/plugins/rule_registry/common/technical_rule_data_field_names.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ValuesType } from 'utility-types'; + +const ALERT_NAMESPACE = 'kibana.rac.alert'; + +const TIMESTAMP = '@timestamp' as const; +const EVENT_KIND = 'event.kind' as const; +const EVENT_ACTION = 'event.action' as const; +const RULE_UUID = 'rule.uuid' as const; +const RULE_ID = 'rule.id' as const; +const RULE_NAME = 'rule.name' as const; +const RULE_CATEGORY = 'rule.category' as const; +const TAGS = 'tags' as const; +const PRODUCER = `${ALERT_NAMESPACE}.producer` as const; +const ALERT_ID = `${ALERT_NAMESPACE}.id` as const; +const ALERT_UUID = `${ALERT_NAMESPACE}.uuid` as const; +const ALERT_START = `${ALERT_NAMESPACE}.start` as const; +const ALERT_END = `${ALERT_NAMESPACE}.end` as const; +const ALERT_DURATION = `${ALERT_NAMESPACE}.duration.us` as const; +const ALERT_SEVERITY_LEVEL = `${ALERT_NAMESPACE}.severity.level` as const; +const ALERT_SEVERITY_VALUE = `${ALERT_NAMESPACE}.severity.value` as const; +const ALERT_STATUS = `${ALERT_NAMESPACE}.status` as const; +const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const; +const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const; + +const fields = { + TIMESTAMP, + EVENT_KIND, + EVENT_ACTION, + RULE_UUID, + RULE_ID, + RULE_NAME, + RULE_CATEGORY, + TAGS, + PRODUCER, + ALERT_ID, + ALERT_UUID, + ALERT_START, + ALERT_END, + ALERT_DURATION, + ALERT_SEVERITY_LEVEL, + ALERT_SEVERITY_VALUE, + ALERT_STATUS, + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, +}; + +export { + TIMESTAMP, + EVENT_KIND, + EVENT_ACTION, + RULE_UUID, + RULE_ID, + RULE_NAME, + RULE_CATEGORY, + TAGS, + PRODUCER, + ALERT_ID, + ALERT_UUID, + ALERT_START, + ALERT_END, + ALERT_DURATION, + ALERT_SEVERITY_LEVEL, + ALERT_SEVERITY_VALUE, + ALERT_STATUS, + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, +}; + +export type TechnicalRuleDataFieldName = ValuesType; diff --git a/x-pack/plugins/rule_registry/common/types.ts b/x-pack/plugins/rule_registry/common/types.ts new file mode 100644 index 0000000000000..299d2c300ab49 --- /dev/null +++ b/x-pack/plugins/rule_registry/common/types.ts @@ -0,0 +1,21 @@ +/* + * 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 { estypes } from '@elastic/elasticsearch'; + +export type PutIndexTemplateRequest = estypes.PutIndexTemplateRequest & { + body?: { composed_of?: string[] }; +}; + +export interface ClusterPutComponentTemplateBody { + template: { + settings: { + number_of_shards: number; + }; + mappings: estypes.TypeMapping; + }; +} diff --git a/x-pack/plugins/rule_registry/kibana.json b/x-pack/plugins/rule_registry/kibana.json index ec2b366f739e6..7e3f8bf6afb72 100644 --- a/x-pack/plugins/rule_registry/kibana.json +++ b/x-pack/plugins/rule_registry/kibana.json @@ -10,6 +10,5 @@ "alerting", "triggersActionsUi" ], - "server": true, - "ui": true + "server": true } diff --git a/x-pack/plugins/rule_registry/public/index.ts b/x-pack/plugins/rule_registry/public/index.ts deleted file mode 100644 index 59697261ff20b..0000000000000 --- a/x-pack/plugins/rule_registry/public/index.ts +++ /dev/null @@ -1,17 +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 { PluginInitializerContext } from 'kibana/public'; -import { Plugin } from './plugin'; - -export type { RuleRegistryPublicPluginSetupContract } from './plugin'; -export { RuleRegistry } from './rule_registry'; -export type { IRuleRegistry, RuleType } from './rule_registry/types'; - -export const plugin = (context: PluginInitializerContext) => { - return new Plugin(context); -}; diff --git a/x-pack/plugins/rule_registry/public/plugin.ts b/x-pack/plugins/rule_registry/public/plugin.ts deleted file mode 100644 index 7f0bceefb6797..0000000000000 --- a/x-pack/plugins/rule_registry/public/plugin.ts +++ /dev/null @@ -1,56 +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 { - CoreSetup, - CoreStart, - Plugin as PluginClass, - PluginInitializerContext, -} from '../../../../src/core/public'; -import type { - PluginSetupContract as AlertingPluginPublicSetupContract, - PluginStartContract as AlertingPluginPublicStartContract, -} from '../../alerting/public'; -import type { - TriggersAndActionsUIPublicPluginSetup, - TriggersAndActionsUIPublicPluginStart, -} from '../../triggers_actions_ui/public'; -import type { BaseRuleFieldMap } from '../common'; -import { RuleRegistry } from './rule_registry'; - -interface RuleRegistrySetupPlugins { - alerting: AlertingPluginPublicSetupContract; - triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; -} - -interface RuleRegistryStartPlugins { - alerting: AlertingPluginPublicStartContract; - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; -} - -export type RuleRegistryPublicPluginSetupContract = ReturnType; - -export class Plugin - implements PluginClass { - constructor(context: PluginInitializerContext) {} - - public setup(core: CoreSetup, plugins: RuleRegistrySetupPlugins) { - const rootRegistry = new RuleRegistry({ - fieldMap: {} as BaseRuleFieldMap, - alertTypeRegistry: plugins.triggersActionsUi.alertTypeRegistry, - }); - return { - registry: rootRegistry, - }; - } - - start(core: CoreStart, plugins: RuleRegistryStartPlugins) { - return { - registerType: plugins.triggersActionsUi.alertTypeRegistry, - }; - } -} diff --git a/x-pack/plugins/rule_registry/public/rule_registry/index.ts b/x-pack/plugins/rule_registry/public/rule_registry/index.ts deleted file mode 100644 index ea47fe2e26aad..0000000000000 --- a/x-pack/plugins/rule_registry/public/rule_registry/index.ts +++ /dev/null @@ -1,47 +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 { BaseRuleFieldMap } from '../../common'; -import type { RuleType, CreateRuleRegistry, RuleRegistryConstructorOptions } from './types'; - -export class RuleRegistry { - protected types: TRuleType[] = []; - - constructor(private readonly options: RuleRegistryConstructorOptions) {} - - getTypes(): TRuleType[] { - return this.types; - } - - getTypeByRuleId(id: string): TRuleType | undefined { - return this.types.find((type) => type.id === id); - } - - registerType(type: TRuleType) { - this.types.push(type); - if (this.options.parent) { - this.options.parent.registerType(type); - } else { - this.options.alertTypeRegistry.register(type); - } - } - - create: CreateRuleRegistry = ({ fieldMap, ctor }) => { - const createOptions = { - fieldMap: { - ...this.options.fieldMap, - ...fieldMap, - }, - alertTypeRegistry: this.options.alertTypeRegistry, - parent: this, - }; - - const registry = ctor ? new ctor(createOptions) : new RuleRegistry(createOptions); - - return registry as any; - }; -} diff --git a/x-pack/plugins/rule_registry/public/rule_registry/types.ts b/x-pack/plugins/rule_registry/public/rule_registry/types.ts deleted file mode 100644 index 7c186385ebd35..0000000000000 --- a/x-pack/plugins/rule_registry/public/rule_registry/types.ts +++ /dev/null @@ -1,63 +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 { AlertTypeRegistryContract } from '../../../triggers_actions_ui/public'; -import type { BaseRuleFieldMap, FieldMap } from '../../common'; - -export interface RuleRegistryConstructorOptions { - fieldMap: TFieldMap; - alertTypeRegistry: AlertTypeRegistryContract; - parent?: IRuleRegistry; -} - -export type RuleType = Parameters[0]; - -export type RegisterRuleType< - TFieldMap extends BaseRuleFieldMap, - TAdditionalRegisterOptions = {} -> = (type: RuleType & TAdditionalRegisterOptions) => void; - -export type RuleRegistryExtensions = Record< - T, - (...args: any[]) => any ->; - -export type CreateRuleRegistry< - TFieldMap extends BaseRuleFieldMap, - TRuleType extends RuleType, - TInstanceType = undefined -> = < - TNextFieldMap extends FieldMap, - TRuleRegistryInstance extends IRuleRegistry< - TFieldMap & TNextFieldMap, - any - > = TInstanceType extends IRuleRegistry - ? TInstanceType - : IRuleRegistry ->(options: { - fieldMap: TNextFieldMap; - ctor?: new ( - options: RuleRegistryConstructorOptions - ) => TRuleRegistryInstance; -}) => TRuleRegistryInstance; - -export interface IRuleRegistry< - TFieldMap extends BaseRuleFieldMap, - TRuleType extends RuleType, - TInstanceType = undefined -> { - create: CreateRuleRegistry; - registerType(type: TRuleType): void; - getTypeByRuleId(ruleId: string): TRuleType; - getTypes(): TRuleType[]; -} - -export type FieldMapOfRuleRegistry = TRuleRegistry extends IRuleRegistry< - infer TFieldMap, - any -> - ? TFieldMap - : never; diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts index e2b4dec31b3f7..08b043657bf98 100644 --- a/x-pack/plugins/rule_registry/server/index.ts +++ b/x-pack/plugins/rule_registry/server/index.ts @@ -9,11 +9,12 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { PluginInitializerContext } from 'src/core/server'; import { RuleRegistryPlugin } from './plugin'; -export { RuleRegistryPluginSetupContract } from './plugin'; -export { createLifecycleRuleTypeFactory } from './rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory'; +export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin'; +export { RuleDataClient } from './rule_data_client'; +export { IRuleDataClient } from './rule_data_client/types'; +export { getRuleExecutorData, RuleExecutorData } from './utils/get_rule_executor_data'; +export { createLifecycleRuleTypeFactory } from './utils/create_lifecycle_rule_type_factory'; export { createPersistenceRuleTypeFactory } from './rule_registry/rule_type_helpers/create_persistence_rule_type_factory'; -export { FieldMapOf } from './types'; -export { ScopedRuleRegistryClient } from './rule_registry/create_scoped_rule_registry_client/types'; export const config = { schema: schema.object({ @@ -24,7 +25,7 @@ export const config = { }), }; -export type RuleRegistryConfig = TypeOf; +export type RuleRegistryPluginConfig = TypeOf; export const plugin = (initContext: PluginInitializerContext) => new RuleRegistryPlugin(initContext); diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 961b947e3f556..86eaa1661a884 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -5,53 +5,46 @@ * 2.0. */ -import { PluginInitializerContext, Plugin, CoreSetup, CoreStart } from 'src/core/server'; -import { - PluginSetupContract as AlertingPluginSetupContract, - PluginStartContract as AlertPluginStartContract, -} from '../../alerting/server'; -import { RuleRegistry } from './rule_registry'; -import { defaultIlmPolicy } from './rule_registry/defaults/ilm_policy'; -import { BaseRuleFieldMap, baseRuleFieldMap } from '../common'; -import { RuleRegistryConfig } from '.'; - -export type RuleRegistryPluginSetupContract = RuleRegistry; +import { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server'; +import { RuleDataPluginService } from './rule_data_plugin_service'; +import { RuleRegistryPluginConfig } from '.'; + +export type RuleRegistryPluginSetupContract = RuleDataPluginService; +export type RuleRegistryPluginStartContract = void; export class RuleRegistryPlugin implements Plugin { constructor(private readonly initContext: PluginInitializerContext) { this.initContext = initContext; } - public setup( - core: CoreSetup, - plugins: { alerting: AlertingPluginSetupContract } - ): RuleRegistryPluginSetupContract { + public setup(core: CoreSetup): RuleRegistryPluginSetupContract { const globalConfig = this.initContext.config.legacy.get(); - const config = this.initContext.config.get(); + const config = this.initContext.config.get(); const logger = this.initContext.logger.get(); - const rootRegistry = new RuleRegistry({ - coreSetup: core, - ilmPolicy: defaultIlmPolicy, - fieldMap: baseRuleFieldMap, + const service = new RuleDataPluginService({ + logger, + isWriteEnabled: config.unsafe.write.enabled, kibanaIndex: globalConfig.kibana.index, - name: 'alerts', - kibanaVersion: this.initContext.env.packageInfo.version, - logger: logger.get('root'), - alertingPluginSetupContract: plugins.alerting, - writeEnabled: config.unsafe.write.enabled, + getClusterClient: async () => { + const [coreStart] = await core.getStartServices(); + + return coreStart.elasticsearch.client.asInternalUser; + }, }); - return rootRegistry; - } + service.init().catch((originalError) => { + const error = new Error('Failed installing assets'); + // @ts-ignore + error.stack = originalError.stack; + logger.error(error); + }); - // Temporarily exposing alerting client as passthrough - public start(core: CoreStart, plugins: { alerting: AlertPluginStartContract }) { - return { - alerting: plugins.alerting, - }; + return service; } + public start(): RuleRegistryPluginStartContract {} + public stop() {} } diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/index.ts b/x-pack/plugins/rule_registry/server/rule_data_client/index.ts new file mode 100644 index 0000000000000..9de1c9a882289 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/rule_data_client/index.ts @@ -0,0 +1,125 @@ +/* + * 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 { TypeMapping } from '@elastic/elasticsearch/api/types'; +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; +import { IndexPatternsFetcher } from '../../../../../src/plugins/data/server'; +import { + IRuleDataClient, + RuleDataClientConstructorOptions, + RuleDataReader, + RuleDataWriter, +} from './types'; + +function getNamespacedAlias(options: { alias: string; namespace?: string }) { + return [options.alias, options.namespace].filter(Boolean).join('-'); +} + +export class RuleDataClient implements IRuleDataClient { + constructor(private readonly options: RuleDataClientConstructorOptions) {} + + private async getClusterClient() { + await this.options.ready(); + return await this.options.getClusterClient(); + } + + getReader(options: { namespace?: string } = {}): RuleDataReader { + const index = `${[this.options.alias, options.namespace].filter(Boolean).join('-')}*`; + + return { + search: async (request) => { + const clusterClient = await this.getClusterClient(); + + const { body } = (await clusterClient.search({ + ...request, + index, + })) as { body: any }; + + return body; + }, + getDynamicIndexPattern: async () => { + const clusterClient = await this.getClusterClient(); + const indexPatternsFetcher = new IndexPatternsFetcher(clusterClient); + + const fields = await indexPatternsFetcher.getFieldsForWildcard({ + pattern: index, + }); + + return { + fields, + timeFieldName: '@timestamp', + title: index, + }; + }, + }; + } + + getWriter(options: { namespace?: string } = {}): RuleDataWriter { + const { namespace } = options; + const alias = getNamespacedAlias({ alias: this.options.alias, namespace }); + return { + bulk: async (request) => { + const clusterClient = await this.getClusterClient(); + + const requestWithDefaultParameters = { + ...request, + require_alias: true, + index: alias, + }; + + return clusterClient.bulk(requestWithDefaultParameters).then((response) => { + if (response.body.errors) { + if ( + response.body.items.length === 1 && + response.body.items[0]?.index?.error?.type === 'index_not_found_exception' + ) { + return this.createOrUpdateWriteTarget({ namespace }).then(() => { + return clusterClient.bulk(requestWithDefaultParameters); + }); + } + const error = new ResponseError(response); + throw error; + } + return response; + }); + }, + }; + } + + async createOrUpdateWriteTarget({ namespace }: { namespace?: string }) { + const alias = getNamespacedAlias({ alias: this.options.alias, namespace }); + + const clusterClient = await this.getClusterClient(); + + const { body: aliasExists } = await clusterClient.indices.existsAlias({ + name: alias, + }); + + const concreteIndexName = `${alias}-000001`; + + if (!aliasExists) { + await clusterClient.indices.create({ + index: concreteIndexName, + body: { + aliases: { + [alias]: { + is_write_index: true, + }, + }, + }, + }); + } + + const { body: simulateResponse } = await clusterClient.transport.request({ + method: 'POST', + path: `/_index_template/_simulate_index/${concreteIndexName}`, + }); + + const mappings: TypeMapping = simulateResponse.template.mappings; + + await clusterClient.indices.putMapping({ index: `${alias}*`, body: mappings }); + } +} diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/types.ts b/x-pack/plugins/rule_registry/server/rule_data_client/types.ts new file mode 100644 index 0000000000000..348fca6a58188 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/rule_data_client/types.ts @@ -0,0 +1,44 @@ +/* + * 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 { ApiResponse } from '@elastic/elasticsearch'; +import { BulkRequest, BulkResponse } from '@elastic/elasticsearch/api/types'; +import { ElasticsearchClient } from 'kibana/server'; +import { FieldDescriptor } from 'src/plugins/data/server'; +import { ESSearchRequest, ESSearchResponse } from 'typings/elasticsearch'; +import { TechnicalRuleDataFieldName } from '../../common/technical_rule_data_field_names'; + +export interface RuleDataReader { + search( + request: TSearchRequest + ): Promise< + ESSearchResponse>, TSearchRequest> + >; + getDynamicIndexPattern( + target?: string + ): Promise<{ + title: string; + timeFieldName: string; + fields: FieldDescriptor[]; + }>; +} + +export interface RuleDataWriter { + bulk(request: BulkRequest): Promise>; +} + +export interface IRuleDataClient { + getReader(options?: { namespace?: string }): RuleDataReader; + getWriter(options?: { namespace?: string }): RuleDataWriter; + createOrUpdateWriteTarget(options: { namespace?: string }): Promise; +} + +export interface RuleDataClientConstructorOptions { + getClusterClient: () => Promise; + ready: () => Promise; + alias: string; +} diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index.ts new file mode 100644 index 0000000000000..f8a3ae6d4d8c2 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index.ts @@ -0,0 +1,159 @@ +/* + * 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 { ClusterPutComponentTemplate } from '@elastic/elasticsearch/api/requestParams'; +import { estypes } from '@elastic/elasticsearch'; +import { ElasticsearchClient, Logger } from 'kibana/server'; +import { DEFAULT_ASSET_NAMESPACE } from '../../common/assets'; +import { technicalComponentTemplate } from '../../common/assets/component_templates/technical_component_template'; +import { + DEFAULT_ILM_POLICY_ID, + ECS_COMPONENT_TEMPLATE_NAME, + TECHNICAL_COMPONENT_TEMPLATE_NAME, +} from '../../common/assets'; +import { ecsComponentTemplate } from '../../common/assets/component_templates/ecs_component_template'; +import { defaultLifecyclePolicy } from '../../common/assets/lifecycle_policies/default_lifecycle_policy'; +import { ClusterPutComponentTemplateBody, PutIndexTemplateRequest } from '../../common/types'; + +const BOOTSTRAP_TIMEOUT = 60000; + +interface RuleDataPluginServiceConstructorOptions { + getClusterClient: () => Promise; + logger: Logger; + isWriteEnabled: boolean; + kibanaIndex: string; +} + +function createSignal() { + let resolver: () => void; + + let ready: boolean = false; + + const promise = new Promise((resolve) => { + resolver = resolve; + }); + + function wait(): Promise { + return promise.then(() => { + ready = true; + }); + } + + function complete() { + resolver(); + } + + return { wait, complete, isReady: () => ready }; +} + +export class RuleDataPluginService { + signal = createSignal(); + + constructor(private readonly options: RuleDataPluginServiceConstructorOptions) {} + + private assertWriteEnabled() { + if (!this.isWriteEnabled) { + throw new Error('Write operations are disabled'); + } + } + + private async getClusterClient() { + return await this.options.getClusterClient(); + } + + async init() { + if (!this.isWriteEnabled) { + this.options.logger.info('Write is disabled, not installing assets'); + this.signal.complete(); + return; + } + + this.options.logger.info(`Installing assets in namespace ${this.getFullAssetName()}`); + + await this._createOrUpdateLifecyclePolicy({ + policy: this.getFullAssetName(DEFAULT_ILM_POLICY_ID), + body: defaultLifecyclePolicy, + }); + + await this._createOrUpdateComponentTemplate({ + name: this.getFullAssetName(TECHNICAL_COMPONENT_TEMPLATE_NAME), + body: technicalComponentTemplate, + }); + + await this._createOrUpdateComponentTemplate({ + name: this.getFullAssetName(ECS_COMPONENT_TEMPLATE_NAME), + body: ecsComponentTemplate, + }); + + this.options.logger.info(`Installed all assets`); + + this.signal.complete(); + } + + private async _createOrUpdateComponentTemplate( + template: ClusterPutComponentTemplate + ) { + this.assertWriteEnabled(); + + const clusterClient = await this.getClusterClient(); + this.options.logger.debug(`Installing component template ${template.name}`); + return clusterClient.cluster.putComponentTemplate(template); + } + + private async _createOrUpdateIndexTemplate(template: PutIndexTemplateRequest) { + this.assertWriteEnabled(); + + const clusterClient = await this.getClusterClient(); + this.options.logger.debug(`Installing index template ${template.name}`); + return clusterClient.indices.putIndexTemplate(template); + } + + private async _createOrUpdateLifecyclePolicy(policy: estypes.PutLifecycleRequest) { + this.assertWriteEnabled(); + const clusterClient = await this.getClusterClient(); + + this.options.logger.debug(`Installing lifecycle policy ${policy.policy}`); + return clusterClient.ilm.putLifecycle(policy); + } + + async createOrUpdateComponentTemplate( + template: ClusterPutComponentTemplate + ) { + await this.wait(); + return this._createOrUpdateComponentTemplate(template); + } + + async createOrUpdateIndexTemplate(template: PutIndexTemplateRequest) { + await this.wait(); + return this._createOrUpdateIndexTemplate(template); + } + + async createOrUpdateLifecyclePolicy(policy: estypes.PutLifecycleRequest) { + await this.wait(); + return this._createOrUpdateLifecyclePolicy(policy); + } + + isReady() { + return this.signal.isReady(); + } + + wait() { + return Promise.race([ + this.signal.wait(), + new Promise((resolve, reject) => { + setTimeout(reject, BOOTSTRAP_TIMEOUT); + }), + ]); + } + + isWriteEnabled(): boolean { + return this.options.isWriteEnabled; + } + + getFullAssetName(assetName?: string) { + return [this.options.kibanaIndex, DEFAULT_ASSET_NAMESPACE, assetName].filter(Boolean).join('-'); + } +} diff --git a/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/index.ts b/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/index.ts deleted file mode 100644 index 0d7735380b640..0000000000000 --- a/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/index.ts +++ /dev/null @@ -1,179 +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 { Either, isLeft, isRight } from 'fp-ts/lib/Either'; -import { Errors } from 'io-ts'; -import { PathReporter } from 'io-ts/lib/PathReporter'; -import { Logger } from 'kibana/server'; -import { IScopedClusterClient as ScopedClusterClient } from 'src/core/server'; -import { castArray, compact } from 'lodash'; -import { ESSearchRequest } from 'typings/elasticsearch'; -import { IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; -import { ClusterClientAdapter } from '../../../../event_log/server'; -import { TypeOfFieldMap } from '../../../common'; -import { ScopedRuleRegistryClient, EventsOf } from './types'; -import { BaseRuleFieldMap } from '../../../common'; -import { RuleRegistry } from '..'; - -const createPathReporterError = (either: Either) => { - const error = new Error(`Failed to validate alert event`); - error.stack += '\n' + PathReporter.report(either).join('\n'); - return error; -}; - -export function createScopedRuleRegistryClient({ - ruleUuids, - scopedClusterClient, - clusterClientAdapter, - indexAliasName, - indexTarget, - logger, - registry, - ruleData, -}: { - ruleUuids: string[]; - scopedClusterClient: ScopedClusterClient; - clusterClientAdapter: ClusterClientAdapter<{ - body: TypeOfFieldMap; - index: string; - }>; - indexAliasName: string; - indexTarget: string; - logger: Logger; - registry: RuleRegistry; - ruleData?: { - rule: { - id: string; - uuid: string; - category: string; - name: string; - }; - producer: string; - tags: string[]; - }; -}): ScopedRuleRegistryClient { - const fieldmapType = registry.getFieldMapType(); - - const defaults = ruleData - ? { - 'rule.uuid': ruleData.rule.uuid, - 'rule.id': ruleData.rule.id, - 'rule.name': ruleData.rule.name, - 'rule.category': ruleData.rule.category, - 'kibana.rac.producer': ruleData.producer, - tags: ruleData.tags, - } - : {}; - - const client: ScopedRuleRegistryClient = { - search: async (searchRequest) => { - const fields = [ - 'rule.id', - ...(searchRequest.body?.fields ? castArray(searchRequest.body.fields) : []), - ]; - - const response = await scopedClusterClient.asInternalUser.search({ - ...searchRequest, - index: indexTarget, - body: { - ...searchRequest.body, - query: { - bool: { - filter: [ - { terms: { 'rule.uuid': ruleUuids } }, - ...compact([searchRequest.body?.query]), - ], - }, - }, - fields, - }, - }); - - return { - body: response.body as any, - events: compact( - response.body.hits.hits.map((hit) => { - const ruleTypeId: string = hit.fields!['rule.id'][0]; - - const registryOfType = registry.getRegistryByRuleTypeId(ruleTypeId); - - if (ruleTypeId && !registryOfType) { - logger.warn( - `Could not find type ${ruleTypeId} in registry, decoding with default type` - ); - } - - const type = registryOfType?.getFieldMapType() ?? fieldmapType; - - const validation = type.decode(hit.fields); - if (isLeft(validation)) { - const error = createPathReporterError(validation); - logger.error(error); - return undefined; - } - return type.encode(validation.right); - }) - ) as EventsOf, - }; - }, - getDynamicIndexPattern: async () => { - const indexPatternsFetcher = new IndexPatternsFetcher(scopedClusterClient.asInternalUser); - - const fields = await indexPatternsFetcher.getFieldsForWildcard({ - pattern: indexTarget, - }); - - return { - fields, - timeFieldName: '@timestamp', - title: indexTarget, - }; - }, - index: (doc) => { - const validation = fieldmapType.decode({ - ...doc, - ...defaults, - }); - - if (isLeft(validation)) { - throw createPathReporterError(validation); - } - - clusterClientAdapter.indexDocument({ - body: validation.right, - index: indexAliasName, - }); - }, - bulkIndex: (docs) => { - const validations = docs.map((doc) => { - return fieldmapType.decode({ - ...doc, - ...defaults, - }); - }); - - const errors = compact( - validations.map((validation) => - isLeft(validation) ? createPathReporterError(validation) : null - ) - ); - - errors.forEach((error) => { - logger.error(error); - }); - - const operations = compact( - validations.map((validation) => (isRight(validation) ? validation.right : null)) - ).map((doc) => ({ body: doc, index: indexAliasName })); - - return clusterClientAdapter.indexDocuments(operations); - }, - }; - - // @ts-expect-error: We can't use ScopedRuleRegistryClient - // when creating the client, due to #41693 which will be fixed in 4.2 - return client; -} diff --git a/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/types.ts b/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/types.ts deleted file mode 100644 index f7b2394fe3a32..0000000000000 --- a/x-pack/plugins/rule_registry/server/rule_registry/create_scoped_rule_registry_client/types.ts +++ /dev/null @@ -1,57 +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 { FieldDescriptor } from 'src/plugins/data/server'; -import { ESSearchRequest, ESSearchResponse } from 'typings/elasticsearch'; -import { - PatternsUnionOf, - PickWithPatterns, - OutputOfFieldMap, - BaseRuleFieldMap, -} from '../../../common'; - -export type PrepopulatedRuleEventFields = keyof Pick< - BaseRuleFieldMap, - 'rule.uuid' | 'rule.id' | 'rule.name' | 'rule.category' | 'kibana.rac.producer' ->; - -type FieldsOf = - | Array<{ field: PatternsUnionOf } | PatternsUnionOf> - | PatternsUnionOf; - -type Fields = Array<{ field: TPattern } | TPattern> | TPattern; - -type FieldsESSearchRequest = ESSearchRequest & { - body?: { fields: FieldsOf }; -}; - -export type EventsOf< - TFieldsESSearchRequest extends ESSearchRequest, - TFieldMap extends BaseRuleFieldMap -> = TFieldsESSearchRequest extends { body: { fields: infer TFields } } - ? TFields extends Fields - ? Array>> - : never - : never; - -export interface ScopedRuleRegistryClient { - search>( - request: TSearchRequest - ): Promise<{ - body: ESSearchResponse; - events: EventsOf; - }>; - getDynamicIndexPattern(): Promise<{ - title: string; - timeFieldName: string; - fields: FieldDescriptor[]; - }>; - index(doc: Omit, PrepopulatedRuleEventFields>): void; - bulkIndex( - doc: Array, PrepopulatedRuleEventFields>> - ): Promise; -} diff --git a/x-pack/plugins/rule_registry/server/rule_registry/index.ts b/x-pack/plugins/rule_registry/server/rule_registry/index.ts deleted file mode 100644 index bbc381f60a809..0000000000000 --- a/x-pack/plugins/rule_registry/server/rule_registry/index.ts +++ /dev/null @@ -1,328 +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 { CoreSetup, Logger, RequestHandlerContext } from 'kibana/server'; -import { inspect } from 'util'; -import { AlertsClient } from '../../../alerting/server'; -import { SpacesServiceStart } from '../../../spaces/server'; -import { - ActionVariable, - AlertInstanceState, - AlertTypeParams, - AlertTypeState, -} from '../../../alerting/common'; -import { createReadySignal, ClusterClientAdapter } from '../../../event_log/server'; -import { ILMPolicy } from './types'; -import { RuleParams, RuleType } from '../types'; -import { - mergeFieldMaps, - TypeOfFieldMap, - FieldMap, - FieldMapType, - BaseRuleFieldMap, - runtimeTypeFromFieldMap, -} from '../../common'; -import { mappingFromFieldMap } from './field_map/mapping_from_field_map'; -import { PluginSetupContract as AlertingPluginSetupContract } from '../../../alerting/server'; -import { createScopedRuleRegistryClient } from './create_scoped_rule_registry_client'; -import { ScopedRuleRegistryClient } from './create_scoped_rule_registry_client/types'; - -interface RuleRegistryOptions { - kibanaIndex: string; - kibanaVersion: string; - name: string; - logger: Logger; - coreSetup: CoreSetup; - spacesStart?: SpacesServiceStart; - fieldMap: TFieldMap; - ilmPolicy: ILMPolicy; - alertingPluginSetupContract: AlertingPluginSetupContract; - writeEnabled: boolean; -} - -export class RuleRegistry { - private readonly esAdapter: ClusterClientAdapter<{ - body: TypeOfFieldMap; - index: string; - }>; - private readonly children: Array> = []; - private readonly types: Array> = []; - - private readonly fieldmapType: FieldMapType; - - constructor(private readonly options: RuleRegistryOptions) { - const { logger, coreSetup } = options; - - this.fieldmapType = runtimeTypeFromFieldMap(options.fieldMap); - - const { wait, signal } = createReadySignal(); - - this.esAdapter = new ClusterClientAdapter<{ - body: TypeOfFieldMap; - index: string; - }>({ - wait, - elasticsearchClientPromise: coreSetup - .getStartServices() - .then(([{ elasticsearch }]) => elasticsearch.client.asInternalUser), - logger: logger.get('esAdapter'), - }); - - if (this.options.writeEnabled) { - this.initialize() - .then(() => { - this.options.logger.debug('Bootstrapped alerts index'); - signal(true); - }) - .catch((err) => { - logger.error(inspect(err, { depth: null })); - signal(false); - }); - } else { - logger.debug('Write disabled, indices are not being bootstrapped'); - } - } - - private getEsNames() { - const base = [this.options.kibanaIndex, this.options.name]; - const indexTarget = `${base.join('-')}*`; - const indexAliasName = [...base, this.options.kibanaVersion.toLowerCase()].join('-'); - const policyName = [...base, 'policy'].join('-'); - - return { - indexAliasName, - indexTarget, - policyName, - }; - } - - private async initialize() { - const { indexAliasName, policyName } = this.getEsNames(); - - const ilmPolicyExists = await this.esAdapter.doesIlmPolicyExist(policyName); - - if (!ilmPolicyExists) { - await this.esAdapter.createIlmPolicy( - policyName, - (this.options.ilmPolicy as unknown) as Record - ); - } - - const templateExists = await this.esAdapter.doesIndexTemplateExist(indexAliasName); - - const mappings = mappingFromFieldMap(this.options.fieldMap); - - const esClient = (await this.options.coreSetup.getStartServices())[0].elasticsearch.client - .asInternalUser; - - if (!templateExists) { - await this.esAdapter.createIndexTemplate(indexAliasName, { - index_patterns: [`${indexAliasName}-*`], - settings: { - number_of_shards: 1, - auto_expand_replicas: '0-1', - 'index.lifecycle.name': policyName, - 'index.lifecycle.rollover_alias': indexAliasName, - 'sort.field': '@timestamp', - 'sort.order': 'desc', - }, - mappings, - }); - } else { - await esClient.indices.putTemplate({ - name: indexAliasName, - body: { - index_patterns: [`${indexAliasName}-*`], - mappings, - }, - create: false, - }); - } - - const aliasExists = await this.esAdapter.doesAliasExist(indexAliasName); - - if (!aliasExists) { - await this.esAdapter.createIndex(`${indexAliasName}-000001`, { - aliases: { - [indexAliasName]: { - is_write_index: true, - }, - }, - }); - } else { - const { body: aliases } = (await esClient.indices.getAlias({ - index: indexAliasName, - })) as { body: Record }> }; - - const writeIndex = Object.entries(aliases).find( - ([indexName, alias]) => alias.aliases[indexAliasName]?.is_write_index === true - )![0]; - - const { body: fieldsInWriteIndex } = await esClient.fieldCaps({ - index: writeIndex, - fields: '*', - }); - - const fieldsNotOrDifferentInIndex = Object.entries(this.options.fieldMap).filter( - ([fieldName, descriptor]) => { - return ( - !fieldsInWriteIndex.fields[fieldName] || - !fieldsInWriteIndex.fields[fieldName][descriptor.type] - ); - } - ); - - if (fieldsNotOrDifferentInIndex.length > 0) { - this.options.logger.debug( - `Some fields were not found in write index mapping: ${Object.keys( - Object.fromEntries(fieldsNotOrDifferentInIndex) - ).join(',')}` - ); - this.options.logger.info(`Updating index mapping due to new fields`); - - await esClient.indices.putMapping({ - index: indexAliasName, - body: mappings, - }); - } - } - } - - getFieldMapType() { - return this.fieldmapType; - } - - getRuleTypeById(ruleTypeId: string) { - return this.types.find((type) => type.id === ruleTypeId); - } - - getRegistryByRuleTypeId(ruleTypeId: string): RuleRegistry | undefined { - if (this.getRuleTypeById(ruleTypeId)) { - return this; - } - - return this.children.find((child) => child.getRegistryByRuleTypeId(ruleTypeId)); - } - - async createScopedRuleRegistryClient({ - context, - alertsClient, - }: { - context: RequestHandlerContext; - alertsClient: AlertsClient; - }): Promise | undefined> { - if (!this.options.writeEnabled) { - return undefined; - } - const { indexAliasName, indexTarget } = this.getEsNames(); - - const frameworkAlerts = ( - await alertsClient.find({ - options: { - perPage: 1000, - }, - }) - ).data; - - return createScopedRuleRegistryClient({ - ruleUuids: frameworkAlerts.map((frameworkAlert) => frameworkAlert.id), - scopedClusterClient: context.core.elasticsearch.client, - clusterClientAdapter: this.esAdapter, - registry: this, - indexAliasName, - indexTarget, - logger: this.options.logger, - }); - } - - registerType( - type: RuleType - ) { - const logger = this.options.logger.get(type.id); - - const { indexAliasName, indexTarget } = this.getEsNames(); - - this.types.push(type); - - this.options.alertingPluginSetupContract.registerType< - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - { [key in TActionVariable['name']]: any }, - string - >({ - ...type, - executor: async (executorOptions) => { - const { services, alertId, name, tags } = executorOptions; - - const rule = { - id: type.id, - uuid: alertId, - category: type.name, - name, - }; - - const producer = type.producer; - - return type.executor({ - ...executorOptions, - rule, - producer, - services: { - ...services, - logger, - ...(this.options.writeEnabled - ? { - scopedRuleRegistryClient: createScopedRuleRegistryClient({ - scopedClusterClient: services.scopedClusterClient, - ruleUuids: [rule.uuid], - clusterClientAdapter: this.esAdapter, - registry: this, - indexAliasName, - indexTarget, - ruleData: { - producer, - rule, - tags, - }, - logger: this.options.logger, - }), - } - : {}), - }, - }); - }, - }); - } - - create({ - name, - fieldMap, - ilmPolicy, - }: { - name: string; - fieldMap: TNextFieldMap; - ilmPolicy?: ILMPolicy; - }): RuleRegistry { - const mergedFieldMap = fieldMap - ? mergeFieldMaps(this.options.fieldMap, fieldMap) - : this.options.fieldMap; - - const child = new RuleRegistry({ - ...this.options, - logger: this.options.logger.get(name), - name: [this.options.name, name].filter(Boolean).join('-'), - fieldMap: mergedFieldMap, - ...(ilmPolicy ? { ilmPolicy } : {}), - }); - - this.children.push(child); - - // @ts-expect-error could be instantiated with a different subtype of constraint - return child; - } -} diff --git a/x-pack/plugins/rule_registry/server/rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory.ts b/x-pack/plugins/rule_registry/server/rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory.ts deleted file mode 100644 index 65eaf0964cfca..0000000000000 --- a/x-pack/plugins/rule_registry/server/rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory.ts +++ /dev/null @@ -1,235 +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 * as t from 'io-ts'; -import { isLeft } from 'fp-ts/lib/Either'; -import v4 from 'uuid/v4'; -import { Mutable } from 'utility-types'; -import { AlertInstance } from '../../../../alerting/server'; -import { ActionVariable, AlertInstanceState } from '../../../../alerting/common'; -import { RuleParams, RuleType } from '../../types'; -import { BaseRuleFieldMap, OutputOfFieldMap } from '../../../common'; -import { PrepopulatedRuleEventFields } from '../create_scoped_rule_registry_client/types'; -import { RuleRegistry } from '..'; - -type UserDefinedAlertFields = Omit< - OutputOfFieldMap, - PrepopulatedRuleEventFields | 'kibana.rac.alert.id' | 'kibana.rac.alert.uuid' | '@timestamp' ->; - -type LifecycleAlertService< - TFieldMap extends BaseRuleFieldMap, - TActionVariable extends ActionVariable -> = (alert: { - id: string; - fields: UserDefinedAlertFields; -}) => AlertInstance; - -type CreateLifecycleRuleType = < - TRuleParams extends RuleParams, - TActionVariable extends ActionVariable ->( - type: RuleType< - TFieldMap, - TRuleParams, - TActionVariable, - { alertWithLifecycle: LifecycleAlertService } - > -) => RuleType; - -const trackedAlertStateRt = t.type({ - alertId: t.string, - alertUuid: t.string, - started: t.string, -}); - -const wrappedStateRt = t.type({ - wrapped: t.record(t.string, t.unknown), - trackedAlerts: t.record(t.string, trackedAlertStateRt), -}); - -export function createLifecycleRuleTypeFactory< - TRuleRegistry extends RuleRegistry ->(): TRuleRegistry extends RuleRegistry - ? CreateLifecycleRuleType - : never; - -export function createLifecycleRuleTypeFactory(): CreateLifecycleRuleType { - return (type) => { - return { - ...type, - executor: async (options) => { - const { - services: { scopedRuleRegistryClient, alertInstanceFactory, logger }, - state: previousState, - rule, - } = options; - - const decodedState = wrappedStateRt.decode(previousState); - - const state = isLeft(decodedState) - ? { - wrapped: previousState, - trackedAlerts: {}, - } - : decodedState.right; - - const currentAlerts: Record< - string, - UserDefinedAlertFields & { 'kibana.rac.alert.id': string } - > = {}; - - const timestamp = options.startedAt.toISOString(); - - const nextWrappedState = await type.executor({ - ...options, - state: state.wrapped, - services: { - ...options.services, - alertWithLifecycle: ({ id, fields }) => { - currentAlerts[id] = { - ...fields, - 'kibana.rac.alert.id': id, - }; - return alertInstanceFactory(id); - }, - }, - }); - - const currentAlertIds = Object.keys(currentAlerts); - const trackedAlertIds = Object.keys(state.trackedAlerts); - const newAlertIds = currentAlertIds.filter((alertId) => !trackedAlertIds.includes(alertId)); - - const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))]; - - const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter( - (trackedAlertState) => !currentAlerts[trackedAlertState.alertId] - ); - - logger.debug( - `Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)` - ); - - const alertsDataMap: Record> = { - ...currentAlerts, - }; - - if (scopedRuleRegistryClient && trackedAlertStatesOfRecovered.length) { - const { events } = await scopedRuleRegistryClient.search({ - body: { - query: { - bool: { - filter: [ - { - term: { - 'rule.uuid': rule.uuid, - }, - }, - { - terms: { - 'kibana.rac.alert.uuid': trackedAlertStatesOfRecovered.map( - (trackedAlertState) => trackedAlertState.alertUuid - ), - }, - }, - ], - }, - }, - size: trackedAlertStatesOfRecovered.length, - collapse: { - field: 'kibana.rac.alert.uuid', - }, - _source: false, - fields: ['*'], - sort: { - '@timestamp': 'desc' as const, - }, - }, - }); - - events.forEach((event) => { - const alertId = event['kibana.rac.alert.id']!; - alertsDataMap[alertId] = event; - }); - } - - const eventsToIndex: Array> = allAlertIds.map( - (alertId) => { - const alertData = alertsDataMap[alertId]; - - if (!alertData) { - logger.warn(`Could not find alert data for ${alertId}`); - } - - const event: Mutable> = { - ...alertData, - '@timestamp': timestamp, - 'event.kind': 'state', - 'kibana.rac.alert.id': alertId, - }; - - const isNew = !state.trackedAlerts[alertId]; - const isRecovered = !currentAlerts[alertId]; - const isActiveButNotNew = !isNew && !isRecovered; - const isActive = !isRecovered; - - const { alertUuid, started } = state.trackedAlerts[alertId] ?? { - alertUuid: v4(), - started: timestamp, - }; - - event['kibana.rac.alert.start'] = started; - event['kibana.rac.alert.uuid'] = alertUuid; - - if (isNew) { - event['event.action'] = 'open'; - } - - if (isRecovered) { - event['kibana.rac.alert.end'] = timestamp; - event['event.action'] = 'close'; - event['kibana.rac.alert.status'] = 'closed'; - } - - if (isActiveButNotNew) { - event['event.action'] = 'active'; - } - - if (isActive) { - event['kibana.rac.alert.status'] = 'open'; - } - - event['kibana.rac.alert.duration.us'] = - (options.startedAt.getTime() - new Date(event['kibana.rac.alert.start']!).getTime()) * - 1000; - - return event; - } - ); - - if (eventsToIndex.length && scopedRuleRegistryClient) { - await scopedRuleRegistryClient.bulkIndex(eventsToIndex); - } - - const nextTrackedAlerts = Object.fromEntries( - eventsToIndex - .filter((event) => event['kibana.rac.alert.status'] !== 'closed') - .map((event) => { - const alertId = event['kibana.rac.alert.id']!; - const alertUuid = event['kibana.rac.alert.uuid']!; - const started = new Date(event['kibana.rac.alert.start']!).toISOString(); - return [alertId, { alertId, alertUuid, started }]; - }) - ); - - return { - wrapped: nextWrappedState, - trackedAlerts: nextTrackedAlerts, - }; - }, - }; - }; -} diff --git a/x-pack/plugins/rule_registry/server/rule_registry/types.ts b/x-pack/plugins/rule_registry/server/rule_registry/types.ts deleted file mode 100644 index ec7293d1c1d4c..0000000000000 --- a/x-pack/plugins/rule_registry/server/rule_registry/types.ts +++ /dev/null @@ -1,42 +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. - */ - -export interface Mappings { - dynamic: 'strict' | boolean; - properties: Record; -} - -enum ILMPolicyPhase { - hot = 'hot', - delete = 'delete', -} - -enum ILMPolicyAction { - rollover = 'rollover', - delete = 'delete', -} - -interface ILMActionOptions { - [ILMPolicyAction.rollover]: { - max_size: string; - max_age: string; - }; - [ILMPolicyAction.delete]: {}; -} - -export interface ILMPolicy { - policy: { - phases: Record< - ILMPolicyPhase, - { - actions: { - [key in keyof ILMActionOptions]?: ILMActionOptions[key]; - }; - } - >; - }; -} diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts index dd54046365d98..959c05fd1334e 100644 --- a/x-pack/plugins/rule_registry/server/types.ts +++ b/x-pack/plugins/rule_registry/server/types.ts @@ -4,97 +4,37 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { Type, TypeOf } from '@kbn/config-schema'; -import { Logger } from 'kibana/server'; + import { - ActionVariable, AlertInstanceContext, AlertInstanceState, AlertTypeParams, AlertTypeState, } from '../../alerting/common'; -import { ActionGroup, AlertExecutorOptions } from '../../alerting/server'; -import { RuleRegistry } from './rule_registry'; -import { ScopedRuleRegistryClient } from './rule_registry/create_scoped_rule_registry_client/types'; -import { BaseRuleFieldMap } from '../common'; - -export type RuleParams = Type; +import { AlertType } from '../../alerting/server'; -type TypeOfRuleParams = TypeOf; +type SimpleAlertType< + TParams extends AlertTypeParams = {}, + TAlertInstanceContext extends AlertInstanceContext = {} +> = AlertType; -type RuleExecutorServices< - TFieldMap extends BaseRuleFieldMap, - TActionVariable extends ActionVariable -> = AlertExecutorOptions< - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - { [key in TActionVariable['name']]: any }, - string ->['services'] & { - logger: Logger; - scopedRuleRegistryClient?: ScopedRuleRegistryClient; -}; - -type PassthroughAlertExecutorOptions = Pick< - AlertExecutorOptions< - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - AlertInstanceContext, - string - >, - 'previousStartedAt' | 'startedAt' | 'state' ->; - -type RuleExecutorFunction< - TFieldMap extends BaseRuleFieldMap, - TRuleParams extends RuleParams, - TActionVariable extends ActionVariable, - TAdditionalRuleExecutorServices extends Record +export type AlertTypeExecutor< + TParams extends AlertTypeParams = {}, + TAlertInstanceContext extends AlertInstanceContext = {}, + TServices extends Record = {} > = ( - options: PassthroughAlertExecutorOptions & { - services: RuleExecutorServices & TAdditionalRuleExecutorServices; - params: TypeOfRuleParams; - rule: { - id: string; - uuid: string; - name: string; - category: string; - }; - producer: string; + options: Parameters['executor']>[0] & { + services: TServices; } -) => Promise>; - -interface RuleTypeBase { - id: string; - name: string; - actionGroups: Array>; - defaultActionGroupId: string; - producer: string; - minimumLicenseRequired: 'basic' | 'gold' | 'trial'; -} - -export type RuleType< - TFieldMap extends BaseRuleFieldMap, - TRuleParams extends RuleParams, - TActionVariable extends ActionVariable, - TAdditionalRuleExecutorServices extends Record = {} -> = RuleTypeBase & { - validate: { - params: TRuleParams; - }; - actionVariables: { - context: TActionVariable[]; - }; - executor: RuleExecutorFunction< - TFieldMap, - TRuleParams, - TActionVariable, - TAdditionalRuleExecutorServices - >; +) => Promise; + +export type AlertTypeWithExecutor< + TParams extends AlertTypeParams = {}, + TAlertInstanceContext extends AlertInstanceContext = {}, + TServices extends Record = {} +> = Omit< + AlertType, + 'executor' +> & { + executor: AlertTypeExecutor; }; - -export type FieldMapOf< - TRuleRegistry extends RuleRegistry -> = TRuleRegistry extends RuleRegistry ? TFieldMap : never; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts new file mode 100644 index 0000000000000..b523dd6770b9f --- /dev/null +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts @@ -0,0 +1,246 @@ +/* + * 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 { Logger } from '@kbn/logging'; +import { isLeft } from 'fp-ts/lib/Either'; +import * as t from 'io-ts'; +import { Mutable } from 'utility-types'; +import v4 from 'uuid/v4'; +import { AlertInstance } from '../../../alerting/server'; +import { RuleDataClient } from '..'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertTypeParams, +} from '../../../alerting/common'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_ID, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + EVENT_ACTION, + EVENT_KIND, + RULE_UUID, + TIMESTAMP, +} from '../../common/technical_rule_data_field_names'; +import { AlertTypeWithExecutor } from '../types'; +import { ParsedTechnicalFields, parseTechnicalFields } from '../../common/parse_technical_fields'; +import { getRuleExecutorData } from './get_rule_executor_data'; + +type LifecycleAlertService> = (alert: { + id: string; + fields: Record; +}) => AlertInstance; + +const trackedAlertStateRt = t.type({ + alertId: t.string, + alertUuid: t.string, + started: t.string, +}); + +const wrappedStateRt = t.type({ + wrapped: t.record(t.string, t.unknown), + trackedAlerts: t.record(t.string, trackedAlertStateRt), +}); + +type CreateLifecycleRuleTypeFactory = (options: { + ruleDataClient: RuleDataClient; + logger: Logger; +}) => < + TParams extends AlertTypeParams, + TAlertInstanceContext extends AlertInstanceContext, + TServices extends { alertWithLifecycle: LifecycleAlertService } +>( + type: AlertTypeWithExecutor +) => AlertTypeWithExecutor; + +export const createLifecycleRuleTypeFactory: CreateLifecycleRuleTypeFactory = ({ + logger, + ruleDataClient, +}) => (type) => { + return { + ...type, + executor: async (options) => { + const { + services: { alertInstanceFactory }, + state: previousState, + } = options; + + const ruleExecutorData = getRuleExecutorData(type, options); + + const decodedState = wrappedStateRt.decode(previousState); + + const state = isLeft(decodedState) + ? { + wrapped: previousState, + trackedAlerts: {}, + } + : decodedState.right; + + const currentAlerts: Record = {}; + + const timestamp = options.startedAt.toISOString(); + + const nextWrappedState = await type.executor({ + ...options, + state: state.wrapped, + services: { + ...options.services, + alertWithLifecycle: ({ id, fields }) => { + currentAlerts[id] = { + ...fields, + [ALERT_ID]: id, + }; + return alertInstanceFactory(id); + }, + }, + }); + + const currentAlertIds = Object.keys(currentAlerts); + const trackedAlertIds = Object.keys(state.trackedAlerts); + const newAlertIds = currentAlertIds.filter((alertId) => !trackedAlertIds.includes(alertId)); + + const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))]; + + const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter( + (trackedAlertState) => !currentAlerts[trackedAlertState.alertId] + ); + + logger.debug( + `Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)` + ); + + const alertsDataMap: Record< + string, + { + [ALERT_ID]: string; + } + > = { + ...currentAlerts, + }; + + if (trackedAlertStatesOfRecovered.length) { + const { hits } = await ruleDataClient.getReader().search({ + body: { + query: { + bool: { + filter: [ + { + term: { + [RULE_UUID]: ruleExecutorData[RULE_UUID], + }, + }, + { + terms: { + [ALERT_UUID]: trackedAlertStatesOfRecovered.map( + (trackedAlertState) => trackedAlertState.alertUuid + ), + }, + }, + ], + }, + }, + size: trackedAlertStatesOfRecovered.length, + collapse: { + field: ALERT_UUID, + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + sort: { + [TIMESTAMP]: 'desc' as const, + }, + }, + allow_no_indices: true, + }); + + hits.hits.forEach((hit) => { + const fields = parseTechnicalFields(hit.fields); + const alertId = fields[ALERT_ID]!; + alertsDataMap[alertId] = { + ...fields, + [ALERT_ID]: alertId, + }; + }); + } + + const eventsToIndex = allAlertIds.map((alertId) => { + const alertData = alertsDataMap[alertId]; + + if (!alertData) { + logger.warn(`Could not find alert data for ${alertId}`); + } + + const event: Mutable = { + ...alertData, + ...ruleExecutorData, + [TIMESTAMP]: timestamp, + [EVENT_KIND]: 'state', + [ALERT_ID]: alertId, + }; + + const isNew = !state.trackedAlerts[alertId]; + const isRecovered = !currentAlerts[alertId]; + const isActiveButNotNew = !isNew && !isRecovered; + const isActive = !isRecovered; + + const { alertUuid, started } = state.trackedAlerts[alertId] ?? { + alertUuid: v4(), + started: timestamp, + }; + + event[ALERT_START] = started; + event[ALERT_UUID] = alertUuid; + + if (isNew) { + event[EVENT_ACTION] = 'open'; + } + + if (isRecovered) { + event[ALERT_END] = timestamp; + event[EVENT_ACTION] = 'close'; + event[ALERT_STATUS] = 'closed'; + } + + if (isActiveButNotNew) { + event[EVENT_ACTION] = 'active'; + } + + if (isActive) { + event[ALERT_STATUS] = 'open'; + } + + event[ALERT_DURATION] = + (options.startedAt.getTime() - new Date(event[ALERT_START]!).getTime()) * 1000; + + return event; + }); + + if (eventsToIndex.length) { + await ruleDataClient.getWriter().bulk({ + body: eventsToIndex.flatMap((event) => [{ index: {} }, event]), + }); + } + + const nextTrackedAlerts = Object.fromEntries( + eventsToIndex + .filter((event) => event[ALERT_STATUS] !== 'closed') + .map((event) => { + const alertId = event[ALERT_ID]!; + const alertUuid = event[ALERT_UUID]!; + const started = new Date(event[ALERT_START]!).toISOString(); + return [alertId, { alertId, alertUuid, started }]; + }) + ); + + return { + wrapped: nextWrappedState, + trackedAlerts: nextTrackedAlerts, + }; + }, + }; +}; diff --git a/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts b/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts new file mode 100644 index 0000000000000..1ea640add7b48 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts @@ -0,0 +1,39 @@ +/* + * 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 { + PRODUCER, + RULE_CATEGORY, + RULE_ID, + RULE_NAME, + RULE_UUID, + TAGS, +} from '../../common/technical_rule_data_field_names'; +import { AlertTypeExecutor, AlertTypeWithExecutor } from '../types'; + +export interface RuleExecutorData { + [RULE_CATEGORY]: string; + [RULE_ID]: string; + [RULE_UUID]: string; + [RULE_NAME]: string; + [PRODUCER]: string; + [TAGS]: string[]; +} + +export function getRuleExecutorData( + type: AlertTypeWithExecutor, + options: Parameters[0] +) { + return { + [RULE_ID]: type.id, + [RULE_UUID]: options.alertId, + [RULE_CATEGORY]: type.name, + [RULE_NAME]: options.name, + [TAGS]: options.tags, + [PRODUCER]: type.producer, + }; +} diff --git a/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts b/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts new file mode 100644 index 0000000000000..02ff6b10f74cf --- /dev/null +++ b/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts @@ -0,0 +1,39 @@ +/* + * 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 { AlertInstanceContext, AlertTypeParams } from '../../../alerting/common'; +import { RuleDataClient } from '../rule_data_client'; +import { AlertTypeWithExecutor } from '../types'; + +export const withRuleDataClientFactory = (ruleDataClient: RuleDataClient) => < + TParams extends AlertTypeParams, + TAlertInstanceContext extends AlertInstanceContext, + TServices extends Record = {} +>( + type: AlertTypeWithExecutor< + TParams, + TAlertInstanceContext, + TServices & { ruleDataClient: RuleDataClient } + > +): AlertTypeWithExecutor< + TParams, + TAlertInstanceContext, + TServices & { ruleDataClient: RuleDataClient } +> => { + return { + ...type, + executor: (options) => { + return type.executor({ + ...options, + services: { + ...options.services, + ruleDataClient, + }, + }); + }, + }; +}; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx index 221e0ec245fd4..2ac99d4f63fba 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import { EuiButtonIcon, EuiTextArea } from '@elastic/eui'; +import { EuiButtonIcon, EuiComboBox, EuiTextArea } from '@elastic/eui'; import React from 'react'; -import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; +import { findTestSubject, mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test/jest'; +import { indicesAPIClientMock } from '../../../index.mock'; import { RoleValidator } from '../../validate_role'; import { IndexPrivilegeForm } from './index_privilege_form'; @@ -25,7 +26,7 @@ test('it renders without crashing', () => { }, formIndex: 0, indexPatterns: [], - availableFields: [], + indicesAPIClient: indicesAPIClientMock.create(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], isRoleReadOnly: false, allowDocumentLevelSecurity: true, @@ -52,7 +53,7 @@ test('it allows for custom index privileges', () => { }, formIndex: 0, indexPatterns: [], - availableFields: [], + indicesAPIClient: indicesAPIClientMock.create(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], isRoleReadOnly: false, allowDocumentLevelSecurity: true, @@ -86,7 +87,7 @@ describe('delete button', () => { }, formIndex: 0, indexPatterns: [], - availableFields: [], + indicesAPIClient: indicesAPIClientMock.create(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], isRoleReadOnly: false, allowDocumentLevelSecurity: true, @@ -138,7 +139,7 @@ describe(`document level security`, () => { }, formIndex: 0, indexPatterns: [], - availableFields: [], + indicesAPIClient: indicesAPIClientMock.create(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], isRoleReadOnly: false, allowDocumentLevelSecurity: true, @@ -197,7 +198,7 @@ describe('field level security', () => { }, formIndex: 0, indexPatterns: [], - availableFields: [], + indicesAPIClient: indicesAPIClientMock.create(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], isRoleReadOnly: false, allowDocumentLevelSecurity: true, @@ -208,19 +209,21 @@ describe('field level security', () => { intl: {} as any, }; - test(`inputs are hidden when FLS is not allowed`, () => { + test(`inputs are hidden when FLS is not allowed, and fields are not queried`, async () => { const testProps = { ...props, allowFieldLevelSecurity: false, }; const wrapper = mountWithIntl(); + await nextTick(); expect(wrapper.find('EuiSwitch[data-test-subj="restrictFieldsQuery0"]')).toHaveLength(0); expect(wrapper.find('.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(0); expect(wrapper.find('.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(0); + expect(testProps.indicesAPIClient.getFields).not.toHaveBeenCalled(); }); - test('only the switch is shown when allowed, and FLS is empty', () => { + test('renders the FLS switch when available, but collapsed when no fields are selected', async () => { const testProps = { ...props, indexPrivilege: { @@ -230,19 +233,126 @@ describe('field level security', () => { }; const wrapper = mountWithIntl(); + await nextTick(); expect(wrapper.find('EuiSwitch[data-test-subj="restrictFieldsQuery0"]')).toHaveLength(1); expect(wrapper.find('.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(0); expect(wrapper.find('.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(0); + expect(testProps.indicesAPIClient.getFields).not.toHaveBeenCalled(); }); - test('inputs are shown when allowed', () => { + test('FLS inputs are shown when allowed', async () => { const testProps = { ...props, }; const wrapper = mountWithIntl(); + await nextTick(); expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1); expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(1); + expect(testProps.indicesAPIClient.getFields).not.toHaveBeenCalled(); + }); + + test('does not query for available fields when a request is already in flight', async () => { + jest.useFakeTimers(); + + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + names: ['foo', 'bar-*'], + }, + indicesAPIClient: indicesAPIClientMock.create(), + }; + + testProps.indicesAPIClient.getFields.mockImplementation(async () => { + return new Promise((resolve) => + setTimeout(() => { + resolve(['foo']); + }, 5000) + ); + }); + + const wrapper = mountWithIntl(); + await nextTick(); + expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1); + expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(1); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(1); + + findTestSubject(wrapper, 'fieldInput0').simulate('focus'); + jest.advanceTimersByTime(2000); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(1); + + findTestSubject(wrapper, 'fieldInput0').simulate('focus'); + jest.advanceTimersByTime(4000); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(1); + }); + + test('queries for available fields when mounted, and FLS is available', async () => { + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + names: ['foo', 'bar-*'], + }, + indicesAPIClient: indicesAPIClientMock.create(), + }; + + testProps.indicesAPIClient.getFields.mockResolvedValue(['a', 'b', 'c']); + + const wrapper = mountWithIntl(); + await nextTick(); + expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1); + expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(1); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(1); + }); + + test('does not query for available fields when mounted, and FLS is unavailable', async () => { + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + names: ['foo', 'bar-*'], + }, + indicesAPIClient: indicesAPIClientMock.create(), + allowFieldLevelSecurity: false, + }; + + testProps.indicesAPIClient.getFields.mockResolvedValue(['a', 'b', 'c']); + + const wrapper = mountWithIntl(); + await nextTick(); + expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(0); + expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(0); + expect(testProps.indicesAPIClient.getFields).not.toHaveBeenCalled(); + }); + + test('queries for available fields when the set of index patterns change', async () => { + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + names: ['foo', 'bar-*'], + }, + indexPatterns: ['foo', 'bar-*', 'newPattern'], + indicesAPIClient: indicesAPIClientMock.create(), + }; + + testProps.indicesAPIClient.getFields.mockResolvedValue(['a', 'b', 'c']); + + const wrapper = mountWithIntl(); + await nextTick(); + expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1); + expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(1); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(1); + + wrapper + .find(EuiComboBox) + .filterWhere((item) => item.props()['data-test-subj'] === 'indicesInput0') + .props().onChange!([{ label: 'newPattern', value: 'newPattern' }]); + + await nextTick(); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledTimes(2); + expect(testProps.indicesAPIClient.getFields).toHaveBeenCalledWith('newPattern'); }); test('it displays a warning when no fields are granted', () => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx index 697b5c1cac347..811d5590b9a33 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx @@ -23,8 +23,10 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import type { PublicMethodsOf } from '@kbn/utility-types'; import type { RoleIndexPrivilege } from '../../../../../../common/model'; +import type { IndicesAPIClient } from '../../../indices_api_client'; import type { RoleValidator } from '../../validate_role'; const fromOption = (option: any) => option.label; @@ -35,7 +37,7 @@ interface Props { indexPrivilege: RoleIndexPrivilege; indexPatterns: string[]; availableIndexPrivileges: string[]; - availableFields: string[]; + indicesAPIClient: PublicMethodsOf; onChange: (indexPrivilege: RoleIndexPrivilege) => void; onDelete: () => void; isRoleReadOnly: boolean; @@ -50,9 +52,16 @@ interface State { grantedFields: string[]; exceptedFields: string[]; documentQuery?: string; + isFieldListLoading: boolean; + flsOptions: string[]; } export class IndexPrivilegeForm extends Component { + // This is distinct from the field within `this.state`. + // We want to make sure that only one request for fields is in-flight at a time, + // and relying on state for this is error prone. + private isFieldListLoading: boolean = false; + constructor(props: Props) { super(props); @@ -64,9 +73,17 @@ export class IndexPrivilegeForm extends Component { grantedFields: grant, exceptedFields: except, documentQuery: props.indexPrivilege.query, + isFieldListLoading: false, + flsOptions: [], }; } + public componentDidMount() { + if (this.state.fieldSecurityExpanded && this.props.allowFieldLevelSecurity) { + this.loadFLSOptions(this.props.indexPrivilege.names); + } + } + public render() { return ( @@ -149,11 +166,30 @@ export class IndexPrivilegeForm extends Component { ); }; + private loadFLSOptions = (indexNames: string[], force = false) => { + if (!force && (this.isFieldListLoading || indexNames.length === 0)) return; + + this.isFieldListLoading = true; + this.setState({ + isFieldListLoading: true, + }); + + this.props.indicesAPIClient + .getFields(indexNames.join(',')) + .then((fields) => { + this.isFieldListLoading = false; + this.setState({ flsOptions: fields, isFieldListLoading: false }); + }) + .catch(() => { + this.isFieldListLoading = false; + this.setState({ flsOptions: [], isFieldListLoading: false }); + }); + }; + private getFieldLevelControls = () => { const { allowFieldLevelSecurity, allowDocumentLevelSecurity, - availableFields, indexPrivilege, isRoleReadOnly, } = this.props; @@ -210,11 +246,13 @@ export class IndexPrivilegeForm extends Component { @@ -233,11 +271,13 @@ export class IndexPrivilegeForm extends Component { @@ -362,6 +402,11 @@ export class IndexPrivilegeForm extends Component { const hasSavedFieldSecurity = this.state.exceptedFields.length > 0 || this.state.grantedFields.length > 0; + // If turning on, then request available fields + if (willToggleOn) { + this.loadFLSOptions(this.props.indexPrivilege.names); + } + if (willToggleOn && !hasConfiguredFieldSecurity && hasSavedFieldSecurity) { this.props.onChange({ ...this.props.indexPrivilege, @@ -380,13 +425,22 @@ export class IndexPrivilegeForm extends Component { ...this.props.indexPrivilege, names: newIndexPatterns, }); + // If FLS controls are visible, then forcefully request a new set of options + if (this.state.fieldSecurityExpanded) { + this.loadFLSOptions(newIndexPatterns, true); + } }; private onIndexPatternsChange = (newPatterns: EuiComboBoxOptionOption[]) => { + const names = newPatterns.map(fromOption); this.props.onChange({ ...this.props.indexPrivilege, - names: newPatterns.map(fromOption), + names, }); + // If FLS controls are visible, then forcefully request a new set of options + if (this.state.fieldSecurityExpanded) { + this.loadFLSOptions(names, true); + } }; private onPrivilegeChange = (newPrivileges: EuiComboBoxOptionOption[]) => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx index 5b7d703865b9c..c910ec5dda9a8 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx @@ -56,6 +56,9 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', async () allowRoleDocumentLevelSecurity: true, } as any); + const indicesAPIClient = indicesAPIClientMock.create(); + indicesAPIClient.getFields.mockResolvedValue(['foo']); + const props = { role: { name: '', @@ -80,7 +83,7 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', async () editable: true, validator: new RoleValidator(), availableIndexPrivileges: ['all', 'read', 'write', 'index'], - indicesAPIClient: indicesAPIClientMock.create(), + indicesAPIClient, license, }; const wrapper = mountWithIntl(); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx index 2f6bb73fc62fb..d761992c275c9 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import type { PublicMethodsOf } from '@kbn/utility-types'; @@ -41,18 +40,15 @@ export class IndexPrivileges extends Component { }; } - public componentDidMount() { - this.loadAvailableFields(this.props.role.elasticsearch.indices); - } - public render() { const { indices = [] } = this.props.role.elasticsearch; - const { indexPatterns, license, availableIndexPrivileges } = this.props; + const { indexPatterns, license, availableIndexPrivileges, indicesAPIClient } = this.props; const { allowRoleDocumentLevelSecurity, allowRoleFieldLevelSecurity } = license.getFeatures(); const props = { indexPatterns, + indicesAPIClient, // If editing an existing role while that has been disabled, always show the FLS/DLS fields because currently // a role is only marked as disabled if it has FLS/DLS setup (usually before the user changed to a license that // doesn't permit FLS/DLS). @@ -69,7 +65,6 @@ export class IndexPrivileges extends Component { validator={this.props.validator} availableIndexPrivileges={availableIndexPrivileges} indexPrivilege={indexPrivilege} - availableFields={this.state.availableFields[indexPrivilege.names.join(',')]} onChange={this.onIndexPrivilegeChange(idx)} onDelete={this.onIndexPrivilegeDelete(idx)} /> @@ -116,8 +111,6 @@ export class IndexPrivileges extends Component { indices: newIndices, }, }); - - this.loadAvailableFields(newIndices); }; }; @@ -141,43 +134,4 @@ export class IndexPrivileges extends Component { public isPlaceholderPrivilege = (indexPrivilege: RoleIndexPrivilege) => { return indexPrivilege.names.length === 0; }; - - public loadAvailableFields(privileges: RoleIndexPrivilege[]) { - // readonly roles cannot be edited, and therefore do not need to fetch available fields. - if (isRoleReadOnly(this.props.role)) { - return; - } - - const patterns = privileges.map((index) => index.names.join(',')); - - const cachedPatterns = Object.keys(this.state.availableFields); - const patternsToFetch = _.difference(patterns, cachedPatterns); - - const fetchRequests = patternsToFetch.map(this.loadFieldsForPattern); - - Promise.all(fetchRequests).then((response) => { - this.setState({ - availableFields: { - ...this.state.availableFields, - ...response.reduce((acc, o) => ({ ...acc, ...o }), {}), - }, - }); - }); - } - - public loadFieldsForPattern = async (pattern: string) => { - if (!pattern) { - return { [pattern]: [] }; - } - - try { - return { - [pattern]: await this.props.indicesAPIClient.getFields(pattern), - }; - } catch (e) { - return { - [pattern]: [], - }; - } - }; } diff --git a/x-pack/plugins/security/public/management/roles/indices_api_client.test.ts b/x-pack/plugins/security/public/management/roles/indices_api_client.test.ts new file mode 100644 index 0000000000000..feb0c73aa5bb2 --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/indices_api_client.test.ts @@ -0,0 +1,104 @@ +/* + * 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 { coreMock } from 'src/core/public/mocks'; + +import { IndicesAPIClient } from './indices_api_client'; + +describe('getFields', () => { + it('queries for available fields', async () => { + const { http } = coreMock.createSetup(); + http.get.mockResolvedValue(['foo']); + + const client = new IndicesAPIClient(http); + + const fields = await client.getFields('foo with special characters-&-*'); + + expect(http.get).toHaveBeenCalledTimes(1); + expect(http.get).toHaveBeenCalledWith( + `/internal/security/fields/${encodeURIComponent('foo with special characters-&-*')}` + ); + expect(fields).toEqual(['foo']); + }); + + it('caches results for matching patterns', async () => { + const { http } = coreMock.createSetup(); + http.get.mockResolvedValue(['foo']); + + const client = new IndicesAPIClient(http); + + const fields = await client.getFields('foo'); + + const fields2 = await client.getFields('foo'); + + expect(http.get).toHaveBeenCalledTimes(1); + expect(http.get).toHaveBeenCalledWith(`/internal/security/fields/${encodeURIComponent('foo')}`); + + expect(fields).toEqual(['foo']); + expect(fields2).toEqual(['foo']); + }); + + it('does not cache results for differing patterns', async () => { + const { http } = coreMock.createSetup(); + http.get.mockResolvedValueOnce(['foo']); + http.get.mockResolvedValueOnce(['bar']); + + const client = new IndicesAPIClient(http); + + const fields = await client.getFields('foo'); + + const fields2 = await client.getFields('bar'); + + expect(http.get).toHaveBeenCalledTimes(2); + expect(http.get).toHaveBeenNthCalledWith( + 1, + `/internal/security/fields/${encodeURIComponent('foo')}` + ); + expect(http.get).toHaveBeenNthCalledWith( + 2, + `/internal/security/fields/${encodeURIComponent('bar')}` + ); + + expect(fields).toEqual(['foo']); + expect(fields2).toEqual(['bar']); + }); + + it('does not cache empty results', async () => { + const { http } = coreMock.createSetup(); + http.get.mockResolvedValue([]); + + const client = new IndicesAPIClient(http); + + const fields = await client.getFields('foo'); + + const fields2 = await client.getFields('foo'); + + expect(http.get).toHaveBeenCalledTimes(2); + expect(http.get).toHaveBeenNthCalledWith( + 1, + `/internal/security/fields/${encodeURIComponent('foo')}` + ); + expect(http.get).toHaveBeenNthCalledWith( + 2, + `/internal/security/fields/${encodeURIComponent('foo')}` + ); + + expect(fields).toEqual([]); + expect(fields2).toEqual([]); + }); + + it('throws unexpected errors', async () => { + const { http } = coreMock.createSetup(); + http.get.mockRejectedValue(new Error('AHHHH')); + + const client = new IndicesAPIClient(http); + + await expect(() => client.getFields('foo')).rejects.toThrowErrorMatchingInlineSnapshot( + `"AHHHH"` + ); + }); +}); diff --git a/x-pack/plugins/security/public/management/roles/indices_api_client.ts b/x-pack/plugins/security/public/management/roles/indices_api_client.ts index ebec4d858d7ad..5d2ead1961fc6 100644 --- a/x-pack/plugins/security/public/management/roles/indices_api_client.ts +++ b/x-pack/plugins/security/public/management/roles/indices_api_client.ts @@ -8,12 +8,20 @@ import type { HttpStart } from 'src/core/public'; export class IndicesAPIClient { + private readonly fieldCache = new Map(); + constructor(private readonly http: HttpStart) {} - async getFields(query: string) { - return ( - (await this.http.get(`/internal/security/fields/${encodeURIComponent(query)}`)) || - [] - ); + async getFields(pattern: string): Promise { + if (pattern && !this.fieldCache.has(pattern)) { + const fields = await this.http.get( + `/internal/security/fields/${encodeURIComponent(pattern)}` + ); + if (Array.isArray(fields) && fields.length > 0) { + this.fieldCache.set(pattern, fields); + } + } + + return this.fieldCache.get(pattern) ?? []; } } diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx index 476aabb845c51..4b10c2d5cf13b 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx @@ -96,7 +96,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} + Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
`); @@ -124,7 +124,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}} + Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}}
`); @@ -149,7 +149,7 @@ describe('rolesManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}} + Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}}
`); diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.ts b/x-pack/plugins/security/server/routes/indices/get_fields.ts index 63704682e3635..d0370a93b14c3 100644 --- a/x-pack/plugins/security/server/routes/indices/get_fields.ts +++ b/x-pack/plugins/security/server/routes/indices/get_fields.ts @@ -25,6 +25,7 @@ export function defineGetFieldsRoutes({ router }: RouteDefinitionParams) { fields: '*', allow_no_indices: false, include_defaults: true, + filter_path: '*.mappings.*.mapping.*.type', }); // The flow is the following (see response format at https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html): @@ -62,7 +63,17 @@ export function defineGetFieldsRoutes({ router }: RouteDefinitionParams) { body: fields, }); } catch (error) { - return response.customError(wrapIntoCustomErrorResponse(error)); + const customResponse = wrapIntoCustomErrorResponse(error); + + // Elasticsearch returns a 404 response if the provided pattern does not match any indices. + // In this scenario, we want to instead treat this as an empty response. + if (customResponse.statusCode === 404) { + return response.ok({ + body: [], + }); + } + + return response.customError(customResponse); } } ); diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 914c20a303358..dddfafc7ddf05 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -27,6 +27,8 @@ export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults'; export const DEFAULT_APP_REFRESH_INTERVAL = 'securitySolution:refreshIntervalDefaults'; export const DEFAULT_ALERTS_INDEX = '*alerts-security-solution*'; export const DEFAULT_SIGNALS_INDEX = '.siem-signals'; +export const DEFAULT_LISTS_INDEX = '.lists'; +export const DEFAULT_ITEMS_INDEX = '.items'; // The DEFAULT_MAX_SIGNALS value exists also in `x-pack/plugins/cases/common/constants.ts` // If either changes, engineer should ensure both values are updated export const DEFAULT_MAX_SIGNALS = 100; @@ -51,6 +53,7 @@ export const DEFAULT_RULE_REFRESH_INTERVAL_ON = true; export const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000; // ms export const DEFAULT_RULE_REFRESH_IDLE_VALUE = 2700000; // ms export const DEFAULT_RULE_NOTIFICATION_QUERY_SIZE = 100; +export const SAVED_OBJECTS_MANAGEMENT_FEATURE_ID = 'Saved Objects Management'; // Document path where threat indicator fields are expected. Fields are used // to enrich signals, and are copied to threat.indicator. diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index b5f88aa144814..e085d500bee0a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -76,14 +76,25 @@ export type Filters = t.TypeOf; // Filters are not easily type-a export const filtersOrUndefined = t.union([filters, t.undefined]); export type FiltersOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const saved_object_attribute_single: t.Type = t.recursion( 'saved_object_attribute_single', () => t.union([t.string, t.number, t.boolean, t.null, t.undefined, saved_object_attributes]) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const saved_object_attribute: t.Type = t.recursion( 'saved_object_attribute', () => t.union([saved_object_attribute_single, t.array(saved_object_attribute_single)]) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const saved_object_attributes: t.Type = t.recursion( 'saved_object_attributes', () => t.record(t.string, saved_object_attribute) @@ -172,10 +183,24 @@ export type Query = t.TypeOf; export const queryOrUndefined = t.union([query, t.undefined]); export type QueryOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const language = t.keyof({ eql: null, kuery: null, lucene: null }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Language = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const languageOrUndefined = t.union([language, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type LanguageOrUndefined = t.TypeOf; export const license = t.string; @@ -246,7 +271,14 @@ export type Meta = t.TypeOf; export const metaOrUndefined = t.union([meta, t.undefined]); export type MetaOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const max_signals = PositiveIntegerGreaterThanZero; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type MaxSignals = t.TypeOf; export const maxSignalsOrUndefined = t.union([max_signals, t.undefined]); @@ -258,10 +290,21 @@ export type Name = t.TypeOf; export const nameOrUndefined = t.union([name, t.undefined]); export type NameOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const operator = t.keyof({ equals: null, }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Operator = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export enum OperatorEnum { EQUALS = 'equals', } @@ -272,8 +315,19 @@ export type RiskScore = t.TypeOf; export const riskScoreOrUndefined = t.union([risk_score, t.undefined]); export type RiskScoreOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const risk_score_mapping_field = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const risk_score_mapping_value = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const risk_score_mapping_item = t.exact( t.type({ field: risk_score_mapping_field, @@ -283,7 +337,14 @@ export const risk_score_mapping_item = t.exact( }) ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const risk_score_mapping = t.array(risk_score_mapping_item); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type RiskScoreMapping = t.TypeOf; export const riskScoreMappingOrUndefined = t.union([risk_score_mapping, t.undefined]); @@ -295,14 +356,39 @@ export type RuleNameOverride = t.TypeOf; export const ruleNameOverrideOrUndefined = t.union([rule_name_override, t.undefined]); export type RuleNameOverrideOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severity = t.keyof({ low: null, medium: null, high: null, critical: null }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Severity = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severityOrUndefined = t.union([severity, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type SeverityOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severity_mapping_field = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severity_mapping_value = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severity_mapping_item = t.exact( t.type({ field: severity_mapping_field, @@ -311,12 +397,30 @@ export const severity_mapping_item = t.exact( severity, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type SeverityMappingItem = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severity_mapping = t.array(severity_mapping_item); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type SeverityMapping = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const severityMappingOrUndefined = t.union([severity_mapping, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type SeverityMappingOrUndefined = t.TypeOf; export const status = t.keyof({ open: null, closed: null, 'in-progress': null }); @@ -407,29 +511,92 @@ export type Fields = t.TypeOf; export const fieldsOrUndefined = t.union([fields, t.undefined]); export type FieldsOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_framework = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_tactic_id = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_tactic_name = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_tactic_reference = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_tactic = t.type({ id: threat_tactic_id, name: threat_tactic_name, reference: threat_tactic_reference, }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatTactic = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_subtechnique_id = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_subtechnique_name = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_subtechnique_reference = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_subtechnique = t.type({ id: threat_subtechnique_id, name: threat_subtechnique_name, reference: threat_subtechnique_reference, }); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatSubtechnique = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_subtechniques = t.array(threat_subtechnique); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_technique_id = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_technique_name = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_technique_reference = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_technique = t.intersection([ t.exact( t.type({ @@ -444,8 +611,20 @@ export const threat_technique = t.intersection([ }) ), ]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatTechnique = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_techniques = t.array(threat_technique); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat = t.intersection([ t.exact( t.type({ @@ -459,9 +638,20 @@ export const threat = t.intersection([ }) ), ]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Threat = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threats = t.array(threat); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Threats = t.TypeOf; export const threatsOrUndefined = t.union([threats, t.undefined]); @@ -517,16 +707,38 @@ export type ThresholdNormalized = t.TypeOf; export const thresholdNormalizedOrUndefined = t.union([thresholdNormalized, t.undefined]); export type ThresholdNormalizedOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const created_at = IsoDateString; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updated_at = IsoDateString; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const updated_by = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const created_by = t.string; export const updatedByOrNull = t.union([updated_by, t.null]); export type UpdatedByOrNull = t.TypeOf; export const createdByOrNull = t.union([created_by, t.null]); export type CreatedByOrNull = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const version = PositiveIntegerGreaterThanZero; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type Version = t.TypeOf; export const versionOrUndefined = t.union([version, t.undefined]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts index 6d7c3d82d428a..49302102ddee9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts @@ -12,6 +12,7 @@ import { actions, Actions } from '../common/schemas'; /** * Types the DefaultStringArray as: * - If undefined, then a default action array will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultActionsArray = new t.Type( 'DefaultActionsArray', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts index 9bd987c6d0c83..a6397754b23d7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultArray as: * - If undefined, then a default array will be set * - If an array is sent in, then the array will be validated to ensure all elements are type C + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultArray = (codec: C) => { const arrType = t.array(codec); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts index 7d094f749e17d..46e7bde55638a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultBooleanFalse as: * - If null or undefined, then a default false will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultBooleanFalse = new t.Type( 'DefaultBooleanFalse', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts index 68654083a745f..3845150841c22 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultBooleanTrue as: * - If null or undefined, then a default true will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultBooleanTrue = new t.Type( 'DefaultBooleanTrue', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts index 4f561ee2f810e..77a06a928a2e3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultEmptyString as: * - If null or undefined, then a default of an empty string "" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultEmptyString = new t.Type( 'DefaultEmptyString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts index 53f0972baca21..a2b2fb6eaad31 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultExportFileName as: * - If null or undefined, then a default of "export.ndjson" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultExportFileName = new t.Type( 'DefaultExportFileName', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts index 23892d32127d4..a2eb2a2b9f46f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts @@ -11,6 +11,7 @@ import { from } from '../common/schemas'; /** * Types the DefaultFromString as: * - If null or undefined, then a default of the string "now-6m" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultFromString = new t.Type( 'DefaultFromString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts index d743fa773f2ec..fb3a5931199a1 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultIntervalString as: * - If null or undefined, then a default of the string "5m" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultIntervalString = new t.Type( 'DefaultIntervalString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts index 5c29e6500511a..a40b0d8eeacaa 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts @@ -12,6 +12,7 @@ import { language } from '../common/schemas'; /** * Types the DefaultLanguageString as: * - If null or undefined, then a default of the string "kuery" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultLanguageString = new t.Type( 'DefaultLanguageString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts index 1e85f3540c623..d9209cdba4d00 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts @@ -15,6 +15,7 @@ import { DEFAULT_MAX_SIGNALS } from '../../../constants'; * - Natural Number (positive integer and not a float), * - greater than 1 * - If undefined then it will use DEFAULT_MAX_SIGNALS (100) as the default + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultMaxSignalsNumber = new t.Type( 'DefaultMaxSignals', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts index 8b9f3b115ae23..2f2cdaf3b6cd2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts @@ -14,6 +14,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_ * - If a string this will convert the string to a number * - If null or undefined, then a default of 1 will be used * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultPage = new t.Type( 'DefaultPerPage', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts index e8c9ef5d7d7f9..124fdf25917fa 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts @@ -14,6 +14,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_ * - If a string this will convert the string to a number * - If null or undefined, then a default of 20 will be used * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultPerPage = new t.Type( 'DefaultPerPage', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts index 172ed78c10eab..219b9d30b52ea 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts @@ -12,6 +12,7 @@ import { risk_score_mapping, RiskScoreMapping } from '../common/schemas'; /** * Types the DefaultStringArray as: * - If null or undefined, then a default risk_score_mapping array will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultRiskScoreMappingArray = new t.Type< RiskScoreMapping, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts index 4ee86b7d3793f..fe65574383294 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts @@ -12,6 +12,7 @@ import { severity_mapping, SeverityMapping } from '../common/schemas'; /** * Types the DefaultStringArray as: * - If null or undefined, then a default severity_mapping array will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultSeverityMappingArray = new t.Type< SeverityMapping, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts index 064ec9536d11c..be4b5563ddb13 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultStringArray as: * - If undefined, then a default array will be set * - If an array is sent in, then the array will be validated to ensure all elements are a string + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultStringArray = new t.Type( 'DefaultStringArray', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts index 02b18b079e3dd..69bd4564fd3bb 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultStringBooleanFalse as: * - If a string this will convert the string to a boolean * - If null or undefined, then a default false will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultStringBooleanFalse = new t.Type( 'DefaultStringBooleanFalse', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts index 139183e8e4370..86c7ab245d6f3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts @@ -12,6 +12,7 @@ import { Threats, threats } from '../common/schemas'; /** * Types the DefaultThreatArray as: * - If null or undefined, then an empty array will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultThreatArray = new t.Type( 'DefaultThreatArray', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts index 4c44b512a3750..db6eb5eab32ae 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts @@ -12,6 +12,7 @@ import { ThrottleOrNull, throttle } from '../common/schemas'; /** * Types the DefaultThrottleNull as: * - If null or undefined, then a null will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultThrottleNull = new t.Type( 'DefaultThreatNull', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts index d2e58f423b592..05c93951bfe1e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultToString as: * - If null or undefined, then a default of the string "now" will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultToString = new t.Type( 'DefaultToString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts index 0546862748b36..ecb8cd00ff308 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts @@ -15,6 +15,7 @@ import { NonEmptyString } from './non_empty_string'; * Types the DefaultUuid as: * - If null or undefined, then a default string uuid.v4() will be * created otherwise it will be checked just against an empty string + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultUuid = new t.Type( 'DefaultUuid', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts index ce9b46a4f0967..625973694a244 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts @@ -12,6 +12,7 @@ import { version, Version } from '../common/schemas'; /** * Types the DefaultVersionNumber as: * - If null or undefined, then a default of the number 1 will be used + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultVersionNumber = new t.Type( 'DefaultVersionNumber', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/iso_date_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/iso_date_string.ts index 6b6be2b035068..9c8f490d2d59a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/iso_date_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/iso_date_string.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the IsoDateString as: * - A string that is an ISOString + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const IsoDateString = new t.Type( 'IsoDateString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts index 390257b5aa37c..79fd264808138 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts @@ -11,6 +11,9 @@ import { exceptionListType, namespaceType } from '../../../shared_imports'; import { NonEmptyString } from './non_empty_string'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const list = t.exact( t.type({ id: NonEmptyString, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts index 64cb54eff0e99..eba48c0419ec2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts @@ -13,6 +13,7 @@ import { ListArray, list } from './lists'; /** * Types the DefaultListArray as: * - If null or undefined, then a default array of type list will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const DefaultListArray = new t.Type( 'DefaultListArray', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_array.ts index 0c1a61a912062..1b5261931230d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_array.ts @@ -8,6 +8,9 @@ import * as t from 'io-ts'; import { Either } from 'fp-ts/lib/Either'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const NonEmptyArray = ( codec: C, name: string = `NonEmptyArray<${codec.name}>` diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_string.ts index 5ba85f2ab0249..e01eabd4c434e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/non_empty_string.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the NonEmptyString as: * - A string that is not empty + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const NonEmptyString = new t.Type( 'NonEmptyString', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/normalized_ml_job_id.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/normalized_ml_job_id.ts index c826bce92c8a0..7001cb8254d2b 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/normalized_ml_job_id.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/normalized_ml_job_id.ts @@ -10,13 +10,27 @@ import * as t from 'io-ts'; import { NonEmptyArray } from './non_empty_array'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const machine_learning_job_id_normalized = NonEmptyArray(t.string); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type MachineLearningJobIdNormalized = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const machineLearningJobIdNormalizedOrUndefined = t.union([ machine_learning_job_id_normalized, t.undefined, ]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type MachineLearningJobIdNormalizedOrUndefined = t.TypeOf< typeof machineLearningJobIdNormalizedOrUndefined >; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts index 5d0334bdec841..8150979cf2947 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts @@ -13,6 +13,7 @@ import { Either } from 'fp-ts/lib/Either'; * - If null or undefined, then a default false will be set * - If true is sent in then this will return an error * - If false is sent in then this will allow it only false + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const OnlyFalseAllowed = new t.Type( 'DefaultBooleanTrue', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts index 4bcdff37b9667..1771d0515fa20 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the positive integer are: * - Natural Number (positive integer and not a float), * - zero or greater + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const PositiveInteger = new t.Type( 'PositiveInteger', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts index 57be0240e8250..ddfb82b748ee3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the positive integer greater than zero is: * - Natural Number (positive integer and not a float), * - 1 or greater + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const PositiveIntegerGreaterThanZero = new t.Type( 'PositiveIntegerGreaterThanZero', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/references_default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/references_default_array.ts index e5ccac3650434..a76606911f4ae 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/references_default_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/references_default_array.ts @@ -11,6 +11,7 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the ReferencesDefaultArray as: * - If null or undefined, then a default array will be set + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const ReferencesDefaultArray = new t.Type( 'referencesWithDefaultArray', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/risk_score.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/risk_score.ts index d644798b667b3..e518bf13fd181 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/risk_score.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/risk_score.ts @@ -12,6 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * Types the risk score as: * - Natural Number (positive integer and not a float), * - Between the values [0 and 100] inclusive. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const RiskScore = new t.Type( 'RiskScore', diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts index aab06941686c2..707b51e6b8965 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts @@ -13,21 +13,69 @@ import { NonEmptyArray } from './non_empty_array'; import { NonEmptyString } from './non_empty_string'; import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_zero'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_query = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatQuery = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatQueryOrUndefined = t.union([threat_query, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatQueryOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_indicator_path = t.string; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatIndicatorPath = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatIndicatorPathOrUndefined = t.union([threat_indicator_path, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatIndicatorPathOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_filters = t.array(t.unknown); // Filters are not easily type-able yet + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatFilters = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatFiltersOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatMapEntry = t.exact( t.type({ field: NonEmptyString, @@ -36,40 +84,131 @@ export const threatMapEntry = t.exact( }) ); +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatMapEntry = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatMappingEntries = t.array(threatMapEntry); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatMappingEntries = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatMap = t.exact( t.type({ entries: threatMappingEntries, }) ); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatMap = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_mapping = NonEmptyArray(threatMap, 'NonEmptyArray'); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatMapping = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatMappingOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_index = t.array(t.string); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatIndex = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatIndexOrUndefined = t.union([threat_index, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatIndexOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threat_language = t.union([language, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatLanguage = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ThreatLanguageOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const concurrent_searches = PositiveIntegerGreaterThanZero; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ConcurrentSearches = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const concurrentSearchesOrUndefined = t.union([concurrent_searches, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ConcurrentSearchesOrUndefined = t.TypeOf; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const items_per_search = PositiveIntegerGreaterThanZero; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ItemsPerSearch = t.TypeOf; + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export const itemsPerSearchOrUndefined = t.union([items_per_search, t.undefined]); + +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils + */ export type ItemsPerSearchOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/uuid.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/uuid.ts index ca90026db0b50..79a130b26a6fd 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/uuid.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/uuid.ts @@ -14,6 +14,7 @@ const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; * Types the risk score as: * - Natural Number (positive integer and not a float), * - Between the values [0 and 100] inclusive. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils */ export const UUID = new t.Type( 'UUID', diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index b35504fc88659..fd26a2d95c9b4 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -18,7 +18,6 @@ import { CreatePackagePolicyRequest, CreatePackagePolicyResponse, GetPackagesResponse, - PostAgentEnrollRequest, AGENT_API_ROUTES, AGENT_POLICY_API_ROUTES, EPM_API_ROUTES, @@ -26,11 +25,7 @@ import { ENROLLMENT_API_KEY_ROUTES, GetEnrollmentAPIKeysResponse, GetOneEnrollmentAPIKeyResponse, - PostAgentEnrollResponse, - PostAgentCheckinRequest, - PostAgentCheckinResponse, - PostAgentAcksResponse, - PostAgentAcksRequest, + Agent, } from '../../../fleet/common'; import { policyFactory as policyConfigFactory } from './models/policy_config'; import { HostMetadata } from './types'; @@ -114,7 +109,7 @@ async function indexHostDocs({ const timestamp = new Date().getTime(); let hostMetadata: HostMetadata; let wasAgentEnrolled = false; - let enrolledAgent: undefined | PostAgentEnrollResponse['item']; + let enrolledAgent: undefined | Agent; for (let j = 0; j < numDocs; j++) { generator.updateHostData(); @@ -304,7 +299,7 @@ const fleetEnrollAgentForHost = async ( kbnClient: KbnClientWithApiKeySupport, endpointHost: HostMetadata, agentPolicyId: string -): Promise => { +): Promise => { // Get Enrollement key for host's applied policy const enrollmentApiKey = await kbnClient .request({ @@ -360,7 +355,7 @@ const fleetEnrollAgentForHost = async ( }; // Enroll an agent for the Host - const body: PostAgentEnrollRequest['body'] = { + const body = { type: 'PERMANENT', metadata: { local: { @@ -401,7 +396,7 @@ const fleetEnrollAgentForHost = async ( }); if (res) { - const enrollObj: PostAgentEnrollResponse = await res.json(); + const enrollObj = await res.json(); if (!res.ok) { // eslint-disable-next-line no-console console.error('unable to enroll agent', enrollObj); @@ -409,7 +404,7 @@ const fleetEnrollAgentForHost = async ( } // ------------------------------------------------ // now check the agent in so that it can complete enrollment - const checkinBody: PostAgentCheckinRequest['body'] = { + const checkinBody = { events: [ { type: 'STATE', @@ -447,7 +442,7 @@ const fleetEnrollAgentForHost = async ( return; } - const checkinObj: PostAgentCheckinResponse = await checkinRes.json(); + const checkinObj = await checkinRes.json(); if (!checkinRes.ok) { // eslint-disable-next-line no-console console.error( @@ -459,9 +454,9 @@ const fleetEnrollAgentForHost = async ( // ------------------------------------------------ // If we have an action to ack(), then do it now if (checkinObj.actions.length) { - const ackActionBody: PostAgentAcksRequest['body'] = { + const ackActionBody = { // @ts-ignore - events: checkinObj.actions.map((action) => { + events: checkinObj.actions.map((action) => { return { action_id: action.id, type: 'ACTION_RESULT', @@ -486,7 +481,7 @@ const fleetEnrollAgentForHost = async ( } ); - const ackActionObj: PostAgentAcksResponse = await ackActionResp.json(); + const ackActionObj = await ackActionResp.json(); if (!ackActionResp.ok) { // eslint-disable-next-line no-console console.error( diff --git a/x-pack/plugins/security_solution/common/exact_check.ts b/x-pack/plugins/security_solution/common/exact_check.ts index b10328a4db233..5334989ea085b 100644 --- a/x-pack/plugins/security_solution/common/exact_check.ts +++ b/x-pack/plugins/security_solution/common/exact_check.ts @@ -25,6 +25,7 @@ import { isObject, get } from 'lodash/fp'; * @param original The original to check if it has additional keys * @param decoded The decoded either which has either an existing error or the * decoded object which could have additional keys stripped from it. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts */ export const exactCheck = ( original: unknown, diff --git a/x-pack/plugins/security_solution/common/format_errors.ts b/x-pack/plugins/security_solution/common/format_errors.ts index 7b33612b4e887..16925699b0fcf 100644 --- a/x-pack/plugins/security_solution/common/format_errors.ts +++ b/x-pack/plugins/security_solution/common/format_errors.ts @@ -8,6 +8,9 @@ import * as t from 'io-ts'; import { isObject } from 'lodash/fp'; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts + */ export const formatErrors = (errors: t.Errors): string[] => { const err = errors.map((error) => { if (error.message != null) { diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts index ada22437a1530..d747758640fab 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts @@ -35,7 +35,7 @@ export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse { } export interface TimelineEventsAllRequestOptions extends TimelineRequestOptionsPaginated { - fields: string[]; + fields: string[] | Array<{ field: string; include_unmapped: boolean }>; fieldRequested: string[]; language: 'eql' | 'kuery' | 'lucene'; } diff --git a/x-pack/plugins/security_solution/common/test_utils.ts b/x-pack/plugins/security_solution/common/test_utils.ts index 5f5df98c08b4a..df9e9e12fc1d9 100644 --- a/x-pack/plugins/security_solution/common/test_utils.ts +++ b/x-pack/plugins/security_solution/common/test_utils.ts @@ -15,10 +15,16 @@ interface Message { schema: T | {}; } +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onLeft = (errors: t.Errors): Message => { return { schema: {}, errors }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ const onRight = (schema: T): Message => { return { schema, @@ -26,11 +32,15 @@ const onRight = (schema: T): Message => { }; }; +/** + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts + */ export const foldLeftRight = fold(onLeft, onRight); /** * Convenience utility to keep the error message handling within tests to be * very concise. + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts * @param validation The validation to get the errors from */ export const getPaths =
(validation: t.Validation): string[] => { @@ -45,6 +55,7 @@ export const getPaths = (validation: t.Validation): string[] => { /** * Convenience utility to remove text appended to links by EUI + * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts */ export const removeExternalLinkText = (str: string) => str.replace(/\(opens in a new tab or window\)/g, ''); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts new file mode 100644 index 0000000000000..5af5a8adf95b7 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -0,0 +1,68 @@ +/* + * 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 { CELL_TEXT, JSON_LINES, TABLE_ROWS } from '../../screens/alerts_details'; + +import { + expandFirstAlert, + waitForAlertsIndexToBeCreated, + waitForAlertsPanelToBeLoaded, +} from '../../tasks/alerts'; +import { openJsonView, openTable, scrollJsonViewToBottom } from '../../tasks/alerts_details'; +import { createCustomRuleActivated } from '../../tasks/api_calls/rules'; +import { cleanKibana } from '../../tasks/common'; +import { esArchiverLoad } from '../../tasks/es_archiver'; +import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; + +import { unmappedRule } from '../../objects/rule'; + +import { DETECTIONS_URL } from '../../urls/navigation'; + +describe('Alert details with unmapped fields', () => { + before(() => { + cleanKibana(); + esArchiverLoad('unmapped_fields'); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(unmappedRule); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + waitForAlertsPanelToBeLoaded(); + expandFirstAlert(); + }); + + it('Displays the unmapped field on the JSON view', () => { + const expectedUnmappedField = { line: 2, text: ' "unmapped": "This is the unmapped field"' }; + + openJsonView(); + scrollJsonViewToBottom(); + + cy.get(JSON_LINES).then((elements) => { + const length = elements.length; + cy.wrap(elements) + .eq(length - expectedUnmappedField.line) + .should('have.text', expectedUnmappedField.text); + }); + }); + + it('Displays the unmapped field on the table', () => { + const expectedUnmmappedField = { + row: 55, + field: 'unmapped', + text: 'This is the unmapped field', + }; + + openTable(); + + cy.get(TABLE_ROWS) + .eq(expectedUnmmappedField.row) + .within(() => { + cy.get(CELL_TEXT).eq(0).should('have.text', expectedUnmmappedField.field); + cy.get(CELL_TEXT).eq(1).should('have.text', expectedUnmmappedField.text); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_readonly.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts similarity index 72% rename from x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_readonly.spec.ts rename to x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts index d807857cd72bd..87a3dc8474876 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_detection_callouts_readonly.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts @@ -41,8 +41,7 @@ const waitForPageTitleToBeShown = () => { }; describe('Detections > Callouts', () => { - const ALERTS_CALLOUT = 'read-only-access-to-alerts'; - const RULES_CALLOUT = 'read-only-access-to-rules'; + const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges'; before(() => { // First, we have to open the app on behalf of a privileged user in order to initialize it. @@ -62,15 +61,15 @@ describe('Detections > Callouts', () => { }); it('We show one primary callout', () => { - waitForCallOutToBeShown(ALERTS_CALLOUT, 'primary'); + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); }); context('When a user clicks Dismiss on the callout', () => { it('We hide it and persist the dismissal', () => { - waitForCallOutToBeShown(ALERTS_CALLOUT, 'primary'); - dismissCallOut(ALERTS_CALLOUT); + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + dismissCallOut(MISSING_PRIVILEGES_CALLOUT); reloadPage(); - getCallOut(ALERTS_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); }); @@ -81,15 +80,15 @@ describe('Detections > Callouts', () => { }); it('We show one primary callout', () => { - waitForCallOutToBeShown(RULES_CALLOUT, 'primary'); + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); }); context('When a user clicks Dismiss on the callout', () => { it('We hide it and persist the dismissal', () => { - waitForCallOutToBeShown(RULES_CALLOUT, 'primary'); - dismissCallOut(RULES_CALLOUT); + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + dismissCallOut(MISSING_PRIVILEGES_CALLOUT); reloadPage(); - getCallOut(RULES_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); }); @@ -106,27 +105,18 @@ describe('Detections > Callouts', () => { deleteCustomRule(); }); - it('We show two primary callouts', () => { - waitForCallOutToBeShown(ALERTS_CALLOUT, 'primary'); - waitForCallOutToBeShown(RULES_CALLOUT, 'primary'); + it('We show one primary callout', () => { + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); }); context('When a user clicks Dismiss on the callouts', () => { it('We hide them and persist the dismissal', () => { - waitForCallOutToBeShown(ALERTS_CALLOUT, 'primary'); - waitForCallOutToBeShown(RULES_CALLOUT, 'primary'); - - dismissCallOut(ALERTS_CALLOUT); - reloadPage(); - - getCallOut(ALERTS_CALLOUT).should('not.exist'); - getCallOut(RULES_CALLOUT).should('be.visible'); + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); - dismissCallOut(RULES_CALLOUT); + dismissCallOut(MISSING_PRIVILEGES_CALLOUT); reloadPage(); - getCallOut(ALERTS_CALLOUT).should('not.exist'); - getCallOut(RULES_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); }); @@ -139,8 +129,7 @@ describe('Detections > Callouts', () => { }); it('We show no callout', () => { - getCallOut(ALERTS_CALLOUT).should('not.exist'); - getCallOut(RULES_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); @@ -150,8 +139,7 @@ describe('Detections > Callouts', () => { }); it('We show no callout', () => { - getCallOut(ALERTS_CALLOUT).should('not.exist'); - getCallOut(RULES_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); @@ -168,8 +156,7 @@ describe('Detections > Callouts', () => { }); it('We show no callouts', () => { - getCallOut(ALERTS_CALLOUT).should('not.exist'); - getCallOut(RULES_CALLOUT).should('not.exist'); + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts index 72f82aa54b7d5..cb3785c1aba25 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/notes_tab.spec.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { timeline } from '../../objects/timeline'; +import { timelineNonValidQuery } from '../../objects/timeline'; import { NOTES_TEXT, NOTES_TEXT_AREA } from '../../screens/timeline'; import { createTimeline } from '../../tasks/api_calls/timelines'; @@ -19,19 +19,26 @@ import { waitForTimelinesPanelToBeLoaded } from '../../tasks/timelines'; import { TIMELINES_URL } from '../../urls/navigation'; describe('Timeline notes tab', () => { - let timelineId: string | undefined; + let timelineId: string; before(() => { cleanKibana(); loginAndWaitForPageWithoutDateRange(TIMELINES_URL); waitForTimelinesPanelToBeLoaded(); - createTimeline(timeline).then((response) => { - timelineId = response.body.data.persistTimeline.timeline.savedObjectId; - waitForTimelinesPanelToBeLoaded(); - openTimelineById(timelineId!); - addNotesToTimeline(timeline.notes); - }); + createTimeline(timelineNonValidQuery) + .then((response) => { + if (response.body.data.persistTimeline.timeline.savedObjectId == null) { + cy.log('"createTimeline" did not return expected response'); + } + timelineId = response.body.data.persistTimeline.timeline.savedObjectId; + waitForTimelinesPanelToBeLoaded(); + }) + .then(() => { + openTimelineById(timelineId).then(() => { + addNotesToTimeline(timelineNonValidQuery.notes); + }); + }); }); after(() => { @@ -39,7 +46,7 @@ describe('Timeline notes tab', () => { }); it('should contain notes', () => { - cy.get(NOTES_TEXT).should('have.text', timeline.notes); + cy.get(NOTES_TEXT).should('have.text', timelineNonValidQuery.notes); }); it('should render mockdown', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts index 93c8d7265478e..7bf0409d91039 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/open_timeline.spec.ts @@ -48,9 +48,10 @@ describe('Open timeline', () => { addNoteToTimeline(note, timelineId!).should((response) => { expect(response.status).to.equal(200); waitForTimelinesPanelToBeLoaded(); - openTimelineById(timelineId!); - pinFirstEvent(); - markAsFavorite(); + openTimelineById(timelineId!).then(() => { + pinFirstEvent(); + markAsFavorite(); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 957046cae003a..12523e39cb597 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -170,7 +170,25 @@ export const newRule: CustomRule = { severity: 'High', riskScore: '17', tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], + falsePositivesExamples: ['False1', 'False2'], + mitre: [mitre1, mitre2], + note: '# test markdown', + runsEvery, + lookBack, + timeline, + maxSignals: 100, +}; + +export const unmappedRule: CustomRule = { + customQuery: '*:*', + index: ['unmapped*'], + name: 'Rule with unmapped fields', + description: 'The new rule description.', + severity: 'High', + riskScore: '17', + tags: ['test', 'newRule'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', @@ -209,7 +227,7 @@ export const newOverrideRule: OverrideRule = { severity: 'High', riskScore: '17', tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', @@ -231,7 +249,7 @@ export const newThresholdRule: ThresholdRule = { severity: 'High', riskScore: '17', tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', @@ -267,7 +285,7 @@ export const eqlRule: CustomRule = { severity: 'High', riskScore: '17', tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', @@ -288,7 +306,7 @@ export const eqlSequenceRule: CustomRule = { severity: 'High', riskScore: '17', tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', @@ -305,7 +323,7 @@ export const newThreatIndicatorRule: ThreatIndicatorRule = { severity: 'Critical', riskScore: '20', tags: ['test', 'threat'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + referenceUrls: ['http://example.com/', 'https://example.com/'], falsePositivesExamples: ['False1', 'False2'], mitre: [mitre1, mitre2], note: '# test markdown', diff --git a/x-pack/plugins/security_solution/cypress/objects/timeline.ts b/x-pack/plugins/security_solution/cypress/objects/timeline.ts index a3d653840dfd9..b5089c9775666 100644 --- a/x-pack/plugins/security_solution/cypress/objects/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/objects/timeline.ts @@ -37,6 +37,16 @@ export const timeline: CompleteTimeline = { filter, }; +/** + * Timeline query that finds no valid data to cut down on test failures + * or other issues for when we want to test one specific thing and not also + * test the queries happening + */ +export const timelineNonValidQuery: CompleteTimeline = { + ...timeline, + query: 'query_to_intentionally_find_nothing: *', +}; + export const caseTimeline: Timeline = { title: 'SIEM test', description: 'description', diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts index 417cf73de47f6..c18949224d4c0 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts @@ -5,8 +5,14 @@ * 2.0. */ +export const CELL_TEXT = '.euiText'; + export const JSON_CONTENT = '[data-test-subj="jsonView"]'; export const JSON_LINES = '.ace_line'; export const JSON_VIEW_TAB = '[data-test-subj="jsonViewTab"]'; + +export const TABLE_TAB = '[data-test-subj="tableTab"]'; + +export const TABLE_ROWS = '.euiTableRow'; diff --git a/x-pack/plugins/security_solution/cypress/screens/common/callouts.ts b/x-pack/plugins/security_solution/cypress/screens/common/callouts.ts index d70ef97789cdf..032d244f76977 100644 --- a/x-pack/plugins/security_solution/cypress/screens/common/callouts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/common/callouts.ts @@ -7,6 +7,6 @@ export const CALLOUT = '[data-test-subj^="callout-"]'; -export const callOutWithId = (id: string) => `[data-test-subj="callout-${id}"]`; +export const callOutWithId = (id: string) => `[data-test-subj^="callout-${id}"]`; export const CALLOUT_DISMISS_BTN = '[data-test-subj^="callout-dismiss-"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_details.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_details.ts index 1582f35989e2c..16c30afe117ef 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_details.ts @@ -5,13 +5,18 @@ * 2.0. */ -import { JSON_CONTENT, JSON_VIEW_TAB } from '../screens/alerts_details'; +import { JSON_CONTENT, JSON_VIEW_TAB, TABLE_TAB } from '../screens/alerts_details'; export const openJsonView = () => { cy.get(JSON_VIEW_TAB).click(); }; +export const openTable = () => { + cy.get(TABLE_TAB).click(); +}; + export const scrollJsonViewToBottom = () => { cy.get(JSON_CONTENT).click({ force: true }); cy.get(JSON_CONTENT).type('{pagedown}{pagedown}{pagedown}'); + cy.get(JSON_CONTENT).should('be.visible'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 5a816a71744cb..617a06cc8e79a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -85,7 +85,7 @@ export const createCustomRuleActivated = ( severity: rule.severity.toLocaleLowerCase(), type: 'query', from: 'now-17520h', - index: ['auditbeat-*'], + index: rule.index, query: rule.customQuery, language: 'kuery', enabled: true, diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 1474098b76281..e81cfa50a1a70 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -89,13 +89,14 @@ export const addNameAndDescriptionToTimeline = (timeline: Timeline) => { cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); }; -export const goToNotesTab = () => { +export const goToNotesTab = (): Cypress.Chainable> => { cy.root() .pipe(($el) => { $el.find(NOTES_TAB_BUTTON).trigger('click'); return $el.find(NOTES_TEXT_AREA); }) .should('be.visible'); + return cy.root().find(NOTES_TAB_BUTTON); }; export const getNotePreviewByNoteId = (noteId: string) => { @@ -112,15 +113,16 @@ export const goToQueryTab = () => { }; export const addNotesToTimeline = (notes: string) => { - cy.wait(150); - goToNotesTab(); - cy.get(NOTES_TEXT_AREA).type(notes); - cy.root() - .pipe(($el) => { - $el.find(ADD_NOTE_BUTTON).trigger('click'); - return $el.find(NOTES_TAB_BUTTON).find('.euiBadge'); - }) - .should('have.text', '1'); + goToNotesTab().then(() => { + cy.get(NOTES_TEXT_AREA).type(notes); + cy.root() + .pipe(($el) => { + $el.find(ADD_NOTE_BUTTON).trigger('click'); + return $el.find(NOTES_TAB_BUTTON).find('.euiBadge'); + }) + .should('have.text', '1'); + }); + goToQueryTab(); goToNotesTab(); }; @@ -136,7 +138,7 @@ export const addFilter = (filter: TimelineFilter) => { cy.get(SAVE_FILTER_BTN).click(); }; -export const addDataProvider = (filter: TimelineFilter) => { +export const addDataProvider = (filter: TimelineFilter): Cypress.Chainable> => { cy.get(TIMELINE_ADD_FIELD_BUTTON).click(); cy.get(TIMELINE_DATA_PROVIDER_FIELD).type(`${filter.field}{downarrow}{enter}`); cy.get(TIMELINE_DATA_PROVIDER_OPERATOR).type(filter.operator); @@ -231,13 +233,15 @@ export const openTimelineTemplateFromSettings = (id: string) => { cy.get(TIMELINE_TITLE_BY_ID(id)).click({ force: true }); }; -export const openTimelineById = (timelineId: string) => { - cy.root() - .pipe(($el) => { - $el.find(TIMELINE_TITLE_BY_ID(timelineId)).trigger('click'); - return $el.find(QUERY_TAB_BUTTON).find('.euiBadge'); - }) - .should('be.visible'); +export const openTimelineById = (timelineId: string): Cypress.Chainable> => { + if (timelineId == null) { + // Log out if for some reason this happens to be null just in case for our tests we experience + // value of null. Some tests return an "any" which is why this could happen. + cy.log('"timelineId" is null or undefined'); + } + // We avoid use cypress.pipe() here and multiple clicks because each of these clicks + // can result in a new URL async operation occurring and then we get indeterminism as the URL loads multiple times. + return cy.get(TIMELINE_TITLE_BY_ID(timelineId)).should('be.visible').click({ force: true }); }; export const pinFirstEvent = () => { diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 0917354894834..cfb25c4436db3 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -25,6 +25,7 @@ import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { StartServices } from '../types'; import { PageRouter } from './routes'; import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; +import { UserPrivilegesProvider } from '../detections/components/user_privileges'; interface StartAppComponent { children: React.ReactNode; @@ -45,11 +46,13 @@ const StartAppComponent: FC = ({ children, history, onAppLeav - - - {children} - - + + + + {children} + + + diff --git a/x-pack/plugins/security_solution/public/cases/components/callout/translations.ts b/x-pack/plugins/security_solution/public/cases/components/callout/translations.ts index 96c4d475c4308..4a5f32684ccde 100644 --- a/x-pack/plugins/security_solution/public/cases/components/callout/translations.ts +++ b/x-pack/plugins/security_solution/public/cases/components/callout/translations.ts @@ -18,7 +18,7 @@ export const READ_ONLY_SAVED_OBJECT_MSG = i18n.translate( 'xpack.securitySolution.cases.readOnlySavedObjectDescription', { defaultMessage: - 'You only have permissions to view cases. If you need to open and update cases, contact your Kibana administrator.', + 'You only have privileges to view cases. If you need to open and update cases, contact your Kibana administrator.', } ); diff --git a/x-pack/plugins/security_solution/public/cases/pages/case.tsx b/x-pack/plugins/security_solution/public/cases/pages/case.tsx index 817295d6b2e32..ff7589e9deb2a 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/case.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/case.tsx @@ -25,7 +25,7 @@ export const CasesPage = React.memo(() => { {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( )} diff --git a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx index edb84db89b878..1841ca39ae853 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx @@ -38,7 +38,7 @@ export const CaseDetailsPage = React.memo(() => { {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( )} { const { getMessages, addMessage } = useMessagesStorage(); - const visibilityStateInitial = useMemo(() => createInitialVisibilityState(messages), [messages]); - const [visibilityState, setVisibilityState] = useMap(visibilityStateInitial); + const [visibilityState, setVisibilityState] = useMap>({}); const dismissedMessagesKey = getDismissedMessagesStorageKey(namespace); @@ -58,16 +56,27 @@ export const useCallOutStorage = ( [setVisibilityState, addMessage, dismissedMessagesKey] ); - useEffectOnce(() => { - const idsAll = Object.keys(visibilityState); - const idsDismissed = getMessages(dismissedMessagesKey); - const idsToMakeVisible = difference(idsAll)(idsDismissed); + const populateVisibilityState = useCallback( + (ids: string[]) => { + const idsDismissed = getMessages(dismissedMessagesKey); + const idsToShow = difference(ids, idsDismissed); + const idsToHide = intersection(ids, idsDismissed); - setVisibilityState.setAll({ - ...createVisibilityState(idsToMakeVisible, true), - ...createVisibilityState(idsDismissed, false), - }); - }); + setVisibilityState.setAll({ + ...createVisibilityState(idsToShow, true), + ...createVisibilityState(idsToHide, false), + }); + }, + [getMessages, dismissedMessagesKey, setVisibilityState] + ); + + useEffect(() => { + const idsFromProps = messages.map((m) => m.id); + const idsFromState = Object.keys(visibilityState); + if (!isEqual(idsFromProps, idsFromState)) { + populateVisibilityState(idsFromProps); + } + }, [messages, visibilityState, populateVisibilityState]); return { getVisibleMessageIds, @@ -79,12 +88,6 @@ export const useCallOutStorage = ( const getDismissedMessagesStorageKey = (namespace: string) => `kibana.securitySolution.${namespace}.callouts.dismissed`; -const createInitialVisibilityState = (messages: CallOutMessage[]) => - createVisibilityState( - messages.map((m) => m.id), - false - ); - const createVisibilityState = (messageIds: string[], isVisible: boolean) => mapToObject(messageIds, identity, () => isVisible); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx index 7da514a551037..198277e1fb941 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx @@ -55,14 +55,14 @@ describe('getColumns', () => { checkboxColumn = getColumns(defaultProps)[0] as Column; }); - test('should be disabled when the field does not exist', () => { + test('should be enabled when the field does not exist', () => { const testField = 'nonExistingField'; const wrapper = mount( {checkboxColumn.render(testField, testData)} ) as ReactWrapper; expect( wrapper.find(`[data-test-subj="toggle-field-${testField}"]`).first().prop('disabled') - ).toBe(true); + ).toBe(false); }); test('should be enabled when the field does exist', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index 3a891222c11a5..204b8c088304b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -91,10 +91,6 @@ export const getColumns = ({ width: '30px', render: (field: string, data: EventFieldsData) => { const label = data.isObjectArray ? i18n.NESTED_COLUMN(field) : i18n.VIEW_COLUMN(field); - const fieldFromBrowserField = getFieldFromBrowserField( - [data.category, 'fields', field], - browserFields - ); return ( ); @@ -192,40 +186,56 @@ export const getColumns = ({ name: i18n.VALUE, sortable: true, truncateText: false, - render: (values: string[] | null | undefined, data: EventFieldsData) => ( - - {values != null && - values.map((value, i) => ( - -
- {data.field === MESSAGE_FIELD_NAME ? ( - - ) : ( - - )} -
-
- ))} -
- ), + render: (values: string[] | null | undefined, data: EventFieldsData) => { + const fieldFromBrowserField = getFieldFromBrowserField( + [data.category, 'fields', data.field], + browserFields + ); + return ( + + {values != null && + values.map((value, i) => { + if (fieldFromBrowserField == null) { + return {value}; + } + return ( + +
+ {data.field === MESSAGE_FIELD_NAME ? ( + + ) : ( + + )} +
+
+ ); + })} +
+ ); + }, }, { field: 'valuesConcatenated', diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 91ebec72d3845..4e924402783c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -146,6 +146,7 @@ const EventDetailsComponent: React.FC = ({ const tableTab = useMemo( () => ({ id: EventsViewType.tableView, + 'data-test-subj': 'tableTab', name: i18n.TABLE, content: ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts index 722a551a587a4..f3b697d12af60 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/translations.ts @@ -46,6 +46,18 @@ export const CREATED_BY = i18n.translate('xpack.securitySolution.exceptions.crea defaultMessage: 'Created by', }); +export const NAME = i18n.translate('xpack.securitySolution.exceptions.nameLabel', { + defaultMessage: 'Name', +}); + +export const DATE_MODIFIED = i18n.translate('xpack.securitySolution.exceptions.dateModifiedLabel', { + defaultMessage: 'Date modified', +}); + +export const MODIFIED_BY = i18n.translate('xpack.securitySolution.exceptions.modifiedByLabel', { + defaultMessage: 'Modified by', +}); + export const COMMENT = i18n.translate('xpack.securitySolution.exceptions.commentLabel', { defaultMessage: 'Comment', }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx index b6cf55585c0d3..e3e9ba1bfa132 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx @@ -221,4 +221,32 @@ describe('ExceptionDetails', () => { expect(wrapper.find('EuiDescriptionListTitle').at(3).text()).toEqual('Description'); expect(wrapper.find('EuiDescriptionListDescription').at(3).text()).toEqual('some description'); }); + + test('it renders with Name and Modified info when showName and showModified props are true', () => { + const exceptionItem = getExceptionListItemSchemaMock(); + exceptionItem.comments = []; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('EuiDescriptionListTitle').at(0).text()).toEqual('Name'); + expect(wrapper.find('EuiDescriptionListDescription').at(0).text()).toEqual('some name'); + + expect(wrapper.find('EuiDescriptionListTitle').at(4).text()).toEqual('Date modified'); + expect(wrapper.find('EuiDescriptionListDescription').at(4).text()).toEqual( + 'April 20th 2020 @ 15:25:31' + ); + + expect(wrapper.find('EuiDescriptionListTitle').at(5).text()).toEqual('Modified by'); + expect(wrapper.find('EuiDescriptionListDescription').at(5).text()).toEqual('some user'); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx index 4e9fcded04013..86eca688ef082 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.tsx @@ -38,16 +38,20 @@ const MyDescriptionListDescription = styled(EuiDescriptionListDescription)` const ExceptionDetailsComponent = ({ showComments, + showModified = false, + showName = false, onCommentsClick, exceptionItem, }: { showComments: boolean; + showModified?: boolean; + showName?: boolean; exceptionItem: ExceptionListItemSchema; onCommentsClick: () => void; }): JSX.Element => { const descriptionListItems = useMemo( - (): DescriptionListItem[] => getDescriptionListContent(exceptionItem), - [exceptionItem] + (): DescriptionListItem[] => getDescriptionListContent(exceptionItem, showModified, showName), + [exceptionItem, showModified, showName] ); const commentsSection = useMemo((): JSX.Element => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx index 8b915379f48bb..7c55c0de68c64 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.test.tsx @@ -45,6 +45,31 @@ describe('ExceptionItem', () => { expect(wrapper.find('ExceptionEntries')).toHaveLength(1); }); + it('it renders ExceptionDetails with Name and Modified info when showName and showModified are true ', () => { + const exceptionItem = getExceptionListItemSchemaMock(); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('ExceptionDetails').props()).toEqual( + expect.objectContaining({ + showModified: true, + showName: true, + }) + ); + }); + it('it invokes "onEditException" when edit button clicked', () => { const mockOnEditException = jest.fn(); const exceptionItem = getExceptionListItemSchemaMock(); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx index bfc607edd5873..4b6ae29de3bbf 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.tsx @@ -30,12 +30,14 @@ const MyFlexItem = styled(EuiFlexItem)` } `; -interface ExceptionItemProps { +export interface ExceptionItemProps { loadingItemIds: ExceptionListItemIdentifiers[]; exceptionItem: ExceptionListItemSchema; commentsAccordionId: string; onDeleteException: (arg: ExceptionListItemIdentifiers) => void; onEditException: (item: ExceptionListItemSchema) => void; + showName?: boolean; + showModified?: boolean; } const ExceptionItemComponent = ({ @@ -44,6 +46,8 @@ const ExceptionItemComponent = ({ commentsAccordionId, onDeleteException, onEditException, + showModified = false, + showName = false, }: ExceptionItemProps): JSX.Element => { const [entryItems, setEntryItems] = useState([]); const [showComments, setShowComments] = useState(false); @@ -86,6 +90,8 @@ const ExceptionItemComponent = ({ showComments={showComments} exceptionItem={exceptionItem} onCommentsClick={onCommentsClick} + showModified={showModified} + showName={showName} /> { expect(result).toEqual(expected); }); + + test('it returns Modified By/On info. when `includeModified` is true', () => { + const result = getDescriptionListContent(getExceptionListItemSchemaMock(), true); + expect(result).toEqual([ + { + description: 'Linux', + title: 'OS', + }, + { + description: 'April 20th 2020 @ 15:25:31', + title: 'Date created', + }, + { + description: 'some user', + title: 'Created by', + }, + { + description: 'April 20th 2020 @ 15:25:31', + title: 'Date modified', + }, + { + description: 'some user', + title: 'Modified by', + }, + { + description: 'some description', + title: 'Description', + }, + ]); + }); + + test('it returns Name when `includeName` is true', () => { + const result = getDescriptionListContent(getExceptionListItemSchemaMock(), false, true); + expect(result).toEqual([ + { + description: 'some name', + title: 'Name', + }, + { + description: 'Linux', + title: 'OS', + }, + { + description: 'April 20th 2020 @ 15:25:31', + title: 'Date created', + }, + { + description: 'some user', + title: 'Created by', + }, + { + description: 'some description', + title: 'Description', + }, + ]); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx index fb8756871ea36..29764625075d6 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx @@ -70,11 +70,23 @@ export const getFormattedEntries = (entries: BuilderEntry[]): FormattedEntry[] = * Formats ExceptionItem details for description list component * * @param exceptionItem an ExceptionItem + * @param includeModified if modified information should be included + * @param includeName if the Name should be included */ export const getDescriptionListContent = ( - exceptionItem: ExceptionListItemSchema + exceptionItem: ExceptionListItemSchema, + includeModified: boolean = false, + includeName: boolean = false ): DescriptionListItem[] => { const details = [ + ...(includeName + ? [ + { + title: i18n.NAME, + value: exceptionItem.name, + }, + ] + : []), { title: i18n.OPERATING_SYSTEM, value: formatOperatingSystems(exceptionItem.os_types), @@ -87,6 +99,18 @@ export const getDescriptionListContent = ( title: i18n.CREATED_BY, value: exceptionItem.created_by, }, + ...(includeModified + ? [ + { + title: i18n.DATE_MODIFIED, + value: moment(exceptionItem.updated_at).format('MMMM Do YYYY @ HH:mm:ss'), + }, + { + title: i18n.MODIFIED_BY, + value: exceptionItem.updated_by, + }, + ] + : []), { title: i18n.DESCRIPTION, value: exceptionItem.description, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index 571cef8f6c484..605478900d066 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -15,7 +15,7 @@ import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../../network/p import { getBreadcrumbs as getCaseDetailsBreadcrumbs } from '../../../../cases/pages/utils'; import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../../detections/pages/detection_engine/rules/utils'; import { getBreadcrumbs as getTimelinesBreadcrumbs } from '../../../../timelines/pages'; -import { getBreadcrumbs as getAdminBreadcrumbs } from '../../../../management/pages'; +import { getBreadcrumbs as getAdminBreadcrumbs } from '../../../../management/common/breadcrumbs'; import { SecurityPageName } from '../../../../app/types'; import { RouteSpyState, diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/comma_separated_values.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/comma_separated_values.tsx new file mode 100644 index 0000000000000..40217924b858e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/comma_separated_values.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCode } from '@elastic/eui'; +import React from 'react'; + +interface CommaSeparatedValuesProps { + values: React.ReactNode[]; +} + +export const CommaSeparatedValues = ({ values }: CommaSeparatedValuesProps) => ( + <> + {values.map((value, i) => ( + + {value} + {i < values.length - 1 ? ', ' : ''} + + ))} + +); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/index.tsx new file mode 100644 index 0000000000000..05c655753acb9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/index.tsx @@ -0,0 +1,48 @@ +/* + * 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 React, { memo, useMemo } from 'react'; +import hash from 'object-hash'; +import { CallOutMessage, CallOutSwitcher } from '../../../../common/components/callouts'; +import * as i18n from './translations'; +import { useMissingPrivileges } from './use_missing_privileges'; + +const MissingPrivilegesCallOutComponent = () => { + const missingPrivileges = useMissingPrivileges(); + + const MissingPrivilegesMessage: CallOutMessage | null = useMemo(() => { + const hasMissingPrivileges = + missingPrivileges.indexPrivileges.length > 0 || + missingPrivileges.featurePrivileges.length > 0; + + if (!hasMissingPrivileges) { + return null; + } + + const missingPrivilegesHash = hash(missingPrivileges); + return { + type: 'primary', + /** + * Use privileges hash as a part of the message id. + * We want to make sure that the user will see the + * callout message in case his privileges change. + * The previous click on Dismiss should not affect that. + */ + id: `missing-user-privileges-${missingPrivilegesHash}`, + title: i18n.MISSING_PRIVILEGES_CALLOUT_TITLE, + description: i18n.missingPrivilegesCallOutBody(missingPrivileges), + }; + }, [missingPrivileges]); + + return ( + MissingPrivilegesMessage && ( + + ) + ); +}; + +export const MissingPrivilegesCallOut = memo(MissingPrivilegesCallOutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx new file mode 100644 index 0000000000000..bb912dcef57cc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx @@ -0,0 +1,147 @@ +/* + * 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 { EuiCode } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { + DetectionsRequirementsLink, + SecuritySolutionRequirementsLink, +} from '../../../../common/components/links_to_docs'; +import { + DEFAULT_ITEMS_INDEX, + DEFAULT_LISTS_INDEX, + DEFAULT_SIGNALS_INDEX, + SAVED_OBJECTS_MANAGEMENT_FEATURE_ID, +} from '../../../../../common/constants'; +import { CommaSeparatedValues } from './comma_separated_values'; +import { MissingPrivileges } from './use_missing_privileges'; + +export const MISSING_PRIVILEGES_CALLOUT_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.messageTitle', + { + defaultMessage: 'Insufficient privileges', + } +); + +const CANNOT_EDIT_RULES = i18n.translate( + 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.cannotEditRules', + { + defaultMessage: 'Without that privilege you cannot create or edit detection engine rules.', + } +); + +const CANNOT_EDIT_LISTS = i18n.translate( + 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.cannotEditLists', + { + defaultMessage: 'Without these privileges, you cannot create or edit value lists.', + } +); + +const CANNOT_EDIT_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.cannotEditAlerts', + { + defaultMessage: 'Without these privileges, you cannot open or close alerts.', + } +); + +export const missingPrivilegesCallOutBody = ({ + indexPrivileges, + featurePrivileges, +}: MissingPrivileges) => ( + + +

+ ), + privileges: ( +
    + {indexPrivileges.map(([index, missingPrivileges]) => ( +
  • {missingIndexPrivileges(index, missingPrivileges)}
  • + ))} + {featurePrivileges.map(([feature, missingPrivileges]) => ( +
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • + ))} +
+ ), + docs: ( +
    +
  • + +
  • +
  • + +
  • +
+ ), + }} + /> +); + +interface PrivilegeExplanations { + [key: string]: { + [privilegeName: string]: string; + }; +} + +const PRIVILEGE_EXPLANATIONS: PrivilegeExplanations = { + [SAVED_OBJECTS_MANAGEMENT_FEATURE_ID]: { + all: CANNOT_EDIT_RULES, + }, + [DEFAULT_SIGNALS_INDEX]: { + write: CANNOT_EDIT_ALERTS, + }, + [DEFAULT_LISTS_INDEX]: { + write: CANNOT_EDIT_LISTS, + }, + [DEFAULT_ITEMS_INDEX]: { + write: CANNOT_EDIT_LISTS, + }, +}; + +const getPrivilegesExplanation = (missingPrivileges: string[], index: string) => { + const explanationsByPrivilege = Object.entries(PRIVILEGE_EXPLANATIONS).find(([key]) => + index.startsWith(key) + )?.[1]; + + return missingPrivileges + .map((privilege) => explanationsByPrivilege?.[privilege]) + .filter(Boolean) + .join(' '); +}; + +const missingIndexPrivileges = (index: string, privileges: string[]) => ( + , + index: {index}, + explanation: getPrivilegesExplanation(privileges, index), + }} + /> +); + +const missingFeaturePrivileges = (feature: string, privileges: string[]) => ( + , + index: {feature}, + explanation: getPrivilegesExplanation(privileges, feature), + }} + /> +); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts new file mode 100644 index 0000000000000..dd139421e9ddd --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts @@ -0,0 +1,91 @@ +/* + * 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 { useMemo } from 'react'; +import { SAVED_OBJECTS_MANAGEMENT_FEATURE_ID } from '../../../../../common/constants'; +import { Privilege } from '../../../containers/detection_engine/alerts/types'; +import { useUserData } from '../../user_info'; +import { useUserPrivileges } from '../../user_privileges'; + +const REQUIRED_INDEX_PRIVILIGES = ['read', 'write', 'view_index_metadata', 'maintenance'] as const; + +const getIndexName = (indexPrivileges: Privilege['index']) => { + const [indexName] = Object.keys(indexPrivileges); + + return indexName; +}; + +const getMissingIndexPrivileges = ( + indexPrivileges: Privilege['index'] +): MissingIndexPrivileges | undefined => { + const indexName = getIndexName(indexPrivileges); + const privileges = indexPrivileges[indexName]; + const missingPrivileges = REQUIRED_INDEX_PRIVILIGES.filter((privelege) => !privileges[privelege]); + + if (missingPrivileges.length) { + return [indexName, missingPrivileges]; + } +}; + +export type MissingFeaturePrivileges = [feature: string, privileges: string[]]; +export type MissingIndexPrivileges = [indexName: string, privileges: string[]]; + +export interface MissingPrivileges { + featurePrivileges: MissingFeaturePrivileges[]; + indexPrivileges: MissingIndexPrivileges[]; +} + +export const useMissingPrivileges = (): MissingPrivileges => { + const { detectionEnginePrivileges, listPrivileges } = useUserPrivileges(); + const [{ canUserCRUD }] = useUserData(); + + return useMemo(() => { + const featurePrivileges: MissingFeaturePrivileges[] = []; + const indexPrivileges: MissingIndexPrivileges[] = []; + + if ( + canUserCRUD == null || + listPrivileges.result == null || + detectionEnginePrivileges.result == null + ) { + /** + * Do not check privileges till we get all the data. That helps to reduce + * subsequent layout shift while loading and skip unneeded re-renders. + */ + return { + featurePrivileges, + indexPrivileges, + }; + } + + if (canUserCRUD === false) { + featurePrivileges.push([SAVED_OBJECTS_MANAGEMENT_FEATURE_ID, ['all']]); + } + + const missingItemsPrivileges = getMissingIndexPrivileges(listPrivileges.result.listItems.index); + if (missingItemsPrivileges) { + indexPrivileges.push(missingItemsPrivileges); + } + + const missingListsPrivileges = getMissingIndexPrivileges(listPrivileges.result.lists.index); + if (missingListsPrivileges) { + indexPrivileges.push(missingListsPrivileges); + } + + const missingDetectionPrivileges = getMissingIndexPrivileges( + detectionEnginePrivileges.result.index + ); + if (missingDetectionPrivileges) { + indexPrivileges.push(missingDetectionPrivileges); + } + + return { + featurePrivileges, + indexPrivileges, + }; + }, [canUserCRUD, listPrivileges, detectionEnginePrivileges]); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/index.tsx deleted file mode 100644 index 94e9fbd87ee65..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/index.tsx +++ /dev/null @@ -1,33 +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 React, { memo } from 'react'; -import { CallOutMessage, CallOutSwitcher } from '../../../../common/components/callouts'; -import { useUserData } from '../../user_info'; - -import * as i18n from './translations'; - -const readOnlyAccessToAlertsMessage: CallOutMessage = { - type: 'primary', - id: 'read-only-access-to-alerts', - title: i18n.READ_ONLY_ALERTS_CALLOUT_TITLE, - description: i18n.readOnlyAlertsCallOutBody(), -}; - -const ReadOnlyAlertsCallOutComponent = () => { - const [{ hasIndexUpdateDelete }] = useUserData(); - - return ( - - ); -}; - -export const ReadOnlyAlertsCallOut = memo(ReadOnlyAlertsCallOutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/translations.tsx deleted file mode 100644 index 000794d2290eb..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_alerts_callout/translations.tsx +++ /dev/null @@ -1,48 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - SecuritySolutionRequirementsLink, - DetectionsRequirementsLink, -} from '../../../../common/components/links_to_docs'; - -export const READ_ONLY_ALERTS_CALLOUT_TITLE = i18n.translate( - 'xpack.securitySolution.detectionEngine.readOnlyAlertsCallOut.messageTitle', - { - defaultMessage: 'You cannot change alert states', - } -); - -export const readOnlyAlertsCallOutBody = () => ( - - -

- ), - docs: ( -
    -
  • - -
  • -
  • - -
  • -
- ), - }} - /> -); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/index.tsx deleted file mode 100644 index c9c47fab0e6c3..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/index.tsx +++ /dev/null @@ -1,33 +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 React, { memo } from 'react'; -import { CallOutMessage, CallOutSwitcher } from '../../../../common/components/callouts'; -import { useUserData } from '../../user_info'; - -import * as i18n from './translations'; - -const readOnlyAccessToRulesMessage: CallOutMessage = { - type: 'primary', - id: 'read-only-access-to-rules', - title: i18n.READ_ONLY_RULES_CALLOUT_TITLE, - description: i18n.readOnlyRulesCallOutBody(), -}; - -const ReadOnlyRulesCallOutComponent = () => { - const [{ canUserCRUD }] = useUserData(); - - return ( - - ); -}; - -export const ReadOnlyRulesCallOut = memo(ReadOnlyRulesCallOutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/translations.tsx deleted file mode 100644 index 59378a0f7db93..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/read_only_rules_callout/translations.tsx +++ /dev/null @@ -1,48 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - SecuritySolutionRequirementsLink, - DetectionsRequirementsLink, -} from '../../../../common/components/links_to_docs'; - -export const READ_ONLY_RULES_CALLOUT_TITLE = i18n.translate( - 'xpack.securitySolution.detectionEngine.readOnlyRulesCallOut.messageTitle', - { - defaultMessage: 'Rule permissions required', - } -); - -export const readOnlyRulesCallOutBody = () => ( - - -

- ), - docs: ( -
    -
  • - -
  • -
  • - -
  • -
- ), - }} - /> -); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 1c9069a9369df..83ca0026b8934 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -5,13 +5,14 @@ * 2.0. */ +import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; import { useUserInfo, ManageUserInfo } from './index'; import { useKibana } from '../../../common/lib/kibana'; import * as api from '../../containers/detection_engine/alerts/api'; import { TestProviders } from '../../../common/mock/test_providers'; -import React from 'react'; +import { UserPrivilegesProvider } from '../user_privileges'; jest.mock('../../../common/lib/kibana'); jest.mock('../../containers/detection_engine/alerts/api'); @@ -63,7 +64,9 @@ describe('useUserInfo', () => { }); const wrapper = ({ children }: { children: JSX.Element }) => ( - {children} + + {children} + ); await act(async () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index b196fd7998ea8..9a8fc5e27a5e4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -8,7 +8,7 @@ import { noop } from 'lodash/fp'; import React, { useEffect, useReducer, Dispatch, createContext, useContext } from 'react'; -import { usePrivilegeUser } from '../../containers/detection_engine/alerts/use_privilege_user'; +import { useAlertsPrivileges } from '../../containers/detection_engine/alerts/use_alerts_privileges'; import { useSignalIndex } from '../../containers/detection_engine/alerts/use_signal_index'; import { useKibana } from '../../../common/lib/kibana'; import { useCreateTransforms } from '../../../transforms/containers/use_create_transforms'; @@ -196,7 +196,7 @@ export const useUserInfo = (): State => { hasIndexMaintenance: hasApiIndexMaintenance, hasIndexWrite: hasApiIndexWrite, hasIndexUpdateDelete: hasApiIndexUpdateDelete, - } = usePrivilegeUser(); + } = useAlertsPrivileges(); const { loading: indexNameLoading, signalIndexExists: isApiSignalIndexExists, @@ -208,8 +208,7 @@ export const useUserInfo = (): State => { const { createTransforms } = useCreateTransforms(); const uiCapabilities = useKibana().services.application.capabilities; - const capabilitiesCanUserCRUD: boolean = - typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false; + const capabilitiesCanUserCRUD: boolean = uiCapabilities.siem.crud === true; useEffect(() => { if (loading !== (privilegeLoading || indexNameLoading)) { @@ -275,7 +274,7 @@ export const useUserInfo = (): State => { }, [dispatch, loading, hasEncryptionKey, isApiEncryptionKey]); useEffect(() => { - if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { + if (!loading && canUserCRUD !== capabilitiesCanUserCRUD) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); } }, [dispatch, loading, canUserCRUD, capabilitiesCanUserCRUD]); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_privileges/index.tsx new file mode 100644 index 0000000000000..bd6ff11b27f96 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/index.tsx @@ -0,0 +1,42 @@ +/* + * 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 React, { createContext, useContext } from 'react'; +import { useFetchDetectionEnginePrivileges } from './use_fetch_detection_engine_privileges'; +import { useFetchListPrivileges } from './use_fetch_list_privileges'; + +export interface UserPrivilegesState { + listPrivileges: ReturnType; + detectionEnginePrivileges: ReturnType; +} + +const UserPrivilegesContext = createContext({ + listPrivileges: { loading: false, error: undefined, result: undefined }, + detectionEnginePrivileges: { loading: false, error: undefined, result: undefined }, +}); + +interface UserPrivilegesProviderProps { + children: React.ReactNode; +} + +export const UserPrivilegesProvider = ({ children }: UserPrivilegesProviderProps) => { + const listPrivileges = useFetchListPrivileges(); + const detectionEnginePrivileges = useFetchDetectionEnginePrivileges(); + + return ( + + {children} + + ); +}; + +export const useUserPrivileges = () => useContext(UserPrivilegesContext); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts new file mode 100644 index 0000000000000..c2ae389a0c571 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts @@ -0,0 +1,22 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const LISTS_PRIVILEGES_FETCH_FAILURE = i18n.translate( + 'xpack.securitySolution.containers.detectionEngine.alerts.readListsPrivileges.errorDescription', + { + defaultMessage: 'Failed to retrieve lists privileges', + } +); + +export const DETECTION_ENGINE_PRIVILEGES_FETCH_FAILURE = i18n.translate( + 'xpack.securitySolution.containers.detectionEngine.alerts.detectionEnginePrivileges.errorFetching', + { + defaultMessage: 'Failed to retreive detection engine privileges', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.mock.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.mock.ts new file mode 100644 index 0000000000000..8be752f3a4f4d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.mock.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useFetchDetectionEnginePrivileges } from './use_fetch_detection_engine_privileges'; + +export const useFetchDetectionEnginePrivilegesMock: () => jest.Mocked< + ReturnType +> = () => ({ loading: false, error: undefined, result: undefined }); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts new file mode 100644 index 0000000000000..f1d78b86f11a2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts @@ -0,0 +1,47 @@ +/* + * 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 { useEffect, useRef } from 'react'; +import { useAppToasts } from '../../../common/hooks/use_app_toasts'; +import { useAsync, withOptionalSignal } from '../../../shared_imports'; +import { getUserPrivilege } from '../../containers/detection_engine/alerts/api'; +import * as i18n from './translations'; + +export const useFetchPrivileges = () => useAsync(withOptionalSignal(getUserPrivilege)); + +export const useFetchDetectionEnginePrivileges = () => { + const { start, ...detectionEnginePrivileges } = useFetchPrivileges(); + const { addError } = useAppToasts(); + const abortCtrlRef = useRef(new AbortController()); + + useEffect(() => { + const { loading, result, error } = detectionEnginePrivileges; + + if (!loading && !(result || error)) { + abortCtrlRef.current.abort(); + abortCtrlRef.current = new AbortController(); + start({ signal: abortCtrlRef.current.signal }); + } + }, [start, detectionEnginePrivileges]); + + useEffect(() => { + return () => { + abortCtrlRef.current.abort(); + }; + }, []); + + useEffect(() => { + const error = detectionEnginePrivileges.error; + if (error != null) { + addError(error, { + title: i18n.DETECTION_ENGINE_PRIVILEGES_FETCH_FAILURE, + }); + } + }, [addError, detectionEnginePrivileges.error]); + + return detectionEnginePrivileges; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts new file mode 100644 index 0000000000000..6d14870935fed --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useRef } from 'react'; +import { useAppToasts } from '../../../common/hooks/use_app_toasts'; +import { useHttp, useKibana } from '../../../common/lib/kibana'; +import { useReadListPrivileges } from '../../../shared_imports'; +import { Privilege } from '../../containers/detection_engine/alerts/types'; +import * as i18n from './translations'; + +interface ListPrivileges { + is_authenticated: boolean; + lists: { + index: Privilege['index']; + }; + listItems: { + index: Privilege['index']; + }; +} + +export const useFetchListPrivileges = () => { + const http = useHttp(); + const { lists } = useKibana().services; + const { start: fetchListPrivileges, ...listPrivileges } = useReadListPrivileges(); + const { addError } = useAppToasts(); + const abortCtrlRef = useRef(new AbortController()); + + useEffect(() => { + const { loading, result, error } = listPrivileges; + + if (lists && !loading && !(result || error)) { + abortCtrlRef.current.abort(); + abortCtrlRef.current = new AbortController(); + fetchListPrivileges({ http, signal: abortCtrlRef.current.signal }); + } + }, [http, lists, fetchListPrivileges, listPrivileges]); + + useEffect(() => { + return () => { + abortCtrlRef.current.abort(); + }; + }, []); + + useEffect(() => { + const error = listPrivileges.error; + if (error != null) { + addError(error, { + title: i18n.LISTS_PRIVILEGES_FETCH_FAILURE, + }); + } + }, [addError, listPrivileges.error]); + + return { + loading: listPrivileges.loading, + error: listPrivileges.error, + result: listPrivileges.result as ListPrivileges | undefined, + }; +}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/translations.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/translations.ts index a4cc80debc1ba..2998c97376c26 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/translations.ts @@ -14,13 +14,6 @@ export const ALERT_FETCH_FAILURE = i18n.translate( } ); -export const PRIVILEGE_FETCH_FAILURE = i18n.translate( - 'xpack.securitySolution.containers.detectionEngine.alerts.errorFetchingAlertsDescription', - { - defaultMessage: 'Failed to query alerts', - } -); - export const SIGNAL_GET_NAME_FAILURE = i18n.translate( 'xpack.securitySolution.containers.detectionEngine.alerts.errorGetAlertDescription', { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx new file mode 100644 index 0000000000000..651d80f3165ab --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx @@ -0,0 +1,196 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks'; +import produce from 'immer'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; +import { useUserPrivileges } from '../../../components/user_privileges'; +import { Privilege } from './types'; +import { UseAlertsPrivelegesReturn, useAlertsPrivileges } from './use_alerts_privileges'; + +jest.mock('./api'); +jest.mock('../../../../common/hooks/use_app_toasts'); +jest.mock('../../../components/user_privileges'); + +const useUserPrivilegesMock = useUserPrivileges as jest.Mock>; + +const privilege: Privilege = { + username: 'soc_manager', + has_all_requested: false, + cluster: { + monitor_ml: false, + manage_ccr: false, + manage_index_templates: false, + monitor_watcher: false, + monitor_transform: false, + read_ilm: false, + manage_api_key: false, + manage_security: false, + manage_own_api_key: false, + manage_saml: false, + all: false, + manage_ilm: false, + manage_ingest_pipelines: false, + read_ccr: false, + manage_rollup: false, + monitor: false, + manage_watcher: false, + manage: true, + manage_transform: false, + manage_token: false, + manage_ml: false, + manage_pipeline: false, + monitor_rollup: false, + transport_client: false, + create_snapshot: false, + }, + index: { + '.siem-signals-default': { + all: false, + manage_ilm: true, + read: true, + create_index: true, + read_cross_cluster: false, + index: true, + monitor: true, + delete: true, + manage: true, + delete_index: true, + create_doc: true, + view_index_metadata: true, + create: true, + manage_follow_index: true, + manage_leader_index: true, + maintenance: true, + write: true, + }, + }, + application: {}, + is_authenticated: true, + has_encryption_key: true, +}; + +const userPrivilegesInitial: ReturnType = { + detectionEnginePrivileges: { + loading: false, + result: undefined, + error: undefined, + }, + listPrivileges: { + loading: false, + result: undefined, + error: undefined, + }, +}; + +describe('usePrivilegeUser', () => { + let appToastsMock: jest.Mocked>; + + beforeEach(() => { + jest.resetAllMocks(); + appToastsMock = useAppToastsMock.create(); + (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); + useUserPrivilegesMock.mockReturnValue(userPrivilegesInitial); + }); + + test('init', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: null, + hasIndexManage: null, + hasIndexRead: null, + hasIndexMaintenance: null, + hasIndexWrite: null, + hasIndexUpdateDelete: null, + isAuthenticated: null, + loading: false, + }); + }); + }); + + test('if there is an error when fetching user privilege, we should get back false for every properties', async () => { + const userPrivileges = produce(userPrivilegesInitial, (draft) => { + draft.detectionEnginePrivileges.error = new Error('Something went wrong'); + }); + useUserPrivilegesMock.mockReturnValue(userPrivileges); + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: false, + hasIndexManage: false, + hasIndexMaintenance: false, + hasIndexRead: false, + hasIndexWrite: false, + hasIndexUpdateDelete: false, + isAuthenticated: false, + loading: false, + }); + }); + }); + + test('returns "hasIndexManage" is false if the privilege does not have cluster manage', async () => { + const privilegeWithClusterManage = produce(privilege, (draft) => { + draft.cluster.manage = false; + }); + const userPrivileges = produce(userPrivilegesInitial, (draft) => { + draft.detectionEnginePrivileges.result = privilegeWithClusterManage; + }); + useUserPrivilegesMock.mockReturnValue(userPrivileges); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: true, + hasIndexManage: false, + hasIndexMaintenance: true, + hasIndexRead: true, + hasIndexWrite: true, + hasIndexUpdateDelete: true, + isAuthenticated: true, + loading: false, + }); + }); + }); + + test('returns "hasIndexManage" is true if the privilege has cluster manage', async () => { + const userPrivileges = produce(userPrivilegesInitial, (draft) => { + draft.detectionEnginePrivileges.result = privilege; + }); + useUserPrivilegesMock.mockReturnValue(userPrivileges); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: true, + hasIndexManage: true, + hasIndexMaintenance: true, + hasIndexRead: true, + hasIndexWrite: true, + hasIndexUpdateDelete: true, + isAuthenticated: true, + loading: false, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx new file mode 100644 index 0000000000000..08e28521e1473 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx @@ -0,0 +1,78 @@ +/* + * 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 { useEffect, useState } from 'react'; +import { useUserPrivileges } from '../../../components/user_privileges'; + +export interface UseAlertsPrivelegesReturn extends AlertsPrivelegesState { + loading: boolean; +} + +export interface AlertsPrivelegesState { + isAuthenticated: boolean | null; + hasEncryptionKey: boolean | null; + hasIndexManage: boolean | null; + hasIndexWrite: boolean | null; + hasIndexUpdateDelete: boolean | null; + hasIndexMaintenance: boolean | null; + hasIndexRead: boolean | null; +} +/** + * Hook to get user privilege from + * + */ +export const useAlertsPrivileges = (): UseAlertsPrivelegesReturn => { + const [privileges, setPrivileges] = useState({ + isAuthenticated: null, + hasEncryptionKey: null, + hasIndexManage: null, + hasIndexRead: null, + hasIndexWrite: null, + hasIndexUpdateDelete: null, + hasIndexMaintenance: null, + }); + const { detectionEnginePrivileges } = useUserPrivileges(); + + useEffect(() => { + if (detectionEnginePrivileges.error != null) { + setPrivileges({ + isAuthenticated: false, + hasEncryptionKey: false, + hasIndexManage: false, + hasIndexRead: false, + hasIndexWrite: false, + hasIndexUpdateDelete: false, + hasIndexMaintenance: false, + }); + } + }, [detectionEnginePrivileges.error]); + + useEffect(() => { + if (detectionEnginePrivileges.result != null) { + const privilege = detectionEnginePrivileges.result; + + if (privilege.index != null && Object.keys(privilege.index).length > 0) { + const indexName = Object.keys(privilege.index)[0]; + setPrivileges({ + isAuthenticated: privilege.is_authenticated, + hasEncryptionKey: privilege.has_encryption_key, + hasIndexManage: privilege.index[indexName].manage && privilege.cluster.manage, + hasIndexMaintenance: privilege.index[indexName].maintenance, + hasIndexRead: privilege.index[indexName].read, + hasIndexWrite: + privilege.index[indexName].create || + privilege.index[indexName].create_doc || + privilege.index[indexName].index || + privilege.index[indexName].write, + hasIndexUpdateDelete: privilege.index[indexName].write, + }); + } + } + }, [detectionEnginePrivileges.result]); + + return { loading: detectionEnginePrivileges.loading, ...privileges }; +}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.test.tsx deleted file mode 100644 index c17d227428391..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.test.tsx +++ /dev/null @@ -1,238 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { usePrivilegeUser, ReturnPrivilegeUser } from './use_privilege_user'; -import * as api from './api'; -import { Privilege } from './types'; -import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; -import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; - -jest.mock('./api'); -jest.mock('../../../../common/hooks/use_app_toasts'); - -describe('usePrivilegeUser', () => { - let appToastsMock: jest.Mocked>; - - beforeEach(() => { - jest.resetAllMocks(); - appToastsMock = useAppToastsMock.create(); - (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); - }); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePrivilegeUser() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - hasEncryptionKey: null, - hasIndexManage: null, - hasIndexMaintenance: null, - hasIndexWrite: null, - hasIndexUpdateDelete: null, - isAuthenticated: null, - loading: true, - }); - }); - }); - - test('fetch user privilege', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePrivilegeUser() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - hasEncryptionKey: true, - hasIndexManage: true, - hasIndexMaintenance: true, - hasIndexWrite: true, - hasIndexUpdateDelete: true, - isAuthenticated: true, - loading: false, - }); - }); - }); - - test('if there is an error when fetching user privilege, we should get back false for every properties', async () => { - const spyOnGetUserPrivilege = jest.spyOn(api, 'getUserPrivilege'); - spyOnGetUserPrivilege.mockImplementation(() => { - throw new Error('Something went wrong, let see what happen'); - }); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePrivilegeUser() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - hasEncryptionKey: false, - hasIndexManage: false, - hasIndexMaintenance: false, - hasIndexWrite: false, - hasIndexUpdateDelete: false, - isAuthenticated: false, - loading: false, - }); - }); - }); - - test('returns "hasIndexManage" is false if the privilege does not have cluster manage', async () => { - const privilege: Privilege = { - username: 'soc_manager', - has_all_requested: false, - cluster: { - monitor_ml: false, - manage_ccr: false, - manage_index_templates: false, - monitor_watcher: false, - monitor_transform: false, - read_ilm: false, - manage_api_key: false, - manage_security: false, - manage_own_api_key: false, - manage_saml: false, - all: false, - manage_ilm: false, - manage_ingest_pipelines: false, - read_ccr: false, - manage_rollup: false, - monitor: false, - manage_watcher: false, - manage: false, - manage_transform: false, - manage_token: false, - manage_ml: false, - manage_pipeline: false, - monitor_rollup: false, - transport_client: false, - create_snapshot: false, - }, - index: { - '.siem-signals-default': { - all: false, - manage_ilm: true, - read: true, - create_index: true, - read_cross_cluster: false, - index: true, - monitor: true, - delete: true, - manage: true, - delete_index: true, - create_doc: true, - view_index_metadata: true, - create: true, - manage_follow_index: true, - manage_leader_index: true, - maintenance: true, - write: true, - }, - }, - application: {}, - is_authenticated: true, - has_encryption_key: true, - }; - const spyOnGetUserPrivilege = jest.spyOn(api, 'getUserPrivilege'); - spyOnGetUserPrivilege.mockImplementation(() => Promise.resolve(privilege)); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePrivilegeUser() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - hasEncryptionKey: true, - hasIndexManage: false, - hasIndexMaintenance: true, - hasIndexWrite: true, - hasIndexUpdateDelete: true, - isAuthenticated: true, - loading: false, - }); - }); - }); - - test('returns "hasIndexManage" is true if the privilege has cluster manage', async () => { - const privilege: Privilege = { - username: 'soc_manager', - has_all_requested: false, - cluster: { - monitor_ml: false, - manage_ccr: false, - manage_index_templates: false, - monitor_watcher: false, - monitor_transform: false, - read_ilm: false, - manage_api_key: false, - manage_security: false, - manage_own_api_key: false, - manage_saml: false, - all: false, - manage_ilm: false, - manage_ingest_pipelines: false, - read_ccr: false, - manage_rollup: false, - monitor: false, - manage_watcher: false, - manage: true, - manage_transform: false, - manage_token: false, - manage_ml: false, - manage_pipeline: false, - monitor_rollup: false, - transport_client: false, - create_snapshot: false, - }, - index: { - '.siem-signals-default': { - all: false, - manage_ilm: true, - read: true, - create_index: true, - read_cross_cluster: false, - index: true, - monitor: true, - delete: true, - manage: true, - delete_index: true, - create_doc: true, - view_index_metadata: true, - create: true, - manage_follow_index: true, - manage_leader_index: true, - maintenance: true, - write: true, - }, - }, - application: {}, - is_authenticated: true, - has_encryption_key: true, - }; - const spyOnGetUserPrivilege = jest.spyOn(api, 'getUserPrivilege'); - spyOnGetUserPrivilege.mockImplementation(() => Promise.resolve(privilege)); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - usePrivilegeUser() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - hasEncryptionKey: true, - hasIndexManage: true, - hasIndexMaintenance: true, - hasIndexWrite: true, - hasIndexUpdateDelete: true, - isAuthenticated: true, - loading: false, - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.tsx deleted file mode 100644 index dd4da78db4e8e..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_privilege_user.tsx +++ /dev/null @@ -1,103 +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 { useEffect, useState } from 'react'; -import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; - -import { getUserPrivilege } from './api'; -import * as i18n from './translations'; - -export interface ReturnPrivilegeUser { - loading: boolean; - isAuthenticated: boolean | null; - hasEncryptionKey: boolean | null; - hasIndexManage: boolean | null; - hasIndexWrite: boolean | null; - hasIndexUpdateDelete: boolean | null; - hasIndexMaintenance: boolean | null; -} -/** - * Hook to get user privilege from - * - */ -export const usePrivilegeUser = (): ReturnPrivilegeUser => { - const [loading, setLoading] = useState(true); - const [privilegeUser, setPrivilegeUser] = useState< - Pick< - ReturnPrivilegeUser, - | 'isAuthenticated' - | 'hasEncryptionKey' - | 'hasIndexManage' - | 'hasIndexWrite' - | 'hasIndexUpdateDelete' - | 'hasIndexMaintenance' - > - >({ - isAuthenticated: null, - hasEncryptionKey: null, - hasIndexManage: null, - hasIndexWrite: null, - hasIndexUpdateDelete: null, - hasIndexMaintenance: null, - }); - const { addError } = useAppToasts(); - - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - setLoading(true); - - const fetchData = async () => { - try { - const privilege = await getUserPrivilege({ - signal: abortCtrl.signal, - }); - - if (isSubscribed && privilege != null) { - if (privilege.index != null && Object.keys(privilege.index).length > 0) { - const indexName = Object.keys(privilege.index)[0]; - setPrivilegeUser({ - isAuthenticated: privilege.is_authenticated, - hasEncryptionKey: privilege.has_encryption_key, - hasIndexManage: privilege.index[indexName].manage && privilege.cluster.manage, - hasIndexMaintenance: privilege.index[indexName].maintenance, - hasIndexWrite: - privilege.index[indexName].create || - privilege.index[indexName].create_doc || - privilege.index[indexName].index || - privilege.index[indexName].write, - hasIndexUpdateDelete: privilege.index[indexName].write, - }); - } - } - } catch (error) { - if (isSubscribed) { - setPrivilegeUser({ - isAuthenticated: false, - hasEncryptionKey: false, - hasIndexManage: false, - hasIndexWrite: false, - hasIndexUpdateDelete: false, - hasIndexMaintenance: false, - }); - addError(error, { title: i18n.PRIVILEGE_FETCH_FAILURE }); - } - } - if (isSubscribed) { - setLoading(false); - } - }; - - fetchData(); - return () => { - isSubscribed = false; - abortCtrl.abort(); - }; - }, [addError]); - - return { loading, ...privilegeUser }; -}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx index ad696703bd7e9..76811cecce8b8 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { renderHook, act } from '@testing-library/react-hooks'; import { TestProviders } from '../../../../common/mock'; import { useSignalIndex, ReturnSignalIndex } from './use_signal_index'; @@ -54,6 +53,7 @@ describe('useSignalIndex', () => { ); await waitForNextUpdate(); await waitForNextUpdate(); + await waitForNextUpdate(); expect(result.current).toEqual({ createDeSignalIndex: result.current.createDeSignalIndex, loading: false, @@ -74,6 +74,7 @@ describe('useSignalIndex', () => { ); await waitForNextUpdate(); await waitForNextUpdate(); + await waitForNextUpdate(); if (result.current.createDeSignalIndex != null) { await result.current.createDeSignalIndex(); } @@ -99,6 +100,7 @@ describe('useSignalIndex', () => { ); await waitForNextUpdate(); await waitForNextUpdate(); + await waitForNextUpdate(); if (result.current.createDeSignalIndex != null) { await result.current.createDeSignalIndex(); } @@ -121,6 +123,7 @@ describe('useSignalIndex', () => { ); await waitForNextUpdate(); await waitForNextUpdate(); + await waitForNextUpdate(); if (result.current.createDeSignalIndex != null) { await result.current.createDeSignalIndex(); } @@ -148,6 +151,7 @@ describe('useSignalIndex', () => { ); await waitForNextUpdate(); await waitForNextUpdate(); + await waitForNextUpdate(); expect(result.current).toEqual({ createDeSignalIndex: result.current.createDeSignalIndex, loading: false, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx index eea4aecc8670e..84eaf8e3aa93c 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx @@ -13,6 +13,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex import { createSignalIndex, getSignalIndex } from './api'; import * as i18n from './translations'; import { isSecurityAppError } from '../../../../common/utils/api'; +import { useAlertsPrivileges } from './use_alerts_privileges'; type Func = () => Promise; @@ -38,6 +39,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { createDeSignalIndex: null, }); const { addError } = useAppToasts(); + const { hasIndexRead } = useAlertsPrivileges(); // TODO: Once we are past experimental phase this code should be removed const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); @@ -111,12 +113,18 @@ export const useSignalIndex = (): ReturnSignalIndex => { } }; - fetchData(); + if (hasIndexRead) { + fetchData(); + } else { + // Skip data fetching as the current user doesn't have enough priviliges. + // Attempt to get the signal index will result in 500 error. + setLoading(false); + } return () => { isSubscribed = false; abortCtrl.abort(); }; - }, [addError, ruleRegistryEnabled]); + }, [addError, hasIndexRead, ruleRegistryEnabled]); return { loading, ...signalIndex }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/translations.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/translations.ts index e5c5ea3d2b8c8..550b4cdb4b81a 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/translations.ts @@ -20,10 +20,3 @@ export const LISTS_INDEX_CREATE_FAILURE = i18n.translate( defaultMessage: 'Failed to create the lists index', } ); - -export const LISTS_PRIVILEGES_READ_FAILURE = i18n.translate( - 'xpack.securitySolution.containers.detectionEngine.alerts.readListsPrivileges.errorDescription', - { - defaultMessage: 'Failed to retrieve lists privileges', - } -); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_index.tsx index 06d717b97da83..ccd8ca79348fe 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_index.tsx @@ -12,6 +12,7 @@ import { useHttp, useKibana } from '../../../../common/lib/kibana'; import { isSecurityAppError } from '../../../../common/utils/api'; import * as i18n from './translations'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useListsPrivileges } from './use_lists_privileges'; export interface UseListsIndexReturn { createIndex: () => void; @@ -26,6 +27,7 @@ export const useListsIndex = (): UseListsIndexReturn => { const { lists } = useKibana().services; const http = useHttp(); const { addError } = useAppToasts(); + const { canReadIndex } = useListsPrivileges(); const { loading: readLoading, start: readListIndex, ...readListIndexState } = useReadListIndex(); const { loading: createLoading, @@ -35,10 +37,10 @@ export const useListsIndex = (): UseListsIndexReturn => { const loading = readLoading || createLoading; const readIndex = useCallback(() => { - if (lists) { + if (lists && canReadIndex) { readListIndex({ http }); } - }, [http, lists, readListIndex]); + }, [http, lists, readListIndex, canReadIndex]); const createIndex = useCallback(() => { if (lists) { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.mock.ts index 7f5e313a44f78..a7dd1f343f0a1 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.mock.ts @@ -9,6 +9,7 @@ import { UseListsPrivilegesReturn } from './use_lists_privileges'; export const getUseListsPrivilegesMock: () => jest.Mocked = () => ({ isAuthenticated: null, + canReadIndex: null, canManageIndex: null, canWriteIndex: null, loading: false, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.tsx index 07cd3a2b206ac..0b7cd673c49f4 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_privileges.tsx @@ -5,16 +5,14 @@ * 2.0. */ -import { useEffect, useState, useCallback } from 'react'; - -import { useReadListPrivileges } from '../../../../shared_imports'; -import { useHttp, useKibana } from '../../../../common/lib/kibana'; -import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; -import * as i18n from './translations'; +import { useEffect, useState } from 'react'; +import { useUserPrivileges } from '../../../components/user_privileges'; +import { Privilege } from '../alerts/types'; export interface UseListsPrivilegesState { isAuthenticated: boolean | null; canManageIndex: boolean | null; + canReadIndex: boolean | null; canWriteIndex: boolean | null; } @@ -22,38 +20,7 @@ export interface UseListsPrivilegesReturn extends UseListsPrivilegesState { loading: boolean; } -interface ListIndexPrivileges { - [indexName: string]: { - all: boolean; - create: boolean; - create_doc: boolean; - create_index: boolean; - delete: boolean; - delete_index: boolean; - index: boolean; - manage: boolean; - manage_follow_index: boolean; - manage_ilm: boolean; - manage_leader_index: boolean; - monitor: boolean; - read: boolean; - read_cross_cluster: boolean; - view_index_metadata: boolean; - write: boolean; - }; -} - -interface ListPrivileges { - is_authenticated: boolean; - lists: { - index: ListIndexPrivileges; - }; - listItems: { - index: ListIndexPrivileges; - }; -} - -const canManageIndex = (indexPrivileges: ListIndexPrivileges): boolean => { +const canManageIndex = (indexPrivileges: Privilege['index']): boolean => { const [indexName] = Object.keys(indexPrivileges); const privileges = indexPrivileges[indexName]; if (privileges == null) { @@ -62,7 +29,17 @@ const canManageIndex = (indexPrivileges: ListIndexPrivileges): boolean => { return privileges.manage; }; -const canWriteIndex = (indexPrivileges: ListIndexPrivileges): boolean => { +const canReadIndex = (indexPrivileges: Privilege['index']): boolean => { + const [indexName] = Object.keys(indexPrivileges); + const privileges = indexPrivileges[indexName]; + if (privileges == null) { + return false; + } + + return privileges.read; +}; + +const canWriteIndex = (indexPrivileges: Privilege['index']): boolean => { const [indexName] = Object.keys(indexPrivileges); const privileges = indexPrivileges[indexName]; if (privileges == null) { @@ -76,57 +53,41 @@ export const useListsPrivileges = (): UseListsPrivilegesReturn => { const [state, setState] = useState({ isAuthenticated: null, canManageIndex: null, + canReadIndex: null, canWriteIndex: null, }); - const { lists } = useKibana().services; - const http = useHttp(); - const { addError } = useAppToasts(); - const { loading, start: readListPrivileges, ...readState } = useReadListPrivileges(); - const readPrivileges = useCallback(() => { - if (lists) { - readListPrivileges({ http }); - } - }, [http, lists, readListPrivileges]); - - // initRead - useEffect(() => { - if (!loading && !readState.error && state.isAuthenticated === null) { - readPrivileges(); - } - }, [loading, readState.error, readPrivileges, state.isAuthenticated]); + const { listPrivileges } = useUserPrivileges(); // handleReadResult useEffect(() => { - if (readState.result != null) { - try { - const { - is_authenticated: isAuthenticated, - lists: { index: listsPrivileges }, - listItems: { index: listItemsPrivileges }, - } = readState.result as ListPrivileges; - - setState({ - isAuthenticated, - canManageIndex: canManageIndex(listsPrivileges) && canManageIndex(listItemsPrivileges), - canWriteIndex: canWriteIndex(listsPrivileges) && canWriteIndex(listItemsPrivileges), - }); - } catch (e) { - setState({ isAuthenticated: null, canManageIndex: false, canWriteIndex: false }); - } + if (listPrivileges.result != null) { + const { + is_authenticated: isAuthenticated, + lists: { index: listsPrivileges }, + listItems: { index: listItemsPrivileges }, + } = listPrivileges.result; + + setState({ + isAuthenticated, + canReadIndex: canReadIndex(listsPrivileges) && canReadIndex(listItemsPrivileges), + canManageIndex: canManageIndex(listsPrivileges) && canManageIndex(listItemsPrivileges), + canWriteIndex: canWriteIndex(listsPrivileges) && canWriteIndex(listItemsPrivileges), + }); } - }, [readState.result]); + }, [listPrivileges.result]); // handleReadError useEffect(() => { - const error = readState.error; - if (error != null) { - setState({ isAuthenticated: false, canManageIndex: false, canWriteIndex: false }); - addError(error, { - title: i18n.LISTS_PRIVILEGES_READ_FAILURE, + if (listPrivileges.error != null) { + setState({ + isAuthenticated: false, + canManageIndex: false, + canReadIndex: false, + canWriteIndex: false, }); } - }, [addError, readState.error]); + }, [listPrivileges.error]); - return { loading, ...state }; + return { loading: listPrivileges.loading, ...state }; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 02e18d09710d7..c1c7e4688bbbe 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -28,7 +28,6 @@ import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { useAlertInfo } from '../../components/alerts_info'; import { AlertsTable } from '../../components/alerts_table'; import { NoApiIntegrationKeyCallOut } from '../../components/callouts/no_api_integration_callout'; -import { ReadOnlyAlertsCallOut } from '../../components/callouts/read_only_alerts_callout'; import { AlertsHistogramPanel } from '../../components/alerts_histogram_panel'; import { alertsHistogramOptions } from '../../components/alerts_histogram_panel/config'; import { useUserData } from '../../components/user_info'; @@ -57,6 +56,7 @@ import { import { useSourcererScope } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { NeedAdminForUpdateRulesCallOut } from '../../components/callouts/need_admin_for_update_callout'; +import { MissingPrivilegesCallOut } from '../../components/callouts/missing_privileges_callout'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -211,8 +211,8 @@ const DetectionEnginePageComponent = () => { return ( <> {hasEncryptionKey != null && !hasEncryptionKey && } - + {indicesExist ? ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index a8d3742bfd600..d4f8db14d8d33 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -61,8 +61,6 @@ import { buildShowBuildingBlockFilter, buildThreatMatchFilter, } from '../../../../components/alerts_table/default_config'; -import { ReadOnlyAlertsCallOut } from '../../../../components/callouts/read_only_alerts_callout'; -import { ReadOnlyRulesCallOut } from '../../../../components/callouts/read_only_rules_callout'; import { RuleSwitch } from '../../../../components/rules/rule_switch'; import { StepPanel } from '../../../../components/rules/step_panel'; import { getStepsData, redirectToDetections, userHasNoPermissions } from '../helpers'; @@ -109,6 +107,7 @@ import * as i18n from './translations'; import { isTab } from '../../../../../common/components/accessibility/helpers'; import { NeedAdminForUpdateRulesCallOut } from '../../../../components/callouts/need_admin_for_update_callout'; import { getRuleStatusText } from '../../../../../../common/detection_engine/utils'; +import { MissingPrivilegesCallOut } from '../../../../components/callouts/missing_privileges_callout'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -528,8 +527,7 @@ const RuleDetailsPageComponent = () => { return ( <> - - + {indicesExist ? ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx index 57566986e47d2..8aca1cb960c1d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx @@ -22,7 +22,6 @@ import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { useUserData } from '../../../components/user_info'; import { AllRules } from './all'; import { ImportDataModal } from '../../../../common/components/import_data_modal'; -import { ReadOnlyRulesCallOut } from '../../../components/callouts/read_only_rules_callout'; import { ValueListsModal } from '../../../components/value_lists_management_modal'; import { UpdatePrePackagedRulesCallOut } from '../../../components/rules/pre_packaged_rules/update_callout'; import { @@ -37,6 +36,7 @@ import { LinkButton } from '../../../../common/components/links'; import { useFormatUrl } from '../../../../common/components/link_to'; import { NeedAdminForUpdateRulesCallOut } from '../../../components/callouts/need_admin_for_update_callout'; import { MlJobCompatibilityCallout } from '../../../components/callouts/ml_job_compatibility_callout'; +import { MissingPrivilegesCallOut } from '../../../components/callouts/missing_privileges_callout'; type Func = () => Promise; @@ -161,7 +161,7 @@ const RulesPageComponent: React.FC = () => { return ( <> - + = { + [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, + [AdministrationSubTab.policies]: POLICIES_TAB, + [AdministrationSubTab.trustedApps]: TRUSTED_APPS_TAB, + [AdministrationSubTab.eventFilters]: EVENT_FILTERS_TAB, +}; + +export function getBreadcrumbs( + params: AdministrationRouteSpyState, + search: string[], + getUrlForApp: GetUrlForApp +): ChromeBreadcrumb[] { + return [ + { + text: ADMINISTRATION, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.administration}`, { + path: !isEmpty(search[0]) ? search[0] : '', + }), + }, + ...(params?.tabName ? [params?.tabName] : []).map((tabName) => ({ + text: TabNameMappedToI18nKey[tabName], + href: '', + })), + ]; +} diff --git a/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx b/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx index ce5d26feb3e75..ea97ce5e30706 100644 --- a/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx +++ b/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx @@ -32,12 +32,13 @@ import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { v4 as generateUUI } from 'uuid'; import { useTestIdGenerator } from '../hooks/use_test_id_generator'; +import { MaybeImmutable } from '../../../../common/endpoint/types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ComponentWithAnyProps = ComponentType; export interface PaginatedContentProps extends CommonProps { - items: T[]; + items: MaybeImmutable; onChange: (changes: { pageIndex: number; pageSize: number }) => void; /** * The React Component that will be used to render the `items`. use `itemComponentProps` below to @@ -79,6 +80,7 @@ interface TypedGenericComponentMemo { const RootContainer = styled.div` position: relative; + padding-top: ${({ theme }) => theme.eui.paddingSizes.xs}; .body { min-height: ${({ theme }) => theme.eui.gutterTypes.gutterExtraLarge}; @@ -174,7 +176,11 @@ export const PaginatedContent = memo( const Item = ItemComponent as ComponentType>; if (items.length) { - return items.map((item) => { + // Cast here is to get around the fact that TS does not seem to be able to narrow the types down when the only + // difference is that the array might be Readonly. The error output is: + // `...has signatures, but none of those signatures are compatible with each other.` + // Can read more about it here: https://github.com/microsoft/TypeScript/issues/33591 + return (items as T[]).map((item) => { let key: Key; if (itemId) { @@ -197,16 +203,15 @@ export const PaginatedContent = memo( return ( - {loading && } + {loading && }
-
{children ? children : generatedBodyItemContent}
- {pagination && ( + {pagination && (children || items.length > 0) && (
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 17ebff603ccfb..304d67d8b6d6f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -47,7 +47,7 @@ jest.mock('../../policy/store/services/ingest', () => { sendGetEndpointSecurityPackage: () => Promise.resolve({}), }; }); -describe('when on the list page', () => { +describe('when on the endpoint list page', () => { const docGenerator = new EndpointDocGenerator(); let render: () => ReturnType; let history: AppContextTestRender['history']; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/index.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/index.tsx index 86c2f2364961d..7d2cb526367c5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/index.tsx @@ -19,3 +19,4 @@ export const EventFiltersContainer = () => { ); }; +export { EventFiltersService } from './types'; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts index b3521d7499362..9d186d590ca67 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/service/index.ts @@ -6,15 +6,15 @@ */ import { HttpStart } from 'kibana/public'; -import { ExceptionListItemSchema, CreateExceptionListItemSchema } from '../../../../shared_imports'; -import { Immutable } from '../../../../../common/endpoint/types'; +import { + CreateExceptionListItemSchema, + ENDPOINT_EVENT_FILTERS_LIST_ID, + ExceptionListItemSchema, +} from '../../../../shared_imports'; import { EVENT_FILTER_LIST, EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '../constants'; +import { FoundExceptionListItemSchema } from '../../../../../../lists/common/schemas'; +import { EventFiltersService } from '../types'; -export interface EventFiltersService { - addEventFilters( - exception: Immutable - ): Promise; -} export class EventFiltersHttpService implements EventFiltersService { private listHasBeenCreated: boolean; @@ -27,6 +27,7 @@ export class EventFiltersHttpService implements EventFiltersService { await this.http.post(EXCEPTION_LIST_URL, { body: JSON.stringify(EVENT_FILTER_LIST), }); + this.listHasBeenCreated = true; } catch (err) { // Ignore 409 errors. List already created if (err.response.status === 409) this.listHasBeenCreated = true; @@ -39,6 +40,30 @@ export class EventFiltersHttpService implements EventFiltersService { return this.http; } + async getList({ + perPage, + page, + sortField, + sortOrder, + }: Partial<{ + page: number; + perPage: number; + sortField: string; + sortOrder: string; + }> = {}): Promise { + const http = await this.httpWrapper(); + return http.get(`${EXCEPTION_LIST_ITEM_URL}/_find`, { + query: { + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + list_id: [ENDPOINT_EVENT_FILTERS_LIST_ID], + namespace_type: ['agnostic'], + }, + }); + } + async addEventFilters(exception: ExceptionListItemSchema | CreateExceptionListItemSchema) { return (await this.httpWrapper()).post(EXCEPTION_LIST_ITEM_URL, { body: JSON.stringify(exception), diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/state/index.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/state/index.ts index caaa48ddf1987..4bc90ce764ace 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/state/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/state/index.ts @@ -7,6 +7,8 @@ import { ExceptionListItemSchema, CreateExceptionListItemSchema } from '../../../../shared_imports'; import { AsyncResourceState } from '../../../state/async_resource_state'; +import { FoundExceptionListItemSchema } from '../../../../../../lists/common/schemas'; +import { EventFiltersServiceGetListOptions } from '../types'; export interface EventFiltersPageLocation { page_index: number; @@ -16,6 +18,7 @@ export interface EventFiltersPageLocation { id?: string; filter: string; } + export interface EventFiltersListPageState { entries: ExceptionListItemSchema[]; form: { @@ -26,4 +29,17 @@ export interface EventFiltersListPageState { submissionResourceState: AsyncResourceState; }; location: EventFiltersPageLocation; + /** State for the Event Filters List page */ + listPage: { + active: boolean; + forceRefresh: boolean; + data: AsyncResourceState<{ + /** The query that was used to retrieve the data */ + query: EventFiltersServiceGetListOptions; + /** The data retrieved from the API */ + content: FoundExceptionListItemSchema; + }>; + /** tracks if the overall list (not filtered or with invalid page numbers) contains data */ + dataExist: AsyncResourceState; + }; } diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/action.ts index 79ef5cbc4e42c..21c8ef63d3eb3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/action.ts @@ -8,6 +8,19 @@ import { Action } from 'redux'; import { ExceptionListItemSchema, CreateExceptionListItemSchema } from '../../../../shared_imports'; import { AsyncResourceState } from '../../../state/async_resource_state'; +import { EventFiltersListPageState } from '../state'; + +export type EventFiltersListPageStateChanged = Action<'eventFiltersListPageStateChanged'> & { + payload: EventFiltersListPageState['listPage']; +}; + +export type EventFiltersListPageDataChanged = Action<'eventFiltersListPageDataChanged'> & { + payload: EventFiltersListPageState['listPage']['data']; +}; + +export type EventFiltersListPageDataExistsChanged = Action<'eventFiltersListPageDataExistsChanged'> & { + payload: EventFiltersListPageState['listPage']['dataExist']; +}; export type EventFiltersInitForm = Action<'eventFiltersInitForm'> & { payload: { @@ -37,10 +50,12 @@ export type EventFiltersFormStateChanged = Action<'eventFiltersFormStateChanged' }; export type EventFiltersPageAction = + | EventFiltersListPageStateChanged + | EventFiltersListPageDataChanged + | EventFiltersListPageDataExistsChanged | EventFiltersCreateStart | EventFiltersInitForm | EventFiltersChangeForm - | EventFiltersCreateStart | EventFiltersCreateSuccess | EventFiltersCreateError | EventFiltersFormStateChanged; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/builders.ts index 92290de4a24ed..8d483c007fe64 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/builders.ts @@ -22,4 +22,11 @@ export const initialEventFiltersPageState = (): EventFiltersListPageState => ({ page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, filter: '', }, + listPage: { + active: false, + forceRefresh: false, + data: { type: 'UninitialisedResourceState' }, + /** We started off assuming data exists, until we can confirm othewise */ + dataExist: { type: 'LoadedResourceState', data: true }, + }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts index b0b47bc1d6fa0..cbd9b7bf0e538 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.test.ts @@ -14,16 +14,17 @@ import { import { AppAction } from '../../../../common/store/actions'; import { createEventFiltersPageMiddleware } from './middleware'; import { eventFiltersPageReducer } from './reducer'; -import { EventFiltersService } from '../service'; import { EventFiltersListPageState } from '../state'; import { initialEventFiltersPageState } from './builders'; import { getInitialExceptionFromEvent } from './utils'; import { createdEventFilterEntryMock, ecsEventMock } from '../test_utils'; +import { EventFiltersService } from '../types'; const initialState: EventFiltersListPageState = initialEventFiltersPageState(); const createEventFiltersServiceMock = (): jest.Mocked => ({ addEventFilters: jest.fn(), + getList: jest.fn(), }); const createStoreSetup = (eventFiltersService: EventFiltersService) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts index b379d0893c133..7aca45049f91a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/middleware.ts @@ -12,16 +12,27 @@ import { ImmutableMiddlewareFactory, } from '../../../../common/store'; -import { EventFiltersHttpService, EventFiltersService } from '../service'; +import { EventFiltersHttpService } from '../service'; import { EventFiltersListPageState } from '../state'; import { getLastLoadedResourceState } from '../../../state/async_resource_state'; import { CreateExceptionListItemSchema, transformNewItemOutput } from '../../../../shared_imports'; +import { + getCurrentListPageDataState, + getCurrentLocation, + getListIsLoading, + getListPageDataExistsState, + getListPageIsActive, + listDataNeedsRefresh, +} from './selector'; +import { EventFiltersService, EventFiltersServiceGetListOptions } from '../types'; -const eventFiltersCreate = async ( +type MiddlewareActionHandler = ( store: ImmutableMiddlewareAPI, eventFiltersService: EventFiltersService -) => { +) => Promise; + +const eventFiltersCreate: MiddlewareActionHandler = async (store, eventFiltersService) => { const submissionResourceState = store.getState().form.submissionResourceState; try { const formEntry = store.getState().form.entry; @@ -62,6 +73,97 @@ const eventFiltersCreate = async ( } }; +const checkIfEventFilterDataExist: MiddlewareActionHandler = async ( + { dispatch, getState }, + eventFiltersService: EventFiltersService +) => { + dispatch({ + type: 'eventFiltersListPageDataExistsChanged', + payload: { + type: 'LoadingResourceState', + // Ignore will be fixed with when AsyncResourceState is refactored (#830) + // @ts-ignore + previousState: getListPageDataExistsState(getState()), + }, + }); + + try { + const anythingInListResults = await eventFiltersService.getList({ perPage: 1, page: 1 }); + + dispatch({ + type: 'eventFiltersListPageDataExistsChanged', + payload: { + type: 'LoadedResourceState', + data: Boolean(anythingInListResults.total), + }, + }); + } catch (error) { + dispatch({ + type: 'eventFiltersListPageDataExistsChanged', + payload: { + type: 'FailedResourceState', + error: error.body || error, + }, + }); + } +}; + +const refreshListDataIfNeeded: MiddlewareActionHandler = async (store, eventFiltersService) => { + const { dispatch, getState } = store; + const state = getState(); + const isLoading = getListIsLoading(state); + + if (!isLoading && listDataNeedsRefresh(state)) { + dispatch({ + type: 'eventFiltersListPageDataChanged', + payload: { + type: 'LoadingResourceState', + // Ignore will be fixed with when AsyncResourceState is refactored (#830) + // @ts-ignore + previousState: getCurrentListPageDataState(state), + }, + }); + + const { page_size: pageSize, page_index: pageIndex } = getCurrentLocation(state); + const query: EventFiltersServiceGetListOptions = { + page: pageIndex + 1, + perPage: pageSize, + sortField: 'created_at', + sortOrder: 'desc', + }; + + try { + const results = await eventFiltersService.getList(query); + + dispatch({ + type: 'eventFiltersListPageDataChanged', + payload: { + type: 'LoadedResourceState', + data: { + query, + content: results, + }, + }, + }); + + // If no results were returned, then just check to make sure data actually exists for + // event filters. This is used to drive the UI between showing "empty state" and "no items found" + // messages to the user + if (results.total === 0) { + await checkIfEventFilterDataExist(store, eventFiltersService); + } + } catch (error) { + dispatch({ + type: 'eventFiltersListPageDataChanged', + payload: { + type: 'FailedResourceState', + error: error.body || error, + }, + }); + } + } +}; + export const createEventFiltersPageMiddleware = ( eventFiltersService: EventFiltersService ): ImmutableMiddleware => { @@ -71,6 +173,13 @@ export const createEventFiltersPageMiddleware = ( if (action.type === 'eventFiltersCreateStart') { await eventFiltersCreate(store, eventFiltersService); } + + // Middleware that only applies to the List Page for Event Filters + if (getListPageIsActive(store.getState())) { + if (action.type === 'userChangedUrl' || action.type === 'eventFiltersCreateSuccess') { + refreshListDataIfNeeded(store, eventFiltersService); + } + } }; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.test.ts index f14f8ecc5f8ad..c78cb030fd3d3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.test.ts @@ -123,6 +123,10 @@ describe('reducer', () => { id: undefined, show: 'create', }, + listPage: { + ...initialState.listPage, + active: true, + }, }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.ts index a52de492f9a66..d9e7a3c6611e5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/reducer.ts @@ -20,10 +20,14 @@ import { EventFiltersChangeForm, EventFiltersFormStateChanged, EventFiltersCreateSuccess, + EventFiltersListPageStateChanged, + EventFiltersListPageDataChanged, + EventFiltersListPageDataExistsChanged, } from './action'; import { EventFiltersListPageState } from '../state'; import { initialEventFiltersPageState } from './builders'; +import { getListPageIsActive } from './selector'; type StateReducer = ImmutableReducer; type CaseReducer = ( @@ -40,6 +44,44 @@ const isEventFiltersPageLocation = (location: Immutable) => { ); }; +// FIXME:PT might not need this. maybe delete +const handleEventFiltersListPageChanges: CaseReducer = ( + state, + action +) => { + return { + ...state, + listPage: action.payload, + }; +}; + +const handleEventFiltersListPageDataChanges: CaseReducer = ( + state, + action +) => { + return { + ...state, + listPage: { + ...state.listPage, + forceRefresh: false, + data: action.payload, + }, + }; +}; + +const handleEventFiltersListPageDataExistChanges: CaseReducer = ( + state, + action +) => { + return { + ...state, + listPage: { + ...state.listPage, + dataExist: action.payload, + }, + }; +}; + const eventFiltersInitForm: CaseReducer = (state, action) => { return { ...state, @@ -89,14 +131,38 @@ const eventFiltersCreateSuccess: CaseReducer = (state return { ...state, entries: [action.payload.exception, ...state.entries], + // If we are on the List page, then force a refresh of data + listPage: getListPageIsActive(state) + ? { + ...state.listPage, + forceRefresh: true, + } + : state.listPage, }; }; const userChangedUrl: CaseReducer = (state, action) => { if (isEventFiltersPageLocation(action.payload)) { const location = extractEventFiltetrsPageLocation(parse(action.payload.search.slice(1))); - return { ...state, location }; + return { + ...state, + location, + listPage: { + ...state.listPage, + active: true, + }, + }; } else { + // Reset the list page state if needed + if (state.listPage.active) { + const { listPage } = initialEventFiltersPageState(); + + return { + ...state, + listPage, + }; + } + return state; } }; @@ -118,5 +184,17 @@ export const eventFiltersPageReducer: StateReducer = ( return userChangedUrl(state, action); } + // actions only handled if we're on the List Page + if (getListPageIsActive(state)) { + switch (action.type) { + case 'eventFiltersListPageStateChanged': + return handleEventFiltersListPageChanges(state, action); + case 'eventFiltersListPageDataChanged': + return handleEventFiltersListPageDataChanges(state, action); + case 'eventFiltersListPageDataExistsChanged': + return handleEventFiltersListPageDataExistChanges(state, action); + } + } + return state; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selector.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selector.ts index dca16be0b1668..671e39bd3f950 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selector.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selector.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { EventFiltersListPageState, EventFiltersPageLocation } from '../state'; +import { createSelector } from 'reselect'; +import { Pagination } from '@elastic/eui'; +import { EventFiltersListPageState } from '../state'; import { ExceptionListItemSchema, CreateExceptionListItemSchema } from '../../../../shared_imports'; import { ServerApiError } from '../../../../common/types'; import { @@ -13,6 +15,111 @@ import { isLoadedResourceState, isFailedResourceState, } from '../../../state/async_resource_state'; +import { FoundExceptionListItemSchema } from '../../../../../../lists/common/schemas'; +import { + MANAGEMENT_DEFAULT_PAGE_SIZE, + MANAGEMENT_PAGE_SIZE_OPTIONS, +} from '../../../common/constants'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { EventFiltersServiceGetListOptions } from '../types'; + +type StoreState = Immutable; +type EventFiltersSelector = (state: StoreState) => T; + +export const getCurrentListPageState: EventFiltersSelector = (state) => { + return state.listPage; +}; + +export const getListPageIsActive: EventFiltersSelector = createSelector( + getCurrentListPageState, + (listPage) => listPage.active +); + +export const getCurrentListPageDataState: EventFiltersSelector = ( + state +) => state.listPage.data; + +/** + * Will return the API response with event filters. If the current state is attempting to load a new + * page of content, then return the previous API response if we have one + */ +export const getListApiSuccessResponse: EventFiltersSelector< + Immutable | undefined +> = createSelector(getCurrentListPageDataState, (listPageData) => { + if (isLoadedResourceState(listPageData)) { + return listPageData.data.content; + } else if ( + isLoadingResourceState(listPageData) && + isLoadedResourceState(listPageData.previousState) + ) { + return listPageData.previousState.data.content; + } + return undefined; +}); + +export const getListItems: EventFiltersSelector< + Immutable +> = createSelector(getListApiSuccessResponse, (apiResponseData) => { + return apiResponseData?.data || []; +}); + +/** + * Will return the query that was used with the currently displayed list of content. If a new page + * of content is being loaded, this selector will then attempt to use the previousState to return + * the query used. + */ +export const getCurrentListItemsQuery: EventFiltersSelector = createSelector( + getCurrentListPageDataState, + (pageDataState) => { + return ( + (isLoadedResourceState(pageDataState) && pageDataState.data.query) || + (isLoadingResourceState(pageDataState) && + isLoadedResourceState(pageDataState.previousState) && + pageDataState.previousState.data.query) || + {} + ); + } +); + +export const getListPagination: EventFiltersSelector = createSelector( + getListApiSuccessResponse, + // memoized via `reselect` until the API response changes + (response) => { + return { + totalItemCount: response?.total ?? 0, + pageSize: response?.per_page ?? MANAGEMENT_DEFAULT_PAGE_SIZE, + pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], + pageIndex: (response?.page ?? 1) - 1, + }; + } +); + +export const getListFetchError: EventFiltersSelector< + Immutable | undefined +> = createSelector(getCurrentListPageDataState, (listPageDataState) => { + return (isFailedResourceState(listPageDataState) && listPageDataState.error) || undefined; +}); + +export const getListIsLoading: EventFiltersSelector = createSelector( + getCurrentListPageDataState, + (listDataState) => isLoadingResourceState(listDataState) +); + +export const getListPageDataExistsState: EventFiltersSelector< + StoreState['listPage']['dataExist'] +> = ({ listPage: { dataExist } }) => dataExist; + +export const getListPageDoesDataExist: EventFiltersSelector = createSelector( + getListPageDataExistsState, + (dataExistsState) => { + if (isLoadedResourceState(dataExistsState)) { + return dataExistsState.data; + } + + // Until we know for sure that data exists (LoadedState), we assume `true` + return true; + } +); export const getFormEntry = ( state: EventFiltersListPageState @@ -38,5 +145,19 @@ export const getCreationError = (state: EventFiltersListPageState): ServerApiErr return isFailedResourceState(submissionResourceState) ? submissionResourceState.error : undefined; }; -export const getCurrentLocation = (state: EventFiltersListPageState): EventFiltersPageLocation => +export const getCurrentLocation: EventFiltersSelector = (state) => state.location; + +/** Compares the URL param values to the values used in the last data query */ +export const listDataNeedsRefresh: EventFiltersSelector = createSelector( + getCurrentLocation, + getCurrentListItemsQuery, + (state) => state.listPage.forceRefresh, + (location, currentQuery, forceRefresh) => { + return ( + forceRefresh || + location.page_index + 1 !== currentQuery.page || + location.page_size !== currentQuery.perPage + ); + } +); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selectors.test.ts index dbc962f1beaf1..a058c95388faa 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/store/selectors.test.ts @@ -6,15 +6,236 @@ */ import { initialEventFiltersPageState } from './builders'; -import { getFormEntry, getFormHasError, getCurrentLocation } from './selector'; +import { + getFormEntry, + getFormHasError, + getCurrentLocation, + getCurrentListPageState, + getListPageIsActive, + getCurrentListPageDataState, + getListApiSuccessResponse, + getListItems, + getCurrentListItemsQuery, + getListPagination, + getListFetchError, + getListIsLoading, + getListPageDoesDataExist, + listDataNeedsRefresh, +} from './selector'; import { ecsEventMock } from '../test_utils'; import { getInitialExceptionFromEvent } from './utils'; -import { EventFiltersPageLocation } from '../state'; +import { EventFiltersListPageState, EventFiltersPageLocation } from '../state'; import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from '../../../common/constants'; +import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; +import { + createFailedResourceState, + createLoadedResourceState, + createLoadingResourceState, + createUninitialisedResourceState, + getLastLoadedResourceState, +} from '../../../state'; -const initialState = initialEventFiltersPageState(); +describe('event filters selectors', () => { + let initialState: EventFiltersListPageState; + + // When `setToLoadingState()` is called, this variable will hold the prevousState in order to + // avoid ts-ignores due to know issues (#830) around the LoadingResourceState + let previousStateWhileLoading: EventFiltersListPageState['listPage']['data'] | undefined; + + const setToLoadedState = () => { + initialState.listPage.data = createLoadedResourceState({ + query: { page: 2, perPage: 10 }, + content: getFoundExceptionListItemSchemaMock(), + }); + }; + + const setToLoadingState = ( + previousState: EventFiltersListPageState['listPage']['data'] = createLoadedResourceState({ + query: { page: 5, perPage: 50 }, + content: getFoundExceptionListItemSchemaMock(), + }) + ) => { + previousStateWhileLoading = previousState; + + // will be fixed when AsyncResourceState is refactored (#830) + // @ts-ignore + initialState.listPage.data = createLoadingResourceState(previousState); + }; + + beforeEach(() => { + initialState = initialEventFiltersPageState(); + }); + + describe('getCurrentListPageState()', () => { + it('should retrieve list page state', () => { + expect(getCurrentListPageState(initialState)).toEqual(initialState.listPage); + }); + }); + + describe('getListPageIsActive()', () => { + it('should return active state', () => { + expect(getListPageIsActive(initialState)).toBe(false); + }); + }); + + describe('getCurrentListPageDataState()', () => { + it('should return list data state', () => { + expect(getCurrentListPageDataState(initialState)).toEqual(initialState.listPage.data); + }); + }); + + describe('getListApiSuccessResponse()', () => { + it('should return api response', () => { + setToLoadedState(); + expect(getListApiSuccessResponse(initialState)).toEqual( + getLastLoadedResourceState(initialState.listPage.data)?.data.content + ); + }); + + it('should return undefined if not available', () => { + setToLoadingState(createUninitialisedResourceState()); + expect(getListApiSuccessResponse(initialState)).toBeUndefined(); + }); + + it('should return previous success response if currently loading', () => { + setToLoadingState(); + expect(getListApiSuccessResponse(initialState)).toEqual( + getLastLoadedResourceState(previousStateWhileLoading!)?.data.content + ); + }); + }); + + describe('getListItems()', () => { + it('should return the list items from api response', () => { + setToLoadedState(); + expect(getListItems(initialState)).toEqual( + getLastLoadedResourceState(initialState.listPage.data)?.data.content.data + ); + }); + + it('should return empty array if no api response', () => { + expect(getListItems(initialState)).toEqual([]); + }); + }); + + describe('getCurrentListItemsQuery()', () => { + it('should return empty object if Uninitialized', () => { + expect(getCurrentListItemsQuery(initialState)).toEqual({}); + }); + + it('should return query from current loaded state', () => { + setToLoadedState(); + expect(getCurrentListItemsQuery(initialState)).toEqual({ page: 2, perPage: 10 }); + }); + + it('should return query from previous state while Loading new page', () => { + setToLoadingState(); + expect(getCurrentListItemsQuery(initialState)).toEqual({ page: 5, perPage: 50 }); + }); + }); + + describe('getListPagination()', () => { + it('should return pagination defaults if no API response is available', () => { + expect(getListPagination(initialState)).toEqual({ + totalItemCount: 0, + pageSize: 10, + pageSizeOptions: [10, 20, 50], + pageIndex: 0, + }); + }); + + it('should return pagination based on API response', () => { + setToLoadedState(); + expect(getListPagination(initialState)).toEqual({ + totalItemCount: 1, + pageSize: 1, + pageSizeOptions: [10, 20, 50], + pageIndex: 0, + }); + }); + }); + + describe('getListFetchError()', () => { + it('should return undefined if no error exists', () => { + expect(getListFetchError(initialState)).toBeUndefined(); + }); + + it('should return the API error', () => { + const error = { + statusCode: 500, + error: 'Internal Server Error', + message: 'Something is not right', + }; + + initialState.listPage.data = createFailedResourceState(error); + expect(getListFetchError(initialState)).toBe(error); + }); + }); + + describe('getListIsLoading()', () => { + it('should return false if not in a Loading state', () => { + expect(getListIsLoading(initialState)).toBe(false); + }); + + it('should return true if in a Loading state', () => { + setToLoadingState(); + expect(getListIsLoading(initialState)).toBe(true); + }); + }); + + describe('getListPageDoesDataExist()', () => { + it('should return true (default) until we get a Loaded Resource state', () => { + expect(getListPageDoesDataExist(initialState)).toBe(true); + + // Set DataExists to Loading + // ts-ignore will be fixed when AsyncResourceState is refactored (#830) + // @ts-ignore + initialState.listPage.dataExist = createLoadingResourceState(initialState.listPage.dataExist); + expect(getListPageDoesDataExist(initialState)).toBe(true); + + // Set DataExists to Failure + initialState.listPage.dataExist = createFailedResourceState({ + statusCode: 500, + error: 'Internal Server Error', + message: 'Something is not right', + }); + expect(getListPageDoesDataExist(initialState)).toBe(true); + }); + + it('should return false if no data exists', () => { + initialState.listPage.dataExist = createLoadedResourceState(false); + expect(getListPageDoesDataExist(initialState)).toBe(false); + }); + }); + + describe('listDataNeedsRefresh()', () => { + beforeEach(() => { + setToLoadedState(); + + initialState.location = { + page_index: 1, + page_size: 10, + filter: '', + id: '', + show: undefined, + }; + }); + + it('should return false if location url params match those that were used in api call', () => { + expect(listDataNeedsRefresh(initialState)).toBe(false); + }); + + it('should return true if `forceRefresh` is set', () => { + initialState.listPage.forceRefresh = true; + expect(listDataNeedsRefresh(initialState)).toBe(true); + }); + + it('should should return true if any of the url params differ from last api call', () => { + initialState.location.page_index = 10; + expect(listDataNeedsRefresh(initialState)).toBe(true); + }); + }); -describe('selectors', () => { describe('getFormEntry()', () => { it('returns undefined when there is no entry', () => { expect(getFormEntry(initialState)).toBe(undefined); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts index 0aceca88efbc7..cad24b9f6ccf6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/types.ts @@ -5,7 +5,29 @@ * 2.0. */ +import { Immutable } from '../../../../common/endpoint/types'; +import { + CreateExceptionListItemSchema, + ExceptionListItemSchema, +} from '../../../../../lists/common'; +import { FoundExceptionListItemSchema } from '../../../../../lists/common/schemas'; + export interface EventFiltersListPageUrlSearchParams { page_index: number; page_size: number; } + +export type EventFiltersServiceGetListOptions = Partial<{ + page: number; + perPage: number; + sortField: keyof ExceptionListItemSchema; + sortOrder: 'asc' | 'desc'; +}>; + +export interface EventFiltersService { + addEventFilters( + exception: Immutable + ): Promise; + + getList(options?: EventFiltersServiceGetListOptions): Promise; +} diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/empty/index.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/empty/index.tsx index 5298578c38e17..81a119af34e5b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/empty/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/empty/index.tsx @@ -47,7 +47,7 @@ export const EventFiltersListEmptyState = memo<{ data-test-subj="eventFiltersListAddButton" > diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index ac38b57fdb635..d32a4cbe1ca24 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -8,14 +8,51 @@ import React, { memo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { AdministrationListPage } from '../../../components/administration_list_page'; +import { EuiButton } from '@elastic/eui'; +import styled from 'styled-components'; +import { AdministrationListPage as _AdministrationListPage } from '../../../components/administration_list_page'; import { EventFiltersListEmptyState } from './components/empty'; import { useEventFiltersNavigateCallback, useEventFiltersSelector } from './hooks'; -import { getCurrentLocation } from '../store/selector'; import { EventFiltersFlyout } from './components/flyout'; +import { + getListFetchError, + getListIsLoading, + getListItems, + getListPagination, + getCurrentLocation, + getListPageDoesDataExist, +} from '../store/selector'; +import { PaginatedContent, PaginatedContentProps } from '../../../components/paginated_content'; +import { ExceptionListItemSchema } from '../../../../../../lists/common'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { + ExceptionItem, + ExceptionItemProps, +} from '../../../../common/components/exceptions/viewer/exception_item'; + +type EventListPaginatedContent = PaginatedContentProps< + Immutable, + typeof ExceptionItem +>; + +const AdministrationListPage = styled(_AdministrationListPage)` + .event-filter-container > * { + margin-bottom: ${({ theme }) => theme.eui.spacerSizes.l}; + + &:last-child { + margin-bottom: 0; + } + } +`; export const EventFiltersListPage = memo(() => { + const listItems = useEventFiltersSelector(getListItems); + const pagination = useEventFiltersSelector(getListPagination); + const isLoading = useEventFiltersSelector(getListIsLoading); + const fetchError = useEventFiltersSelector(getListFetchError); const location = useEventFiltersSelector(getCurrentLocation); + const doesDataExist = useEventFiltersSelector(getListPageDoesDataExist); + const navigateCallback = useEventFiltersNavigateCallback(); const showFlyout = !!location.show; @@ -28,7 +65,7 @@ export const EventFiltersListPage = memo(() => { [navigateCallback] ); - const handleCancelButtonClick = useCallback( + const handleAddCancelButtonClick = useCallback( () => navigateCallback({ show: undefined, @@ -36,6 +73,38 @@ export const EventFiltersListPage = memo(() => { }), [navigateCallback] ); + + const handleItemEdit: ExceptionItemProps['onEditException'] = useCallback((item) => { + // TODO: implement edit item + }, []); + + const handleItemDelete: ExceptionItemProps['onDeleteException'] = useCallback((args) => { + // TODO: implement delete item + }, []); + + const handleItemComponentProps: EventListPaginatedContent['itemComponentProps'] = useCallback( + (exceptionItem) => ({ + exceptionItem: exceptionItem as ExceptionListItemSchema, + loadingItemIds: [], + commentsAccordionId: '', + onEditException: handleItemEdit, + onDeleteException: handleItemDelete, + showModified: true, + showName: true, + }), + [handleItemDelete, handleItemEdit] + ); + + const handlePaginatedContentChange: EventListPaginatedContent['onChange'] = useCallback( + ({ pageIndex, pageSize }) => { + navigateCallback({ + page_index: pageIndex, + page_size: pageSize, + }); + }, + [navigateCallback] + ); + return ( { /> } subtitle={i18n.translate('xpack.securitySolution.eventFilters.aboutInfo', { - defaultMessage: 'Something here about Event Filtering....', + defaultMessage: + 'Add an event filter to exclude high volume or unwanted events from being written to Elasticsearch. Event ' + + 'filters are processed by the Endpoint Security integration, and are applied to hosts running this integration on their agents.', })} + actions={ + doesDataExist && ( + + + + ) + } > - {/* */} - {/* TODO: Display this only when list is empty (there are no endpoint events) */} - - {showFlyout ? : null} + {showFlyout && } + + , typeof ExceptionItem> + items={listItems} + ItemComponent={ExceptionItem} + itemComponentProps={handleItemComponentProps} + onChange={handlePaginatedContentChange} + error={fetchError?.message} + loading={isLoading} + pagination={pagination} + contentClassName="event-filter-container" + noItemsMessage={ + !doesDataExist && ( + + ) + } + /> ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/hooks.ts index df87d150891c1..bf2e187c127c2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/hooks.ts @@ -20,7 +20,7 @@ import { useToasts } from '../../../../common/lib/kibana'; import { getCreationSuccessMessage, getCreationErrorMessage } from './translations'; import { State } from '../../../../common/store'; -import { EventFiltersListPageState } from '../state'; +import { EventFiltersListPageState, EventFiltersPageLocation } from '../state'; import { getEventFiltersListPath } from '../../../common/routing'; import { @@ -54,8 +54,9 @@ export function useEventFiltersNavigateCallback() { const location = useEventFiltersSelector(getCurrentLocation); const history = useHistory(); - return useCallback((args) => history.push(getEventFiltersListPath({ ...location, ...args })), [ - history, - location, - ]); + return useCallback( + (args: Partial) => + history.push(getEventFiltersListPath({ ...location, ...args })), + [history, location] + ); } diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index 9e1a90a031d99..4be75117daeda 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -5,12 +5,9 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; import React, { memo } from 'react'; -import { useHistory, Route, Switch } from 'react-router-dom'; - -import { ChromeBreadcrumb } from 'kibana/public'; -import { EuiText, EuiEmptyPrompt } from '@elastic/eui'; +import { Route, Switch, useHistory } from 'react-router-dom'; +import { EuiEmptyPrompt, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, @@ -24,48 +21,12 @@ import { EndpointsContainer } from './endpoint_hosts'; import { PolicyContainer } from './policy'; import { TrustedAppsContainer } from './trusted_apps'; import { getEndpointListPath } from '../common/routing'; -import { APP_ID, SecurityPageName } from '../../../common/constants'; -import { GetUrlForApp } from '../../common/components/navigation/types'; -import { AdministrationRouteSpyState } from '../../common/utils/route/types'; -import { ADMINISTRATION } from '../../app/home/translations'; -import { AdministrationSubTab } from '../types'; -import { - ENDPOINTS_TAB, - EVENT_FILTERS_TAB, - POLICIES_TAB, - TRUSTED_APPS_TAB, -} from '../common/translations'; +import { SecurityPageName } from '../../../common/constants'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useIngestEnabledCheck } from '../../common/hooks/endpoint/ingest_enabled'; import { EventFiltersContainer } from './event_filters'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; -const TabNameMappedToI18nKey: Record = { - [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, - [AdministrationSubTab.policies]: POLICIES_TAB, - [AdministrationSubTab.trustedApps]: TRUSTED_APPS_TAB, - [AdministrationSubTab.eventFilters]: EVENT_FILTERS_TAB, -}; - -export function getBreadcrumbs( - params: AdministrationRouteSpyState, - search: string[], - getUrlForApp: GetUrlForApp -): ChromeBreadcrumb[] { - return [ - { - text: ADMINISTRATION, - href: getUrlForApp(`${APP_ID}:${SecurityPageName.administration}`, { - path: !isEmpty(search[0]) ? search[0] : '', - }), - }, - ...(params?.tabName ? [params?.tabName] : []).map((tabName) => ({ - text: TabNameMappedToI18nKey[tabName], - href: '', - })), - ]; -} - const NoPermissions = memo(() => { return ( <> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/agents_summary.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/agents_summary.tsx index 37b4f963bf920..512b3d494faf6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/agents_summary.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/agents_summary.tsx @@ -35,7 +35,7 @@ export const AgentsSummary = memo((props) => { title: i18n.translate( 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.totalTitle', { - defaultMessage: 'Endpoints', + defaultMessage: 'Agents', } ), health: '', @@ -45,30 +45,30 @@ export const AgentsSummary = memo((props) => { title: i18n.translate( 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.onlineTitle', { - defaultMessage: 'Online', + defaultMessage: 'Healthy', } ), health: 'success', }, { - key: 'offline', + key: 'error', title: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle', + 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle', { - defaultMessage: 'Offline', + defaultMessage: 'Unhealthy', } ), health: 'warning', }, { - key: 'error', + key: 'offline', title: i18n.translate( - 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle', + 'xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle', { - defaultMessage: 'Error', + defaultMessage: 'Offline', } ), - health: 'danger', + health: 'subdued', }, ]; }, []); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index ff5f410611099..1407e5f64f156 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -161,7 +161,7 @@ describe('Policy Details', () => { const agentsSummary = policyView.find('EuiFlexGroup[data-test-subj="policyAgentsSummary"]'); expect(agentsSummary).toHaveLength(1); - expect(agentsSummary.text()).toBe('Endpoints5Online3Offline1Error1'); + expect(agentsSummary.text()).toBe('Agents5Healthy3Unhealthy1Offline1'); }); it('should display cancel button', async () => { await asyncActions; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts index 14a5b3e809a53..0331d1cec3a7f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts @@ -19,7 +19,12 @@ describe('utils', () => { }); it('should parse complex query with term', () => { expect(parseQueryFilterToKQL('complex query')).toBe( - 'exception-list-agnostic.attributes.name:*complex* *query* OR exception-list-agnostic.attributes.description:*complex* *query* OR exception-list-agnostic.attributes.entries.value:*complex* *query* OR exception-list-agnostic.attributes.entries.entries.value:*complex* *query*' + 'exception-list-agnostic.attributes.name:*complex*query* OR exception-list-agnostic.attributes.description:*complex*query* OR exception-list-agnostic.attributes.entries.value:*complex*query* OR exception-list-agnostic.attributes.entries.entries.value:*complex*query*' + ); + }); + it('should parse complex query with special chars term', () => { + expect(parseQueryFilterToKQL('C:\\tmpes')).toBe( + 'exception-list-agnostic.attributes.name:*C\\:\\\\tmpes* OR exception-list-agnostic.attributes.description:*C\\:\\\\tmpes* OR exception-list-agnostic.attributes.entries.value:*C\\:\\\\tmpes* OR exception-list-agnostic.attributes.entries.entries.value:*C\\:\\\\tmpes*' ); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts index a2f19a6003056..aacea33087169 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts @@ -10,7 +10,10 @@ export const parseQueryFilterToKQL = (filter: string): string => { const kuery = [`name`, `description`, `entries.value`, `entries.entries.value`] .map( (field) => - `exception-list-agnostic.attributes.${field}:*${filter.trim().replace(/\s/gm, '* *')}*` + `exception-list-agnostic.attributes.${field}:*${filter + .trim() + .replace(/([^a-zA-Z0-9\s/])/gm, '\\$&') + .replace(/\s/gm, '*')}*` ) .join(' OR '); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap index c4a58a3b99d3f..31ce0bc208827 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap @@ -3,6 +3,7 @@ exports[`TrustedAppsGrid renders correctly initially 1`] = ` .c1 { position: relative; + padding-top: 4px; } .c1 .body { @@ -27,9 +28,6 @@ exports[`TrustedAppsGrid renders correctly initially 1`] = `
-
@@ -49,108 +47,6 @@ exports[`TrustedAppsGrid renders correctly initially 1`] = `
-
-
-
-
-
-
- -
-
-
-
- -
-
-