Skip to content

Commit 19ebf65

Browse files
[Entity Analytics] Remove legacy scripted metric calculation for risk scoring (#244470)
## Summary This PR removes the legacy scripted metric calculation code from Entity Analytics Risk Scoring, leaving only the ESQL-based implementation as the default. Closes elastic/security-team#14204 ## Changes ### Core Implementation - **Moved shared utilities**: Extracted `processScores`, `filterFromRange`, and `getGlobalWeightForIdentifierType` functions from `calculate_risk_scores.ts` to `helpers.ts` for reuse - **Deleted legacy implementation**: Removed `calculate_risk_scores.ts` and all related test/mock files - **Deleted painless scripts**: Removed entire `painless/` folder containing 6 files (index.ts, index.test.ts, and 4 .painless script files) - **Simplified service logic**: Updated `risk_score_service.ts` and `calculate_and_persist_risk_scores.ts` to always use `calculateScoresWithESQL` without conditional logic ### Feature Flags & Settings - **Removed experimental flag**: Deleted `disableESQLRiskScoring` from `experimental_features.ts` - **Removed UI setting**: Deleted `ENABLE_ESQL_RISK_SCORING` constant and its UI setting configuration - **Updated imports**: Cleaned up all references to removed constants ### Tests - **Unit tests**: Updated `calculate_and_persist_risk_scores.test.ts` to remove scripted metric test blocks - **Integration tests**: Removed "Scripted metric" describe blocks from 4 test files: - `task_execution.ts` - `task_execution_nondefault_spaces.ts` - `risk_score_preview.ts` - `risk_score_entity_calculation.ts` ### Documentation & Schema - **Documentation**: Removed `securitySolution:enableEsqlRiskScoring` entry from advanced settings documentation - **Telemetry schema**: Removed references from: - `src/platform/plugins/shared/telemetry/schema/oss_platform.json` - `src/platform/plugins/private/kibana_usage_collection/server/collectors/management/types.ts` - `src/platform/plugins/private/kibana_usage_collection/server/collectors/management/schema.ts` ## Files Changed ### Modified (14 files) - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts` - Added shared utility functions - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/reset_to_zero.ts` - Updated import - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_esql_risk_scores.ts` - Updated import - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/apply_score_modifiers.ts` - Updated import - `x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts` - Removed flag - `x-pack/solutions/security/plugins/security_solution/common/constants.ts` - Removed constant - `x-pack/solutions/security/plugins/security_solution/server/ui_settings.ts` - Removed setting - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts` - Simplified logic - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts` - Simplified logic - `x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.test.ts` - Removed tests - 4 integration test files - Removed scripted metric test blocks ### Deleted (9 files) - `calculate_risk_scores.ts` - `calculate_risk_scores.test.ts` - `calculate_risk_scores.mock.ts` - `painless/index.ts` - `painless/index.test.ts` - `painless/risk_scoring_init.painless` - `painless/risk_scoring_map.painless` - `painless/risk_scoring_combine.painless` - `painless/risk_scoring_reduce.painless`
1 parent 843e226 commit 19ebf65

File tree

27 files changed

+199
-1199
lines changed

27 files changed

+199
-1199
lines changed

docs/reference/advanced-settings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ $$$security-solution-excluded-data-tiers-for-rule-execution$$$`securitySolution:
590590
$$$security-solution-enable-privileged-user-monitoring$$$`securitySolution:enablePrivilegedUserMonitoring` {applies_to}`stack: preview` {applies_to}`serverless: unavailable`
591591
: Enables the privileged user monitoring dashboard and onboarding experience, which are in technical preview. `true` by default.
592592

593-
$$$security-solution-enable-esql-risk-scoring$$$`securitySolution:enableEsqlRiskScoring` {applies_to}`stack: preview` {applies_to}`serverless: unavailable`
593+
$$$security-solution-enable-esql-risk-scoring$$$`securitySolution:enableEsqlRiskScoring` {applies_to}`stack: preview 9.0, removed 9.3` {applies_to}`serverless: unavailable`
594594
: Enables risk scoring based on {{esql}} queries. Disabling this reverts to using scripted metrics. `true` by default.
595595

596596
$$$security-solution-default-ai-connector$$$`securitySolution:defaultAIConnector` {applies_to}`stack: unavailable` {applies_to}`security: ga`

src/platform/plugins/private/kibana_usage_collection/server/collectors/management/schema.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,6 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
122122
type: 'boolean',
123123
_meta: { description: 'Non-default value of setting.' },
124124
},
125-
'securitySolution:enableEsqlRiskScoring': {
126-
type: 'boolean',
127-
_meta: { description: 'Non-default value of setting.' },
128-
},
129125
'securitySolution:defaultAnomalyScore': {
130126
type: 'long',
131127
_meta: { description: 'Non-default value of setting.' },

src/platform/plugins/private/kibana_usage_collection/server/collectors/management/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export interface UsageStats {
7575
'securitySolution:enableGraphVisualization': boolean;
7676
'securitySolution:enableAssetInventory': boolean;
7777
'securitySolution:enablePrivilegedUserMonitoring': boolean;
78-
'securitySolution:enableEsqlRiskScoring': boolean;
7978
'securitySolution:enableCloudConnector': boolean;
8079
'securitySolution:suppressionBehaviorOnAlertClosure': string;
8180
'securitySolution:defaultValueReportMinutes': string;

src/platform/plugins/shared/telemetry/schema/oss_platform.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10229,12 +10229,6 @@
1022910229
"description": "Non-default value of setting."
1023010230
}
1023110231
},
10232-
"securitySolution:enableEsqlRiskScoring": {
10233-
"type": "boolean",
10234-
"_meta": {
10235-
"description": "Non-default value of setting."
10236-
}
10237-
},
1023810232
"securitySolution:defaultAnomalyScore": {
1023910233
"type": "long",
1024010234
"_meta": {

x-pack/solutions/security/plugins/security_solution/common/constants.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,6 @@ export const ENABLE_SIEM_READINESS_SETTING = 'securitySolution:enableSiemReadine
266266
export const ENABLE_PRIVILEGED_USER_MONITORING_SETTING =
267267
'securitySolution:enablePrivilegedUserMonitoring' as const;
268268

269-
/** This Kibana Advanced Setting allows users to enable/disable ESQL-based risk scoring */
270-
export const ENABLE_ESQL_RISK_SCORING = 'securitySolution:enableEsqlRiskScoring' as const;
271-
272269
/**
273270
* Id for the notifications alerting type
274271
* @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function

x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@ export const allowedExperimentalValues = Object.freeze({
6060
*/
6161
assistantModelEvaluation: false,
6262

63-
/**
64-
* Disables ESQL-based risk scoring
65-
*/
66-
disableESQLRiskScoring: false,
67-
6863
/**
6964
* Enable resetting risk scores to zero for outdated entities
7065
*/

x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/apply_score_modifiers.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@ import type { PrivmonUserCrudService } from '../privilege_monitoring/users/privi
2020

2121
import type { RiskScoreBucket } from '../types';
2222
import { RIEMANN_ZETA_VALUE } from './constants';
23-
import { getGlobalWeightForIdentifierType } from './calculate_risk_scores';
23+
import { getGlobalWeightForIdentifierType, max10DecimalPlaces } from './helpers';
2424
import type { AssetCriticalityRiskFields } from './modifiers/asset_criticality';
2525
import { applyCriticalityModifier } from './modifiers/asset_criticality';
2626
import type { PrivmonRiskFields } from './modifiers/privileged_users';
2727
import { applyPrivmonModifier } from './modifiers/privileged_users';
28-
import { max10DecimalPlaces } from './helpers';
2928
import type { ExperimentalFeatures } from '../../../../common';
3029
interface ModifiersUpdateParams {
3130
now: string;

x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.test.ts

Lines changed: 34 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,19 @@ import { assetCriticalityServiceMock } from '../asset_criticality/asset_critical
1111
import { privmonUserCrudServiceMock } from '../privilege_monitoring/users/privileged_users_crud.mock';
1212

1313
import { calculateAndPersistRiskScores } from './calculate_and_persist_risk_scores';
14-
import { calculateRiskScores } from './calculate_risk_scores';
15-
import { calculateRiskScoresMock } from './calculate_risk_scores.mock';
1614
import { calculateScoresWithESQL } from './calculate_esql_risk_scores';
1715
import { calculateScoresWithESQLMock } from './calculate_esql_risk_scores.mock';
1816
import { riskScoreDataClientMock } from './risk_score_data_client.mock';
1917
import type { RiskScoreDataClient } from './risk_score_data_client';
2018
import type { ExperimentalFeatures } from '../../../../common';
2119
import { EntityType } from '../../../../common/search_strategy';
2220

23-
jest.mock('./calculate_risk_scores');
2421
jest.mock('./calculate_esql_risk_scores');
2522

2623
const calculateAndPersistRecentHostRiskScores = (
2724
esClient: ElasticsearchClient,
2825
logger: Logger,
29-
riskScoreDataClient: RiskScoreDataClient,
30-
esql: boolean = false
26+
riskScoreDataClient: RiskScoreDataClient
3127
) => {
3228
return calculateAndPersistRiskScores({
3329
afterKeys: {},
@@ -42,9 +38,7 @@ const calculateAndPersistRecentHostRiskScores = (
4238
assetCriticalityService: assetCriticalityServiceMock.create(),
4339
privmonUserCrudService: privmonUserCrudServiceMock.create(),
4440
runtimeMappings: {},
45-
experimentalFeatures: {
46-
disableESQLRiskScoring: !esql,
47-
} as ExperimentalFeatures,
41+
experimentalFeatures: {} as ExperimentalFeatures,
4842
});
4943
};
5044

@@ -53,100 +47,52 @@ describe('calculateAndPersistRiskScores', () => {
5347
let logger: Logger;
5448
let riskScoreDataClient: RiskScoreDataClient;
5549

56-
describe('scripted metric', () => {
57-
const calculate = () =>
58-
calculateAndPersistRecentHostRiskScores(esClient, logger, riskScoreDataClient, false);
50+
const calculate = () =>
51+
calculateAndPersistRecentHostRiskScores(esClient, logger, riskScoreDataClient);
5952

53+
beforeEach(() => {
54+
esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
55+
logger = loggingSystemMock.createLogger();
56+
riskScoreDataClient = riskScoreDataClientMock.create();
57+
});
58+
59+
describe('with no risk scores to persist', () => {
6060
beforeEach(() => {
61-
esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
62-
logger = loggingSystemMock.createLogger();
63-
riskScoreDataClient = riskScoreDataClientMock.create();
61+
(calculateScoresWithESQL as jest.Mock).mockResolvedValueOnce(
62+
calculateScoresWithESQLMock.buildResponse({ scores: { host: [] } })
63+
);
6464
});
6565

66-
describe('with no risk scores to persist', () => {
67-
beforeEach(() => {
68-
(calculateRiskScores as jest.Mock).mockResolvedValueOnce(
69-
calculateRiskScoresMock.buildResponse({ scores: { host: [] } })
70-
);
71-
});
72-
73-
it('does not upgrade configurations', async () => {
74-
await calculate();
66+
it('does not upgrade configurations', async () => {
67+
await calculate();
7568

76-
expect(riskScoreDataClient.upgradeIfNeeded).not.toHaveBeenCalled();
77-
});
78-
79-
it('returns an appropriate response', async () => {
80-
const results = await calculate();
81-
82-
const entities = {
83-
host: [],
84-
user: [],
85-
service: [],
86-
generic: [],
87-
};
88-
expect(results).toEqual({ after_keys: {}, errors: [], scores_written: 0, entities });
89-
});
69+
expect(riskScoreDataClient.upgradeIfNeeded).not.toHaveBeenCalled();
9070
});
91-
describe('with risk scores to persist', () => {
92-
beforeEach(() => {
93-
(calculateRiskScores as jest.Mock).mockResolvedValueOnce(
94-
calculateRiskScoresMock.buildResponseWithOneScore()
95-
);
96-
});
97-
it('upgrades configurations when persisting risk scores', async () => {
98-
await calculate();
9971

100-
expect(riskScoreDataClient.upgradeIfNeeded).toHaveBeenCalled();
101-
});
72+
it('returns an appropriate response', async () => {
73+
const results = await calculate();
74+
75+
const entities = {
76+
host: [],
77+
user: [],
78+
service: [],
79+
generic: [],
80+
};
81+
expect(results).toEqual({ after_keys: {}, errors: [], scores_written: 0, entities });
10282
});
10383
});
10484

105-
describe('ESQL', () => {
106-
const calculate = () =>
107-
calculateAndPersistRecentHostRiskScores(esClient, logger, riskScoreDataClient, true);
85+
describe('with risk scores to persist', () => {
10886
beforeEach(() => {
109-
esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
110-
logger = loggingSystemMock.createLogger();
111-
riskScoreDataClient = riskScoreDataClientMock.create();
87+
(calculateScoresWithESQL as jest.Mock).mockResolvedValueOnce(
88+
calculateScoresWithESQLMock.buildResponseWithOneScore()
89+
);
11290
});
11391

114-
describe('with no risk scores to persist', () => {
115-
beforeEach(() => {
116-
(calculateScoresWithESQL as jest.Mock).mockResolvedValueOnce(
117-
calculateScoresWithESQLMock.buildResponse({ scores: { host: [] } })
118-
);
119-
});
120-
121-
it('does not upgrade configurations', async () => {
122-
await calculate();
123-
124-
expect(riskScoreDataClient.upgradeIfNeeded).not.toHaveBeenCalled();
125-
});
126-
127-
it('returns an appropriate response', async () => {
128-
const results = await calculate();
129-
130-
const entities = {
131-
host: [],
132-
user: [],
133-
service: [],
134-
generic: [],
135-
};
136-
expect(results).toEqual({ after_keys: {}, errors: [], scores_written: 0, entities });
137-
});
138-
});
139-
describe('with risk scores to persist', () => {
140-
beforeEach(() => {
141-
(calculateScoresWithESQL as jest.Mock).mockResolvedValueOnce(
142-
calculateScoresWithESQLMock.buildResponseWithOneScore()
143-
);
144-
});
145-
it('upgrades configurations when persisting risk scores', async () => {
146-
await calculate();
92+
it('upgrades configurations when persisting risk scores', async () => {
93+
await calculate();
14794

148-
expect(riskScoreDataClient.upgradeIfNeeded).toHaveBeenCalled();
149-
});
95+
expect(riskScoreDataClient.upgradeIfNeeded).toHaveBeenCalled();
15096
});
15197
});
15298
});

x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import type { ExperimentalFeatures } from '../../../../common';
1111
import type { EntityType } from '../../../../common/search_strategy';
1212
import type { RiskScoreDataClient } from './risk_score_data_client';
1313
import type { AssetCriticalityService } from '../asset_criticality/asset_criticality_service';
14-
import { calculateRiskScores } from './calculate_risk_scores';
1514
import type { CalculateAndPersistScoresParams } from '../types';
1615
import { calculateScoresWithESQL } from './calculate_esql_risk_scores';
1716
import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics';
@@ -38,10 +37,7 @@ export const calculateAndPersistRiskScores = async (
3837
namespace: spaceId,
3938
});
4039

41-
const calculate = params.experimentalFeatures.disableESQLRiskScoring
42-
? calculateRiskScores
43-
: calculateScoresWithESQL;
44-
const { after_keys: afterKeys, scores } = await calculate(rest);
40+
const { after_keys: afterKeys, scores } = await calculateScoresWithESQL(rest);
4541

4642
// Extract entity IDs from scores for reset-to-zero functionality
4743
const entities: Record<EntityType, string[]> = {

x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_esql_risk_scores.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import type { AssetCriticalityService } from '../asset_criticality/asset_critica
3232
import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics';
3333
import type { CalculateScoresParams, RiskScoreBucket, RiskScoreCompositeBuckets } from '../types';
3434
import { RIEMANN_ZETA_S_VALUE, RIEMANN_ZETA_VALUE } from './constants';
35-
import { filterFromRange } from './calculate_risk_scores';
35+
import { filterFromRange } from './helpers';
3636
import { applyScoreModifiers } from './apply_score_modifiers';
3737
import type { PrivmonUserCrudService } from '../privilege_monitoring/users/privileged_users_crud';
3838

0 commit comments

Comments
 (0)