[Security Solution] [Detections] Clarify result of missing read-index privileges on index patterns for a given rule (#215941)

## Summary

Ref: https://github.com/elastic/kibana/issues/193204

Updates the text shown when a rule may not have read privileges to a
given index pattern.
This commit is contained in:
Devin W. Hurley 2025-04-18 14:14:45 -04:00 committed by GitHub
parent 36c495c9f0
commit 6e4a06ee0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 25 additions and 12 deletions

View file

@ -622,6 +622,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`,

View file

@ -408,6 +408,7 @@ export interface DocLinks {
aiAssistant: string;
}>;
readonly alerting: Readonly<{
authorization: string;
guide: string;
actionTypes: string;
apmRulesErrorCount: string;

View file

@ -97,6 +97,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) {
@ -520,7 +522,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,

View file

@ -16,6 +16,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';
@ -42,6 +43,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);
@ -53,6 +55,7 @@ describe('Custom Query Alerts', () => {
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
actions,
docLinks,
lists,
logger,
config: createMockConfig(),
@ -250,7 +253,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.",
})
);
});

View file

@ -31,6 +31,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';
@ -142,6 +143,7 @@ export type SecurityAlertType<TParams extends RuleParams, TState extends RuleTyp
export interface CreateSecurityRuleTypeWrapperProps {
lists: SetupPlugins['lists'];
actions: SetupPlugins['actions'];
docLinks: DocLinksServiceSetup;
logger: Logger;
config: ConfigType;
publicBaseUrl: string | undefined;

View file

@ -31,7 +31,11 @@ import type {
FoundExceptionListItemSchema,
} from '@kbn/securitysolution-io-ts-list-types';
import type { ElasticsearchClient, IUiSettingsClient } from '@kbn/core/server';
import type {
DocLinksServiceSetup,
ElasticsearchClient,
IUiSettingsClient,
} from '@kbn/core/server';
import type { AlertingServerSetup } from '@kbn/alerting-plugin/server';
import { parseDuration } from '@kbn/alerting-plugin/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
@ -91,35 +95,32 @@ export const hasReadIndexPrivileges = async (args: {
privileges: Privilege;
ruleExecutionLogger: IRuleExecutionLogForExecutors;
uiSettingsClient: IUiSettingsClient;
docLinks: DocLinksServiceSetup;
}): Promise<string | undefined> => {
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;
};

View file

@ -356,6 +356,7 @@ export class Plugin implements ISecuritySolutionPlugin {
const securityRuleTypeOptions = {
lists: plugins.lists,
docLinks: core.docLinks,
actions: plugins.actions,
logger: this.logger,
config: this.config,

View file

@ -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/current/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/current/alerting-setup.html#alerting-authorization.`
);
await deleteUserAndRole(getService, ROLES.detections_admin);