From 5170468009ca46576aefda362247af94d4072d88 Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Mon, 24 May 2021 11:59:51 -0400 Subject: [PATCH] WIP - re-add update function to alerts as data client (#13) * WIP - creating alerting authorization client factory and exposing authorization client on plugin start contract * Updating alerting feature privilege builder to handle different alerting types * Converting to es query if requested * first pass at getting everything working again * working update function * fix rebase conflicts * fixes index mapping to add owner field * fixes update route, previously using wrong ruleTypeId and not building the correct index * fix getIndex function Co-authored-by: Ying Mao --- .../src/technical_field_names.ts | 3 + .../authorization/alerting_authorization.ts | 17 +- .../alerts_authorization_kuery.test.ts | 490 ++++++++++++++++++ .../alerts_authorization_kuery.ts | 52 ++ x-pack/plugins/apm/common/alert_types.ts | 10 +- .../alerting/alerting_flyout/index.tsx | 7 +- x-pack/plugins/apm/server/feature.ts | 20 +- x-pack/plugins/apm/server/index.ts | 1 + .../alerts/register_error_count_alert_type.ts | 8 +- ...egister_transaction_duration_alert_type.ts | 8 +- ...ister_transaction_error_rate_alert_type.ts | 8 +- x-pack/plugins/monitoring/server/plugin.ts | 13 + x-pack/plugins/monitoring/server/types.ts | 2 + .../field_maps/technical_rule_field_map.ts | 2 + .../server/alert_data_client/alert_client.ts | 42 +- x-pack/plugins/rule_registry/server/index.ts | 1 + x-pack/plugins/rule_registry/server/plugin.ts | 38 ++ .../rule_registry/server/scripts/README.md | 24 + .../server/scripts/get_observability_alert.sh | 21 + .../scripts/get_security_solution_alert.sh | 22 + .../server/scripts/hunter/README.md | 5 + .../scripts/hunter/delete_detections_user.sh | 11 + .../scripts/hunter/detections_role.json | 38 ++ .../scripts/hunter/detections_user.json | 6 + .../scripts/hunter/get_detections_role.sh | 11 + .../server/scripts/hunter/index.ts | 10 + .../scripts/hunter/post_detections_role.sh | 14 + .../scripts/hunter/post_detections_user.sh | 14 + .../server/scripts/observer copy/README.md | 5 + .../observer copy/delete_detections_user.sh | 11 + .../observer copy/detections_role.json | 38 ++ .../observer copy/detections_user.json | 6 + .../observer copy/get_detections_role.sh | 11 + .../server/scripts/observer copy/index.ts | 10 + .../observer copy/post_detections_role.sh | 14 + .../observer copy/post_detections_user.sh | 14 + .../server/scripts/observer/README.md | 5 + .../observer/delete_detections_user.sh | 11 + .../scripts/observer/detections_role.json | 39 ++ .../scripts/observer/detections_user.json | 6 + .../scripts/observer/get_detections_role.sh | 11 + .../observer/get_observability_alert.sh | 21 + .../observer/get_security_solution_alert.sh | 22 + .../server/scripts/observer/index.ts | 10 + .../scripts/observer/post_detections_role.sh | 14 + .../scripts/observer/post_detections_user.sh | 14 + .../scripts/update_observability_alert.sh | 19 + .../create_lifecycle_rule_type_factory.ts | 15 + .../server/utils/get_rule_executor_data.ts | 3 + .../feature_privilege_builder/alerting.ts | 2 +- .../routes/index/signals_mapping.json | 3 + .../signals/build_bulk_body.ts | 2 +- .../detection_engine/signals/build_signal.ts | 3 +- .../signals/signal_rule_alert_type.ts | 3 + .../lib/detection_engine/signals/types.ts | 1 + .../security_solution/server/plugin.ts | 34 ++ .../plugins/security_solution/server/types.ts | 2 + 57 files changed, 1197 insertions(+), 50 deletions(-) create mode 100644 x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.test.ts create mode 100644 x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.ts create mode 100644 x-pack/plugins/rule_registry/server/scripts/README.md create mode 100755 x-pack/plugins/rule_registry/server/scripts/get_observability_alert.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/get_security_solution_alert.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/hunter/README.md create mode 100755 x-pack/plugins/rule_registry/server/scripts/hunter/delete_detections_user.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/hunter/detections_role.json create mode 100644 x-pack/plugins/rule_registry/server/scripts/hunter/detections_user.json create mode 100755 x-pack/plugins/rule_registry/server/scripts/hunter/get_detections_role.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/hunter/index.ts create mode 100755 x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_role.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_user.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer copy/README.md create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer copy/delete_detections_user.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer copy/detections_role.json create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer copy/detections_user.json create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer copy/get_detections_role.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer copy/index.ts create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_role.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_user.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer/README.md create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/delete_detections_user.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer/detections_role.json create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer/detections_user.json create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/get_detections_role.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/get_observability_alert.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/get_security_solution_alert.sh create mode 100644 x-pack/plugins/rule_registry/server/scripts/observer/index.ts create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/post_detections_role.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/observer/post_detections_user.sh create mode 100755 x-pack/plugins/rule_registry/server/scripts/update_observability_alert.sh diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index 31779c9f08e81..6c45403fc0a13 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -19,6 +19,7 @@ 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 OWNER = `${ALERT_NAMESPACE}.owner` 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; @@ -40,6 +41,7 @@ const fields = { RULE_CATEGORY, TAGS, PRODUCER, + OWNER, ALERT_ID, ALERT_UUID, ALERT_START, @@ -62,6 +64,7 @@ export { RULE_CATEGORY, TAGS, PRODUCER, + OWNER, ALERT_ID, ALERT_UUID, ALERT_START, diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts index 5aedb3f2c1284..fe7a8148040e7 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts @@ -6,10 +6,10 @@ */ import Boom from '@hapi/boom'; -import { map, mapValues, fromPairs, has } from 'lodash'; +import { map, mapValues, fromPairs, has, get } from 'lodash'; import { KibanaRequest } from 'src/core/server'; import { AlertTypeRegistry } from '../types'; -import { SecurityPluginSetup } from '../../../security/server'; +import { SecurityPluginSetup, AlertingActions } from '../../../security/server'; import { RegistryAlertType } from '../alert_type_registry'; import { PluginStartContract as FeaturesPluginStart } from '../../../features/server'; import { AlertingAuthorizationAuditLogger, ScopeType } from './audit_logger'; @@ -145,12 +145,12 @@ export class AlertingAuthorization { return this.authorization?.mode?.useRbacForRequest(this.request) ?? false; } - public getAuthorizedAlertsIndices(owners: string[]): string { - return owners - .map((owner) => { - return `.alerts-${owner}*`; - }) - .join(','); + public getAuthorizedAlertsIndices(owner: string): string | undefined { + return owner === 'apm' + ? '.alerts-observability-apm' + : owner === 'securitySolution' + ? '.siem-signals*' + : undefined; } public async ensureAuthorized({ ruleTypeId, consumer, operation, entity }: EnsureAuthorizedOpts) { @@ -197,6 +197,7 @@ export class AlertingAuthorization { * as Privileged. * This check will ensure we don't accidentally let these through */ + // This should also log the type they're trying to access rule/alert throw Boom.forbidden( this.auditLogger.logAuthorizationFailure( username, diff --git a/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.test.ts b/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.test.ts new file mode 100644 index 0000000000000..1b4ddffc2eef2 --- /dev/null +++ b/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.test.ts @@ -0,0 +1,490 @@ +/* + * Copyright 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 { RecoveredActionGroup } from '../../common'; +import { + asKqlFiltersByRuleTypeAndConsumer, + asEsDslFiltersByRuleTypeAndConsumer, + ensureFieldIsSafeForQuery, +} from './alerts_authorization_kuery'; +import { esKuery } from '../../../../../src/plugins/data/server'; + +describe('asKqlFiltersByRuleTypeAndConsumer', () => { + test('constructs KQL filter for single rule type with single authorized consumer', async () => { + expect( + asKqlFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + minimumLicenseRequired: 'basic', + authorizedConsumers: { + myApp: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual( + esKuery.fromKueryExpression(`((path.to.rule.id:myAppAlertType and consumer-field:(myApp)))`) + ); + }); + + test('constructs KQL filter for single rule type with multiple authorized consumers', async () => { + expect( + asKqlFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual( + esKuery.fromKueryExpression( + `((path.to.rule.id:myAppAlertType and consumer-field:(alerts or myApp or myOtherApp)))` + ) + ); + }); + + test('constructs KQL filter for multiple rule types across authorized consumer', async () => { + expect( + asKqlFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myOtherAppAlertType', + name: 'myOtherAppAlertType', + producer: 'alerts', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'mySecondAppAlertType', + name: 'mySecondAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual( + esKuery.fromKueryExpression( + `((path.to.rule.id:myAppAlertType and consumer-field:(alerts or myApp or myOtherApp or myAppWithSubFeature)) or (path.to.rule.id:myOtherAppAlertType and consumer-field:(alerts or myApp or myOtherApp or myAppWithSubFeature)) or (path.to.rule.id:mySecondAppAlertType and consumer-field:(alerts or myApp or myOtherApp or myAppWithSubFeature)))` + ) + ); + }); +}); + +describe('asEsDslFiltersByRuleTypeAndConsumer', () => { + test('constructs ES DSL filter for single rule type with single authorized consumer', async () => { + expect( + asEsDslFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + minimumLicenseRequired: 'basic', + authorizedConsumers: { + myApp: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual({ + bool: { + filter: [ + { + bool: { + should: [ + { + match: { + 'path.to.rule.id': 'myAppAlertType', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + match: { + 'consumer-field': 'myApp', + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }); + }); + + test('constructs ES DSL filter for single rule type with multiple authorized consumers', async () => { + expect( + asEsDslFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual({ + bool: { + filter: [ + { + bool: { + should: [{ match: { 'path.to.rule.id': 'myAppAlertType' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + bool: { + should: [{ match: { 'consumer-field': 'alerts' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myOtherApp' } }], + minimum_should_match: 1, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }); + }); + + test('constructs ES DSL filter for multiple rule types across authorized consumer', async () => { + expect( + asEsDslFiltersByRuleTypeAndConsumer( + new Set([ + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'myOtherAppAlertType', + name: 'myOtherAppAlertType', + producer: 'alerts', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + id: 'mySecondAppAlertType', + name: 'mySecondAppAlertType', + producer: 'myApp', + authorizedConsumers: { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + myAppWithSubFeature: { read: true, all: true }, + }, + enabledInLicense: true, + }, + ]), + { + ruleTypeId: 'path.to.rule.id', + consumer: 'consumer-field', + } + ) + ).toEqual({ + bool: { + should: [ + { + bool: { + filter: [ + { + bool: { + should: [{ match: { 'path.to.rule.id': 'myAppAlertType' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + bool: { + should: [{ match: { 'consumer-field': 'alerts' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myOtherApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myAppWithSubFeature' } }], + minimum_should_match: 1, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + { + bool: { + filter: [ + { + bool: { + should: [{ match: { 'path.to.rule.id': 'myOtherAppAlertType' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + bool: { + should: [{ match: { 'consumer-field': 'alerts' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myOtherApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myAppWithSubFeature' } }], + minimum_should_match: 1, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + { + bool: { + filter: [ + { + bool: { + should: [{ match: { 'path.to.rule.id': 'mySecondAppAlertType' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + bool: { + should: [{ match: { 'consumer-field': 'alerts' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myOtherApp' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match: { 'consumer-field': 'myAppWithSubFeature' } }], + minimum_should_match: 1, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + ], + minimum_should_match: 1, + }, + }); + }); +}); + +describe('ensureFieldIsSafeForQuery', () => { + test('throws if field contains character that isnt safe in a KQL query', () => { + expect(() => ensureFieldIsSafeForQuery('id', 'alert-*')).toThrowError( + `expected id not to include invalid character: *` + ); + + expect(() => ensureFieldIsSafeForQuery('id', '<=""')).toThrowError( + `expected id not to include invalid character: <=` + ); + + expect(() => ensureFieldIsSafeForQuery('id', '>=""')).toThrowError( + `expected id not to include invalid character: >=` + ); + + expect(() => ensureFieldIsSafeForQuery('id', '1 or alertid:123')).toThrowError( + `expected id not to include whitespace and invalid character: :` + ); + + expect(() => ensureFieldIsSafeForQuery('id', ') or alertid:123')).toThrowError( + `expected id not to include whitespace and invalid characters: ), :` + ); + + expect(() => ensureFieldIsSafeForQuery('id', 'some space')).toThrowError( + `expected id not to include whitespace` + ); + }); + + test('doesnt throws if field is safe as part of a KQL query', () => { + expect(() => ensureFieldIsSafeForQuery('id', '123-0456-678')).not.toThrow(); + }); +}); diff --git a/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.ts b/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.ts new file mode 100644 index 0000000000000..2f153b682a181 --- /dev/null +++ b/x-pack/plugins/alerting/server/authorization/alerts_authorization_kuery.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 { remove } from 'lodash'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; +import { nodeBuilder, EsQueryConfig } from '../../../../../src/plugins/data/common'; +import { toElasticsearchQuery } from '../../../../../src/plugins/data/common/es_query'; +import { KueryNode } from '../../../../../src/plugins/data/server'; +import { RegistryAlertTypeWithAuth } from './alerts_authorization'; + +// pass in the field name instead of hardcoding `alert.attributes.alertTypeId` and `alertTypeId` +export function asFiltersByAlertTypeAndConsumer( + alertTypes: Set +): KueryNode { + return nodeBuilder.or( + Array.from(alertTypes).reduce((filters, { id, authorizedConsumers }) => { + ensureFieldIsSafeForQuery('alertTypeId', id); + filters.push( + nodeBuilder.and([ + nodeBuilder.is(`alert.attributes.alertTypeId`, id), + nodeBuilder.or( + Object.keys(authorizedConsumers).map((consumer) => { + ensureFieldIsSafeForQuery('consumer', consumer); + return nodeBuilder.is(`alert.attributes.consumer`, consumer); + }) + ), + ]) + ); + return filters; + }, []) + ); +} + +export function ensureFieldIsSafeForQuery(field: string, value: string): boolean { + const invalid = value.match(/([>=<\*:()]+|\s+)/g); + if (invalid) { + const whitespace = remove(invalid, (chars) => chars.trim().length === 0); + const errors = []; + if (whitespace.length) { + errors.push(`whitespace`); + } + if (invalid.length) { + errors.push(`invalid character${invalid.length > 1 ? `s` : ``}: ${invalid?.join(`, `)}`); + } + throw new Error(`expected ${field} not to include ${errors.join(' and ')}`); + } + return true; +} diff --git a/x-pack/plugins/apm/common/alert_types.ts b/x-pack/plugins/apm/common/alert_types.ts index 12df93d54b296..19a429a0c1a7a 100644 --- a/x-pack/plugins/apm/common/alert_types.ts +++ b/x-pack/plugins/apm/common/alert_types.ts @@ -10,6 +10,8 @@ import type { ValuesType } from 'utility-types'; import type { ActionGroup } from '../../alerting/common'; import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from './ml_constants'; +export const APM_SERVER_FEATURE_ID = 'apm'; + export enum AlertType { ErrorCount = 'apm.error_rate', // ErrorRate was renamed to ErrorCount but the key is kept as `error_rate` for backwards-compat. TransactionErrorRate = 'apm.transaction_error_rate', @@ -43,7 +45,7 @@ export const ALERT_TYPES_CONFIG: Record< actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, }, [AlertType.TransactionDuration]: { name: i18n.translate('xpack.apm.transactionDurationAlert.name', { @@ -52,7 +54,7 @@ export const ALERT_TYPES_CONFIG: Record< actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, }, [AlertType.TransactionDurationAnomaly]: { name: i18n.translate('xpack.apm.transactionDurationAnomalyAlert.name', { @@ -61,7 +63,7 @@ export const ALERT_TYPES_CONFIG: Record< actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, }, [AlertType.TransactionErrorRate]: { name: i18n.translate('xpack.apm.transactionErrorRateAlert.name', { @@ -70,7 +72,7 @@ export const ALERT_TYPES_CONFIG: Record< actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, }, }; diff --git a/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx b/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx index 50788c28999b5..e227dc863f9e1 100644 --- a/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx @@ -8,7 +8,10 @@ import React, { useCallback, useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { AlertType } from '../../../../common/alert_types'; +import { + AlertType, + APM_SERVER_FEATURE_ID, +} from '../../../../common/alert_types'; import { getInitialAlertValues } from '../get_initial_alert_values'; import { TriggersAndActionsUIPublicPluginStart } from '../../../../../triggers_actions_ui/public'; interface Props { @@ -38,7 +41,7 @@ export function AlertingFlyout(props: Props) { () => alertType && triggersActionsUi.getAddAlertFlyout({ - consumer: 'apm', + consumer: APM_SERVER_FEATURE_ID, onClose: onCloseAddFlyout, alertTypeId: alertType, canChangeTrigger: false, diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts index 51f82a577d429..67ba33002e6da 100644 --- a/x-pack/plugins/apm/server/feature.ts +++ b/x-pack/plugins/apm/server/feature.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { LicenseType } from '../../licensing/common/types'; -import { AlertType } from '../common/alert_types'; +import { AlertType, APM_SERVER_FEATURE_ID } from '../common/alert_types'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; import { LicensingPluginSetup, @@ -15,14 +15,14 @@ import { } from '../../licensing/server'; export const APM_FEATURE = { - id: 'apm', + id: APM_SERVER_FEATURE_ID, name: i18n.translate('xpack.apm.featureRegistry.apmFeatureName', { defaultMessage: 'APM and User Experience', }), order: 900, category: DEFAULT_APP_CATEGORIES.observability, - app: ['apm', 'ux', 'kibana'], - catalogue: ['apm'], + app: [APM_SERVER_FEATURE_ID, 'ux', 'kibana'], + catalogue: [APM_SERVER_FEATURE_ID], management: { insightsAndAlerting: ['triggersActions'], }, @@ -30,9 +30,9 @@ export const APM_FEATURE = { // see x-pack/plugins/features/common/feature_kibana_privileges.ts privileges: { all: { - app: ['apm', 'ux', 'kibana'], - api: ['apm', 'apm_write', 'rac'], - catalogue: ['apm'], + app: [APM_SERVER_FEATURE_ID, 'ux', 'kibana'], + api: [APM_SERVER_FEATURE_ID, 'apm_write'], + catalogue: [APM_SERVER_FEATURE_ID], savedObject: { all: [], read: [], @@ -51,9 +51,9 @@ export const APM_FEATURE = { ui: ['show', 'save', 'alerting:show', 'alerting:save'], }, read: { - app: ['apm', 'ux', 'kibana'], - api: ['apm', 'rac'], - catalogue: ['apm'], + app: [APM_SERVER_FEATURE_ID, 'ux', 'kibana'], + api: [APM_SERVER_FEATURE_ID], + catalogue: [APM_SERVER_FEATURE_ID], savedObject: { all: [], read: [], diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index 9ab56c1a303ea..f603249b8d1e2 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -120,6 +120,7 @@ export function mergeConfigs( export const plugin = (initContext: PluginInitializerContext) => new APMPlugin(initContext); +export { APM_SERVER_FEATURE_ID } from '../common/alert_types'; export { APMPlugin } from './plugin'; export { APMPluginSetup } from './types'; export { APMServerRouteRepository } from './routes/get_global_apm_server_route_repository'; 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 885b22ae343d8..f99d04132aff4 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 @@ -14,7 +14,11 @@ import { 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'; +import { + AlertType, + ALERT_TYPES_CONFIG, + APM_SERVER_FEATURE_ID, +} from '../../../common/alert_types'; import { PROCESSOR_EVENT, SERVICE_ENVIRONMENT, @@ -66,7 +70,7 @@ export function registerErrorCountAlertType({ apmActionVariables.interval, ], }, - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, minimumLicenseRequired: 'basic', executor: async ({ services, params }) => { const config = await config$.pipe(take(1)).toPromise(); 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 f77cc3ee930b1..565673936ba1a 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 @@ -14,7 +14,11 @@ import { } from '@kbn/rule-data-utils/target/technical_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 { + AlertType, + ALERT_TYPES_CONFIG, + APM_SERVER_FEATURE_ID, +} from '../../../common/alert_types'; import { PROCESSOR_EVENT, SERVICE_ENVIRONMENT, @@ -75,7 +79,7 @@ export function registerTransactionDurationAlertType({ apmActionVariables.interval, ], }, - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, minimumLicenseRequired: 'basic', executor: async ({ services, params }) => { const config = await config$.pipe(take(1)).toPromise(); 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 4d6a0685fd379..41b32985f7c21 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 @@ -12,7 +12,11 @@ import { ALERT_EVALUATION_VALUE, } from '@kbn/rule-data-utils/target/technical_field_names'; import { createLifecycleRuleTypeFactory } from '../../../../rule_registry/server'; -import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; +import { + AlertType, + ALERT_TYPES_CONFIG, + APM_SERVER_FEATURE_ID, +} from '../../../common/alert_types'; import { EVENT_OUTCOME, PROCESSOR_EVENT, @@ -70,7 +74,7 @@ export function registerTransactionErrorRateAlertType({ apmActionVariables.interval, ], }, - producer: 'apm', + producer: APM_SERVER_FEATURE_ID, minimumLicenseRequired: 'basic', executor: async ({ services, params: alertParams }) => { const config = await config$.pipe(take(1)).toPromise(); diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 10724594ce576..65c08191a97f6 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -176,6 +176,19 @@ export class MonitoringPlugin logger: this.log, }); initInfraSource(config, plugins.infra); + router.get({ path: '/monitoring-myfakepath', validate: false }, async (context, req, res) => { + try { + const racClient = await context.ruleRegistry?.getAlertsClient(); + const thing = await racClient?.find({ owner: 'apm' }); + console.error('THE THING!!!', JSON.stringify(thing.body, null, 2)); + return res.ok({ body: { success: true, alerts: thing.body.hits.hits } }); + } catch (err) { + console.error('monitoring route threw an error'); + console.error(err); + return res.unauthorized({ body: { message: err.message } }); + // return res.customError({ statusCode: err.statusCode, body: { message: err.message } }); + } + }); } return { diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 3dcf6862b7232..40bc837c85065 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -21,6 +21,7 @@ import type { ActionsApiRequestHandlerContext, } from '../../actions/server'; import type { AlertingApiRequestHandlerContext } from '../../alerting/server'; +import { RacApiRequestHandlerContext } from '../../rule_registry/server'; import { PluginStartContract as AlertingPluginStartContract, PluginSetupContract as AlertingPluginSetupContract, @@ -58,6 +59,7 @@ export interface RequestHandlerContextMonitoringPlugin extends RequestHandlerCon actions?: ActionsApiRequestHandlerContext; alerting?: AlertingApiRequestHandlerContext; infra: InfraRequestHandlerContext; + ruleRegistry?: RacApiRequestHandlerContext; } export interface PluginsStart { 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 index a946e9523548c..6d70c581802c1 100644 --- 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 @@ -18,6 +18,7 @@ import { ALERT_UUID, EVENT_ACTION, EVENT_KIND, + OWNER, PRODUCER, RULE_CATEGORY, RULE_ID, @@ -40,6 +41,7 @@ export const technicalRuleFieldMap = { RULE_CATEGORY, TAGS ), + [OWNER]: { type: 'keyword' }, [PRODUCER]: { type: 'keyword' }, [ALERT_UUID]: { type: 'keyword' }, [ALERT_ID]: { type: 'keyword' }, diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts index e0d75dc07afc1..42e71a19c974a 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts @@ -66,6 +66,7 @@ export interface FindResult { export interface UpdateOptions { id: string; + owner: string; data: { status: string; }; @@ -174,34 +175,36 @@ export class AlertsClient { public async update({ id, + owner, data, }: UpdateOptions): Promise> { - const query = buildAlertsSearchQuery({ - index: '.alerts-*', - alertId: id, - }); // TODO: Type out alerts (rule registry fields + alerting alerts type) - const { body: result } = await this.esClient.search(query); - const hits = result.hits.hits[0]; + const result = await this.esClient.get({ + index: '.alerts-observability-apm', // '.siem-signals-devin-hurley-default', + id, + }); + console.error('RESULT', result); + const hits = result.body._source; + console.error(`ruleTypeId: ${hits['rule.id']} and consumer: ${hits['kibana.rac.alert.owner']}`); try { // ASSUMPTION: user bulk updating alerts from single owner/space // may need to iterate to support rules shared across spaces await this.authorization.ensureAuthorized({ - ruleTypeId: hits['kibana.rac.alert.id'], - consumer: hits['kibana.rac.producer'], + ruleTypeId: hits['rule.id'], + consumer: hits['kibana.rac.alert.owner'], operation: WriteOperations.Update, entity: AlertingAuthorizationEntity.Alert, }); + console.error('GOT PAST AUTHZ'); try { - const indices = this.authorization.getAuthorizedAlertsIndices([ - hits['kibana.rac.producer'], - ]); - // TODO: @Devin fix params for update + const index = this.authorization.getAuthorizedAlertsIndices(hits['kibana.rac.alert.owner']); + + console.error('INDEX', index); const updateParameters = { id, - index: indices, + index, body: { doc: { 'kibana.rac.alert.status': data.status, @@ -209,16 +212,18 @@ export class AlertsClient { }, }; - return await this.esClient.update(updateParameters); + return this.esClient.update(updateParameters); } catch (error) { // TODO: Update error message this.logger.error(''); + console.error('UPDATE ERROR', error); throw error; } } catch (error) { + console.error('AUTHZ ERROR', error); throw Boom.forbidden( this.auditLogger.racAuthorizationFailure({ - owner: hits['kibana.rac.producer'], + owner: hits['kibana.rac.alert.producer'], operation: ReadOperations.Get, type: 'access', }) @@ -246,10 +251,13 @@ export class AlertsClient { }); try { - const indices = this.authorization.getAuthorizedAlertsIndices([owner]); + const index = this.authorization.getAuthorizedAlertsIndices(owner); + if (index == null) { + throw Error(`cannot find authorized index for owner: ${owner}`); + } const updateParameters = buildAlertsUpdateParameters({ ids, - index: indices, + index, status: data.status, }); diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts index b51ba3e10f91a..a25a8de526cb6 100644 --- a/x-pack/plugins/rule_registry/server/index.ts +++ b/x-pack/plugins/rule_registry/server/index.ts @@ -10,6 +10,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { RuleRegistryPlugin } from './plugin'; export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin'; +export { RacRequestHandlerContext, RacApiRequestHandlerContext } from './types'; export { RuleDataClient } from './rule_data_client'; export { IRuleDataClient } from './rule_data_client/types'; export { getRuleExecutorData, RuleExecutorData } from './utils/get_rule_executor_data'; diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index a8dff3d0d49d2..16d8f4e2a18a5 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import { PluginInitializerContext, Plugin, @@ -78,6 +79,43 @@ export class RuleRegistryPlugin implements Plugin { + const racClient = await context.rac.getAlertsClient(); + // console.error(`WHATS IN THE RAC CLIENT`, racClient); + racClient?.get({ id: 'hello world' }); + return res.ok(); + }); + + router.post( + { + path: '/update-alert', + validate: { + body: schema.object({ + status: schema.string(), + ids: schema.arrayOf(schema.string()), + }), + }, + }, + async (context, req, res) => { + try { + const racClient = await context.rac.getAlertsClient(); + console.error(req); + const { status, ids } = req.body; + console.error('STATUS', status); + console.error('ID', ids); + const thing = await racClient?.update({ + id: ids[0], + owner: 'apm', + data: { status }, + }); + return res.ok({ body: { success: true, alerts: thing } }); + } catch (exc) { + console.error('OOPS', exc); + return res.unauthorized(); + } + } + ); return service; } diff --git a/x-pack/plugins/rule_registry/server/scripts/README.md b/x-pack/plugins/rule_registry/server/scripts/README.md new file mode 100644 index 0000000000000..2b3f01f3c4d6b --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/README.md @@ -0,0 +1,24 @@ +Users with roles granting them access to monitoring (observability) and siem (security solution) should only be able to access alerts with those roles + +```bash +myterminal~$ ./get_security_solution_alert.sh observer +{ + "statusCode": 404, + "error": "Not Found", + "message": "Unauthorized to get \"rac:8.0.0:securitySolution/get\" alert\"" +} +myterminal~$ ./get_security_solution_alert.sh +{ + "success": true +} +myterminal~$ ./get_observability_alert.sh +{ + "success": true +} +myterminal~$ ./get_observability_alert.sh hunter +{ + "statusCode": 404, + "error": "Not Found", + "message": "Unauthorized to get \"rac:8.0.0:observability/get\" alert\"" +} +``` diff --git a/x-pack/plugins/rule_registry/server/scripts/get_observability_alert.sh b/x-pack/plugins/rule_registry/server/scripts/get_observability_alert.sh new file mode 100755 index 0000000000000..dd71e9dc6af43 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/get_observability_alert.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# Copyright 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. +# + +set -e + +USER=${1:-'observer'} + +cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd .. + +# Example: ./find_rules.sh +curl -s -k \ + -u $USER:changeme \ + -X GET ${KIBANA_URL}${SPACE_URL}/monitoring-myfakepath | jq . diff --git a/x-pack/plugins/rule_registry/server/scripts/get_security_solution_alert.sh b/x-pack/plugins/rule_registry/server/scripts/get_security_solution_alert.sh new file mode 100755 index 0000000000000..b4348266c9634 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/get_security_solution_alert.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Copyright 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. +# + +set -e + +cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd .. + + +USER=${1:-'hunter'} + +# Example: ./find_rules.sh +curl -s -k \ + -u $USER:changeme \ + -X GET ${KIBANA_URL}${SPACE_URL}/security-myfakepath | jq . diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/README.md b/x-pack/plugins/rule_registry/server/scripts/hunter/README.md new file mode 100644 index 0000000000000..a0269d5b060a3 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/README.md @@ -0,0 +1,5 @@ +This user can access the monitoring route at http://localhost:5601/security-myfakepath + +| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts | +| :-----------------: | :----------: | :-------------------------------: | :---: | :--------------: | :---------------: | :------------: | +| Hunter / T3 Analyst | read, write | read | read | read, write | read | read, write | diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/delete_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/hunter/delete_detections_user.sh new file mode 100755 index 0000000000000..595f0a49282d8 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/delete_detections_user.sh @@ -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. +# + +curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XDELETE ${ELASTICSEARCH_URL}/_security/user/hunter diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/detections_role.json b/x-pack/plugins/rule_registry/server/scripts/hunter/detections_role.json new file mode 100644 index 0000000000000..119fe5421c86c --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/detections_role.json @@ -0,0 +1,38 @@ +{ + "elasticsearch": { + "cluster": [], + "indices": [ + { + "names": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "privileges": ["read", "write"] + }, + { + "names": [".siem-signals-*"], + "privileges": ["read", "write"] + }, + { + "names": [".lists*", ".items*"], + "privileges": ["read", "write"] + } + ] + }, + "kibana": [ + { + "feature": { + "ml": ["read"], + "siem": ["all"], + "actions": ["read"], + "builtInAlerts": ["all"] + }, + "spaces": ["*"] + } + ] +} diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/detections_user.json b/x-pack/plugins/rule_registry/server/scripts/hunter/detections_user.json new file mode 100644 index 0000000000000..f9454cc0ad2fe --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/detections_user.json @@ -0,0 +1,6 @@ +{ + "password": "changeme", + "roles": ["hunter"], + "full_name": "Hunter", + "email": "detections-reader@example.com" +} diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/get_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/hunter/get_detections_role.sh new file mode 100755 index 0000000000000..7ec850ce220bb --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/get_detections_role.sh @@ -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. +# + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XGET ${KIBANA_URL}/api/security/role/hunter | jq -S . diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/index.ts b/x-pack/plugins/rule_registry/server/scripts/hunter/index.ts new file mode 100644 index 0000000000000..3411589de7721 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright 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 hunterUser from './detections_user.json'; +import * as hunterRole from './detections_role.json'; +export { hunterUser, hunterRole }; diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_role.sh new file mode 100755 index 0000000000000..debffe0fcac4c --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_role.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +ROLE=(${@:-./detections_role.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XPUT ${KIBANA_URL}/api/security/role/hunter \ +-d @${ROLE} diff --git a/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_user.sh new file mode 100755 index 0000000000000..ab2a053081394 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/hunter/post_detections_user.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +USER=(${@:-./detections_user.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + ${ELASTICSEARCH_URL}/_security/user/hunter \ +-d @${USER} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/README.md b/x-pack/plugins/rule_registry/server/scripts/observer copy/README.md new file mode 100644 index 0000000000000..a0269d5b060a3 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/README.md @@ -0,0 +1,5 @@ +This user can access the monitoring route at http://localhost:5601/security-myfakepath + +| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts | +| :-----------------: | :----------: | :-------------------------------: | :---: | :--------------: | :---------------: | :------------: | +| Hunter / T3 Analyst | read, write | read | read | read, write | read | read, write | diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/delete_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/observer copy/delete_detections_user.sh new file mode 100755 index 0000000000000..595f0a49282d8 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/delete_detections_user.sh @@ -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. +# + +curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XDELETE ${ELASTICSEARCH_URL}/_security/user/hunter diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_role.json b/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_role.json new file mode 100644 index 0000000000000..119fe5421c86c --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_role.json @@ -0,0 +1,38 @@ +{ + "elasticsearch": { + "cluster": [], + "indices": [ + { + "names": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "privileges": ["read", "write"] + }, + { + "names": [".siem-signals-*"], + "privileges": ["read", "write"] + }, + { + "names": [".lists*", ".items*"], + "privileges": ["read", "write"] + } + ] + }, + "kibana": [ + { + "feature": { + "ml": ["read"], + "siem": ["all"], + "actions": ["read"], + "builtInAlerts": ["all"] + }, + "spaces": ["*"] + } + ] +} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_user.json b/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_user.json new file mode 100644 index 0000000000000..f9454cc0ad2fe --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/detections_user.json @@ -0,0 +1,6 @@ +{ + "password": "changeme", + "roles": ["hunter"], + "full_name": "Hunter", + "email": "detections-reader@example.com" +} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/get_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/observer copy/get_detections_role.sh new file mode 100755 index 0000000000000..7ec850ce220bb --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/get_detections_role.sh @@ -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. +# + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XGET ${KIBANA_URL}/api/security/role/hunter | jq -S . diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/index.ts b/x-pack/plugins/rule_registry/server/scripts/observer copy/index.ts new file mode 100644 index 0000000000000..3411589de7721 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright 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 hunterUser from './detections_user.json'; +import * as hunterRole from './detections_role.json'; +export { hunterUser, hunterRole }; diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_role.sh new file mode 100755 index 0000000000000..debffe0fcac4c --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_role.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +ROLE=(${@:-./detections_role.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XPUT ${KIBANA_URL}/api/security/role/hunter \ +-d @${ROLE} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_user.sh new file mode 100755 index 0000000000000..ab2a053081394 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer copy/post_detections_user.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +USER=(${@:-./detections_user.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + ${ELASTICSEARCH_URL}/_security/user/hunter \ +-d @${USER} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/README.md b/x-pack/plugins/rule_registry/server/scripts/observer/README.md new file mode 100644 index 0000000000000..dc7e989ba4635 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/README.md @@ -0,0 +1,5 @@ +This user can access the monitoring route at http://localhost:5601/monitoring-myfakepath + +| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts | +| :------: | :----------: | :-------------------------------: | :---: | :--------------: | :---------------: | :------------: | +| observer | read, write | read | read | read, write | read | read, write | \ No newline at end of file diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/delete_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/observer/delete_detections_user.sh new file mode 100755 index 0000000000000..017d8904a51e1 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/delete_detections_user.sh @@ -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. +# + +curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XDELETE ${ELASTICSEARCH_URL}/_security/user/observer diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/detections_role.json b/x-pack/plugins/rule_registry/server/scripts/observer/detections_role.json new file mode 100644 index 0000000000000..5f6d43b451d13 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/detections_role.json @@ -0,0 +1,39 @@ +{ + "elasticsearch": { + "cluster": [], + "indices": [ + { + "names": [ + "apm-*-transaction*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "privileges": ["read", "write"] + }, + { + "names": [".siem-signals-*"], + "privileges": ["read", "write"] + }, + { + "names": [".lists*", ".items*"], + "privileges": ["read", "write"] + } + ] + }, + "kibana": [ + { + "feature": { + "ml": ["read"], + "monitoring": ["all"], + "apm": ["all"], + "actions": ["read"], + "builtInAlerts": ["all"] + }, + "spaces": ["*"] + } + ] +} diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/detections_user.json b/x-pack/plugins/rule_registry/server/scripts/observer/detections_user.json new file mode 100644 index 0000000000000..9f06e7dcc29f1 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/detections_user.json @@ -0,0 +1,6 @@ +{ + "password": "changeme", + "roles": ["observer"], + "full_name": "Observer", + "email": "monitoring-observer@example.com" +} \ No newline at end of file diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/get_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/observer/get_detections_role.sh new file mode 100755 index 0000000000000..7ec850ce220bb --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/get_detections_role.sh @@ -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. +# + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XGET ${KIBANA_URL}/api/security/role/hunter | jq -S . diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/get_observability_alert.sh b/x-pack/plugins/rule_registry/server/scripts/observer/get_observability_alert.sh new file mode 100755 index 0000000000000..dd71e9dc6af43 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/get_observability_alert.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# Copyright 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. +# + +set -e + +USER=${1:-'observer'} + +cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd .. + +# Example: ./find_rules.sh +curl -s -k \ + -u $USER:changeme \ + -X GET ${KIBANA_URL}${SPACE_URL}/monitoring-myfakepath | jq . diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/get_security_solution_alert.sh b/x-pack/plugins/rule_registry/server/scripts/observer/get_security_solution_alert.sh new file mode 100755 index 0000000000000..b4348266c9634 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/get_security_solution_alert.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Copyright 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. +# + +set -e + +cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd .. + + +USER=${1:-'hunter'} + +# Example: ./find_rules.sh +curl -s -k \ + -u $USER:changeme \ + -X GET ${KIBANA_URL}${SPACE_URL}/security-myfakepath | jq . diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/index.ts b/x-pack/plugins/rule_registry/server/scripts/observer/index.ts new file mode 100644 index 0000000000000..5feebc1caeed1 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright 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 observerUser from './detections_user.json'; +import * as observerRole from './detections_role.json'; +export { observerUser, observerRole }; diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_role.sh b/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_role.sh new file mode 100755 index 0000000000000..4dddb64befc6b --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_role.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +ROLE=(${@:-./detections_role.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-XPUT ${KIBANA_URL}/api/security/role/observer \ +-d @${ROLE} \ No newline at end of file diff --git a/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_user.sh b/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_user.sh new file mode 100755 index 0000000000000..8a897c0d28142 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/observer/post_detections_user.sh @@ -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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +USER=(${@:-./detections_user.json}) + +curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + ${ELASTICSEARCH_URL}/_security/user/observer \ +-d @${USER} \ No newline at end of file diff --git a/x-pack/plugins/rule_registry/server/scripts/update_observability_alert.sh b/x-pack/plugins/rule_registry/server/scripts/update_observability_alert.sh new file mode 100755 index 0000000000000..2fb78b2dd0656 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/update_observability_alert.sh @@ -0,0 +1,19 @@ +set -e + +IDS=${1} +STATUS=${2} + +echo $IDS +echo "'"$STATUS"'" + +cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh +cd .. + +# Example: ./update_observability_alert.sh [\"my-alert-id\",\"another-alert-id\"] +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u observer:changeme \ + -X POST ${KIBANA_URL}${SPACE_URL}/update-alert \ + -d "{\"ids\": $IDS, \"status\":\"$STATUS\"}" | jq . 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 index b523dd6770b9f..ab40ab70d58fe 100644 --- 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 @@ -25,6 +25,7 @@ import { ALERT_UUID, EVENT_ACTION, EVENT_KIND, + OWNER, RULE_UUID, TIMESTAMP, } from '../../common/technical_rule_data_field_names'; @@ -72,6 +73,13 @@ export const createLifecycleRuleTypeFactory: CreateLifecycleRuleTypeFactory = ({ } = options; const ruleExecutorData = getRuleExecutorData(type, options); + const so = await options.services.savedObjectsClient.get( + 'alert', + ruleExecutorData[RULE_UUID] + ); + console.error('IS THE LOGGER DEFINED?', logger); + console.error(`RULE REGISTRY CONSUMER ${so.attributes.consumer}`); + logger.debug(`LOGGER RULE REGISTRY CONSUMER ${so.attributes.consumer}`); const decodedState = wrappedStateRt.decode(previousState); @@ -180,6 +188,7 @@ export const createLifecycleRuleTypeFactory: CreateLifecycleRuleTypeFactory = ({ ...ruleExecutorData, [TIMESTAMP]: timestamp, [EVENT_KIND]: 'state', + [OWNER]: so.attributes.consumer, [ALERT_ID]: alertId, }; @@ -220,7 +229,13 @@ export const createLifecycleRuleTypeFactory: CreateLifecycleRuleTypeFactory = ({ return event; }); + logger.debug(`LOGGER EVENTSTOINDEX: ${JSON.stringify(eventsToIndex, null, 2)}`); + console.error(`EVENTSTOINDEX: ${JSON.stringify(eventsToIndex, null, 2)}`); + if (eventsToIndex.length) { + logger.debug('LOGGER ABOUT TO INDEX ALERTS'); + console.error('ABOUT TO INDEX ALERTS'); + await ruleDataClient.getWriter().bulk({ body: eventsToIndex.flatMap((event) => [{ index: {} }, event]), }); 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 index 1ea640add7b48..7b7ffc4717e29 100644 --- 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 @@ -6,6 +6,7 @@ */ import { + OWNER, PRODUCER, RULE_CATEGORY, RULE_ID, @@ -16,6 +17,7 @@ import { import { AlertTypeExecutor, AlertTypeWithExecutor } from '../types'; export interface RuleExecutorData { + [OWNER]: string; [RULE_CATEGORY]: string; [RULE_ID]: string; [RULE_UUID]: string; @@ -35,5 +37,6 @@ export function getRuleExecutorData( [RULE_NAME]: options.name, [TAGS]: options.tags, [PRODUCER]: type.producer, + [OWNER]: type.consumer, }; } diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts index 78f598500612d..8f7c777d9af7b 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts @@ -46,7 +46,7 @@ export class FeaturePrivilegeAlertingBuilder extends BaseFeaturePrivilegeBuilder feature: KibanaFeature ): string[] { const getAlertingPrivilege = ( - operations: string[], + operations: Record, privilegedTypes: readonly string[], alertingEntity: string, consumer: string diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json index d6a06848592cc..81c5c3b296ba8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json @@ -77,6 +77,9 @@ } } }, + "owner": { + "type": "keyword" + }, "rule": { "properties": { "id": { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts index 10cc168700447..5eb35e7b4a32a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts @@ -28,7 +28,7 @@ export const buildBulkBody = ( ): SignalHit => { const rule = buildRuleWithOverrides(ruleSO, doc._source!); const signal: Signal = { - ...buildSignal([doc], rule), + ...buildSignal([doc], rule, ruleSO.attributes.consumer), ...additionalSignalFields(doc), }; const event = buildEventTypeSignal(doc); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts index 237536a99c0f0..08c2e24c69712 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts @@ -76,7 +76,7 @@ export const removeClashes = (doc: BaseSignalHit): BaseSignalHit => { * @param docs The parent signals/events of the new signal to be built. * @param rule The rule that is generating the new signal. */ -export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema): Signal => { +export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema, owner: string): Signal => { const _meta = { version: SIGNALS_TEMPLATE_VERSION, }; @@ -92,6 +92,7 @@ export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema): Signal => parents, ancestors, status: 'open', + owner, rule, depth, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index d00bcc2a9f11e..664affeba3985 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -108,10 +108,13 @@ export const signalRulesAlertType = ({ services, params, spaceId, + consumer, updatedBy: updatedByUser, }) { const { ruleId, maxSignals, meta, outputIndex, timestampOverride, type } = params; + console.error('CONSUMER', consumer); + const searchAfterSize = Math.min(maxSignals, DEFAULT_SEARCH_AFTER_PAGE_SIZE); let hasError: boolean = false; let result = createSearchAfterReturnType(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 8f34e58ebc85b..2a30b62d3edfd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -211,6 +211,7 @@ export interface Signal { }; original_time?: string; original_event?: SearchTypes; + owner?: string; status: Status; threshold_result?: ThresholdResult; original_signal?: SearchTypes; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 9a35670737779..3223868d5ef68 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -210,6 +210,40 @@ export class Plugin implements IPlugin { + try { + const racClient = await context.ruleRegistry?.getAlertsClient(); + const thing = await racClient?.find({ owner: SERVER_APP_ID }); + console.error('hits?', JSON.stringify(thing.body.hits.hits, null, 2)); + return res.ok({ body: { success: true, alerts: thing.body.hits.hits } }); + } catch (err) { + console.error('monitoring route threw an error'); + console.error('ERROR JSON', JSON.stringify(err, null, 2)); + const statusCode = err.output.statusCode; + console.error('ERROR STATUSCODE?', statusCode); + // { message: err.message }, + + // const contentType = { + // 'Content-Type': 'application/json', + // }; + // const defaultedHeaders = { + // ...contentType, + // }; + + // return res.custom({ + // statusCode, + // headers: defaultedHeaders, + // body: Buffer.from( + // JSON.stringify({ + // message: 'hello world', //err.message, + // status_code: statusCode, + // }) + // ), + // }); + return res.unauthorized({ body: { message: err.message } }); + } + }); + plugins.features.registerKibanaFeature({ id: SERVER_APP_ID, name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle', { diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index ea63e188ab26d..5f35d735fd00f 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -9,6 +9,7 @@ import type { IRouter, RequestHandlerContext } from 'src/core/server'; import type { ListsApiRequestHandlerContext } from '../../lists/server'; import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; import type { AlertingApiRequestHandlerContext } from '../../alerting/server'; +import { RacApiRequestHandlerContext } from '../../rule_registry/server'; import { AppClient } from './client'; @@ -23,6 +24,7 @@ export type SecuritySolutionRequestHandlerContext = RequestHandlerContext & { licensing: LicensingApiRequestHandlerContext; alerting: AlertingApiRequestHandlerContext; lists?: ListsApiRequestHandlerContext; + ruleRegistry?: RacApiRequestHandlerContext; }; export type SecuritySolutionPluginRouter = IRouter;