diff --git a/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts b/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts index 7f4c572d47531..891b8b7727f63 100644 --- a/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts +++ b/src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts @@ -618,6 +618,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D apmRulesTransactionDuration: `${ELASTIC_DOCS}solutions/observability/incident-management/create-latency-threshold-rule`, apmRulesTransactionError: `${ELASTIC_DOCS}solutions/observability/incident-management/create-failed-transaction-rate-threshold-rule`, apmRulesAnomaly: `${ELASTIC_DOCS}solutions/observability/incident-management/create-an-apm-anomaly-rule`, + authorization: `${KIBANA_DOCS}alerting-setup.html#alerting-authorization`, emailAction: `${ELASTIC_DOCS}reference/kibana/connectors-kibana/email-action-type`, emailActionConfig: `${ELASTIC_DOCS}reference/kibana/connectors-kibana/email-action-type`, emailExchangeClientSecretConfig: `${ELASTIC_DOCS}reference/kibana/connectors-kibana/email-action-type#exchange-client-secret`, diff --git a/src/platform/packages/shared/kbn-doc-links/src/types.ts b/src/platform/packages/shared/kbn-doc-links/src/types.ts index e9ecda2727578..4f3c60132776a 100644 --- a/src/platform/packages/shared/kbn-doc-links/src/types.ts +++ b/src/platform/packages/shared/kbn-doc-links/src/types.ts @@ -405,6 +405,7 @@ export interface DocLinks { aiAssistant: string; }>; readonly alerting: Readonly<{ + authorization: string; guide: string; actionTypes: string; apmRulesErrorCount: string; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 83b730f717bcf..38eb4fe35c98c 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -98,6 +98,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ({ lists, actions, + docLinks, logger, config, publicBaseUrl, @@ -306,6 +307,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = privileges, ruleExecutionLogger, uiSettingsClient, + docLinks, }); if (readIndexWarningMessage != null) { @@ -537,7 +539,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = // as the current status of the rule. await ruleExecutionLogger.logStatusChange({ newStatus: RuleExecutionStatusEnum['partial failure'], - message: truncateList(result.warningMessages.concat(wrapperWarnings)).join(', '), + message: truncateList(result.warningMessages.concat(wrapperWarnings)).join('\n\n'), metrics: { searchDurations: result.searchAfterTimes, indexingDurations: result.bulkCreateTimes, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 2d7d67b843e78..fa99a7a975ed7 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -18,6 +18,7 @@ import { sampleDocNoSortId } from '../__mocks__/es_results'; import { getQueryRuleParams } from '../../rule_schema/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { QUERY_RULE_TYPE_ID } from '@kbn/securitysolution-rules'; +import { docLinksServiceMock } from '@kbn/core/server/mocks'; import { hasTimestampFields } from '../utils/utils'; import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine'; @@ -44,6 +45,7 @@ jest.mock('../utils/get_list_client', () => ({ describe('Custom Query Alerts', () => { const mocks = createRuleTypeMocks(); const licensing = licensingMock.createSetup(); + const docLinks = docLinksServiceMock.createSetupContract(); const publicBaseUrl = 'http://somekibanabaseurl.com'; const mockedStatusLogger = ruleExecutionLogMock.forExecutors.create(); const ruleStatusLogger = () => Promise.resolve(mockedStatusLogger); @@ -52,6 +54,7 @@ describe('Custom Query Alerts', () => { const { actions, alerting, lists, logger, ruleDataClient } = dependencies; const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ actions, + docLinks, lists, logger, config: createMockConfig(), @@ -282,7 +285,9 @@ describe('Custom Query Alerts', () => { expect.objectContaining({ newStatus: RuleExecutionStatusEnum['partial failure'], message: - "Check privileges failed to execute Error: hastTimestampFields test error, The rule's max alerts per run setting (10000) is greater than the Kibana alerting limit (1000). The rule will only write a maximum of 1000 alerts per rule run.", + 'Check privileges failed to execute Error: hastTimestampFields test error\n' + + '\n' + + "The rule's max alerts per run setting (10000) is greater than the Kibana alerting limit (1000). The rule will only write a maximum of 1000 alerts per rule run.", }) ); }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index d789dba3c24d7..3461d04411db2 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -35,6 +35,7 @@ import type { TypeOfFieldMap } from '@kbn/rule-registry-plugin/common/field_map' import type { Filter } from '@kbn/es-query'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; +import type { DocLinksServiceSetup } from '@kbn/core/server'; import type { RulePreviewLoggedRequest } from '../../../../common/api/detection_engine/rule_preview/rule_preview.gen'; import type { RuleResponseAction } from '../../../../common/api/detection_engine/model/rule_response_actions'; import type { ConfigType } from '../../../config'; @@ -139,6 +140,7 @@ export type SecurityAlertType< export interface CreateSecurityRuleTypeWrapperProps { lists: SetupPlugins['lists']; actions: SetupPlugins['actions']; + docLinks: DocLinksServiceSetup; logger: Logger; config: ConfigType; publicBaseUrl: string | undefined; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts index bef83d09405fb..6a7656478198e 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts @@ -33,6 +33,7 @@ import type { } from '@kbn/securitysolution-io-ts-list-types'; import type { + DocLinksServiceSetup, ElasticsearchClient, IUiSettingsClient, SavedObjectsClientContract, @@ -105,35 +106,32 @@ export const hasReadIndexPrivileges = async (args: { privileges: Privilege; ruleExecutionLogger: IRuleExecutionLogForExecutors; uiSettingsClient: IUiSettingsClient; + docLinks: DocLinksServiceSetup; }): Promise => { - const { privileges, ruleExecutionLogger, uiSettingsClient } = args; - + const { privileges, ruleExecutionLogger, uiSettingsClient, docLinks } = args; + const apiKeyDocs = docLinks.links.alerting.authorization; const isCcsPermissionWarningEnabled = await uiSettingsClient.get(ENABLE_CCS_READ_WARNING_SETTING); - const indexNames = Object.keys(privileges.index); const filteredIndexNames = isCcsPermissionWarningEnabled ? indexNames : indexNames.filter((indexName) => { return !isCCSRemoteIndexName(indexName); }); - const [, indexesWithNoReadPrivileges] = partition( filteredIndexNames, (indexName) => privileges.index[indexName].read ); - let warningStatusMessage; // Some indices have read privileges others do not. if (indexesWithNoReadPrivileges.length > 0) { const indexesString = JSON.stringify(indexesWithNoReadPrivileges); - warningStatusMessage = `This rule may not have the required read privileges to the following index patterns: ${indexesString}`; + warningStatusMessage = `This rule's API key is unable to access all indices that match the ${indexesString} pattern. To learn how to update and manage API keys, refer to ${apiKeyDocs}.`; await ruleExecutionLogger.logStatusChange({ newStatus: RuleExecutionStatusEnum['partial failure'], message: warningStatusMessage, }); } - return warningStatusMessage; }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/plugin.ts b/x-pack/solutions/security/plugins/security_solution/server/plugin.ts index f56aecbb17621..df943986a0d38 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/plugin.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/plugin.ts @@ -359,6 +359,7 @@ export class Plugin implements ISecuritySolutionPlugin { const securityRuleTypeOptions = { lists: plugins.lists, + docLinks: core.docLinks, actions: plugins.actions, logger: this.logger, config: this.config, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/check_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/check_privileges.ts index 251fc7c74d71f..d7d7119670edf 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/check_privileges.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/check_privileges.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext) => { // TODO: https://github.com/elastic/kibana/pull/121644 clean up, make type-safe expect(body?.execution_summary?.last_execution.message).to.contain( - `This rule may not have the required read privileges to the following index patterns: ["${index[0]}"]` + `This rule's API key is unable to access all indices that match the ["${index[0]}"] pattern. To learn how to update and manage API keys, refer to https://www.elastic.co/guide/en/kibana/9.0/alerting-setup.html#alerting-authorization.` ); await deleteUserAndRole(getService, ROLES.detections_admin); @@ -166,7 +166,7 @@ export default ({ getService }: FtrProviderContext) => { // TODO: https://github.com/elastic/kibana/pull/121644 clean up, make type-safe expect(body?.execution_summary?.last_execution.message).to.eql( - `This rule may not have the required read privileges to the following index patterns: ["${index[0]}"]` + `This rule's API key is unable to access all indices that match the ["${index[0]}"] pattern. To learn how to update and manage API keys, refer to https://www.elastic.co/guide/en/kibana/9.0/alerting-setup.html#alerting-authorization.` ); await deleteUserAndRole(getService, ROLES.detections_admin);