Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Entity Analytics][Risk Engine] Risk Scoring Task #163216

Merged
merged 65 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f48a70c
WIP: defining the basics of our risk scoring task
rylnd Aug 3, 2023
cafaeb5
RiskEngineDataClient uses scoped esClient
rylnd Aug 3, 2023
d56196a
Instantiate the RiskEngineDataClient with an SO client and the namespace
rylnd Aug 3, 2023
1ca8915
Add some helpers for converting a relative date range to an absolute one
rylnd Aug 4, 2023
61d3fd3
Add (and use) configuration from our saved object
rylnd Aug 4, 2023
3aab2fb
Create our initial configuration SO with reasonable default values
rylnd Aug 4, 2023
f5d8b8d
WIP: starting task and writing an integration test
rylnd Aug 4, 2023
b63146e
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Aug 4, 2023
a1a7614
[CI] Auto-commit changed files from 'node scripts/check_mappings_upda…
kibanamachine Aug 4, 2023
2daf715
Add risk engine task to CI test runner
rylnd Aug 5, 2023
2b9a8d5
More accurate integration test name
rylnd Aug 5, 2023
a29b2b2
Update integration test
rylnd Aug 5, 2023
ecf4fea
Trying to thread through our init route properly
rylnd Aug 5, 2023
8d67ef0
Remove overriding ssl config in our integration tests
rylnd Aug 8, 2023
af2b245
WIP: Moving dependencies around to get enable/disable of task working
rylnd Aug 9, 2023
50cfb82
Disable o11y plugin in our integration tests
rylnd Aug 9, 2023
b0bcfdb
Better integration tests, and more OF them
rylnd Aug 9, 2023
a8c59e4
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Aug 9, 2023
f494399
Fix some basic CI checks
rylnd Aug 9, 2023
f59ec8f
Fix flaky test by ensuring our `waitFor` is not a false positive
rylnd Aug 9, 2023
060a975
Test (and fix) removal of the risk scoring task
rylnd Aug 9, 2023
f9ac1a9
Common description for all our risk engine integration tests
rylnd Aug 9, 2023
5670253
Remove security dependency from risk engine routes
rylnd Aug 9, 2023
865bd63
Clean up Risk Engine routes
rylnd Aug 9, 2023
ee0d4e5
Refactors task code into component functions
rylnd Aug 9, 2023
e9725ef
Add TODO
rylnd Aug 9, 2023
c307603
[CI] Auto-commit changed files from 'node scripts/check_mappings_upda…
kibanamachine Aug 9, 2023
5fcc112
Update data client unit tests
rylnd Aug 9, 2023
669d0f0
Prevent an empty object/array filter arg from breaking our scoring query
rylnd Aug 10, 2023
e6a4bdc
Update risk engine configuration SO hash
rylnd Aug 10, 2023
3f4bc36
Risk scoring task can now score both hosts and users
rylnd Aug 10, 2023
cf82666
Address some race conditions in Risk Engine setup
rylnd Aug 10, 2023
1859efb
Allow risk scoring task to be executed in multiple spaces
rylnd Aug 11, 2023
7fb7863
node scripts/check_mappings_update --fix
rylnd Aug 11, 2023
191f378
Update core snapshot hash for our risk configuration SO
rylnd Aug 11, 2023
bcbb6c6
Add back our log option to this delete helper
rylnd Aug 11, 2023
dfdb10a
Allow soClient to retrieve configuration from the current space
rylnd Aug 14, 2023
42c4ec2
Add some more task logging
rylnd Aug 14, 2023
c02416b
Merge branch 'main' into risk_engine_task
rylnd Aug 14, 2023
5c563e1
Merge branch 'main' into risk_engine_task
kibanamachine Aug 14, 2023
58dbf6f
Merge branch 'main' into risk_engine_task
rylnd Aug 15, 2023
9fb7741
Merge branch 'main' into risk_engine_task
rylnd Aug 15, 2023
1212229
Fix bad resolution
rylnd Aug 15, 2023
85619a8
Merge branch 'main' into risk_engine_task
rylnd Aug 15, 2023
ab81b27
Merge branch 'main' into risk_engine_task
rylnd Aug 16, 2023
561fdb8
Merge branch 'main' into risk_engine_task
rylnd Aug 17, 2023
343ee3c
Merge branch 'main' into risk_engine_task
rylnd Aug 22, 2023
672fd00
Add JSDoc for some ambiguously named test helper functions
rylnd Aug 22, 2023
2879d8a
Re-throw errors from taskmanager when enabling the risk scoring task
rylnd Aug 22, 2023
0dcb486
Add some tests around enable/disable routes
rylnd Aug 22, 2023
7cf14b9
Better synchronize enabling of risk engine task
rylnd Aug 23, 2023
14eabff
Merge branch 'main' into risk_engine_task
rylnd Aug 23, 2023
904a764
linting: remove redundant await
rylnd Aug 23, 2023
c8c1c12
Add risk engine test for non-default space
rylnd Aug 23, 2023
d160e0d
Remove outstanding TODO
rylnd Aug 23, 2023
64d9cf5
Use a more robust SavedObjects client in our risk scoring task
rylnd Aug 23, 2023
26a440f
Merge branch 'main' into risk_engine_task
rylnd Aug 23, 2023
acdec7d
Add persistence feature flag to our cypress tests
rylnd Aug 24, 2023
8687fff
Remove unnecessary `visit` step from our test
rylnd Aug 24, 2023
80785e2
Rename and document function to make its purpose more clear
rylnd Aug 24, 2023
6777892
Revert integration test config changes
rylnd Aug 24, 2023
bcb1cee
Merge branch 'main' into risk_engine_task
rylnd Aug 24, 2023
fe475d4
Update pending tests
rylnd Aug 24, 2023
9abdb47
Merge branch 'main' into risk_engine_task
rylnd Aug 24, 2023
22938f8
Merge branch 'main' into risk_engine_task
rylnd Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/kbn-check-mappings-update-cli/current_mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2956,8 +2956,34 @@
"risk-engine-configuration": {
"dynamic": false,
"properties": {
"dataViewId": {
"type": "keyword"
},
"enabled": {
"type": "boolean"
},
"filter": {
"dynamic": false,
"properties": {}
},
"identifierType": {
"type": "keyword"
},
"interval": {
"type": "keyword"
},
"pageSize": {
"type": "integer"
},
"range": {
rylnd marked this conversation as resolved.
Show resolved Hide resolved
"properties": {
"start": {
"type": "keyword"
},
"end": {
"type": "keyword"
}
}
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"osquery-pack-asset": "b14101d3172c4b60eb5404696881ce5275c84152",
"osquery-saved-query": "44f1161e165defe3f9b6ad643c68c542a765fcdb",
"query": "21cbbaa09abb679078145ce90087b1e88b7eae95",
"risk-engine-configuration": "1b8b175e29ea5311408125c92c6247f502b2d79d",
"risk-engine-configuration": "b105d4a3c6adce40708d729d12e5ef3c8fbd9508",
"rules-settings": "892a2918ebaeba809a612b8d97cec0b07c800b5f",
"sample-data-telemetry": "37441b12f5b0159c2d6d5138a494c9f440e950b5",
"search": "8d5184dd5b986d57250b6ffd9ae48a1925e4c7a3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
export * from './after_keys';
export * from './risk_weights';
export * from './identifier_types';
export * from './range';
export * from './types';
export * from './indices';
export * from './constants';
15 changes: 15 additions & 0 deletions x-pack/plugins/security_solution/common/risk_engine/range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import * as t from 'io-ts';

export const rangeSchema = t.type({
start: t.string,
end: t.string,
});
export type RangeSchema = t.TypeOf<typeof rangeSchema>;
export type Range = RangeSchema;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as t from 'io-ts';
import { DataViewId } from '../../api/detection_engine/model/rule_schema';
import { afterKeysSchema } from '../after_keys';
import { identifierTypeSchema } from '../identifier_types';
import { rangeSchema } from '../range';
import { riskWeightsSchema } from '../risk_weights/schema';

export const riskScorePreviewRequestSchema = t.exact(
Expand All @@ -22,10 +23,7 @@ export const riskScorePreviewRequestSchema = t.exact(
filter: t.unknown,
page_size: t.number,
identifier_type: identifierTypeSchema,
range: t.type({
start: t.string,
end: t.string,
}),
range: rangeSchema,
weights: riskWeightsSchema,
}),
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import type {

import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks';
import type { EndpointAuthz } from '../../../../../common/endpoint/types/authz';
import { riskEngineDataClientMock } from '../../../risk_engine/__mocks__/risk_engine_data_client_mock';
import { riskEngineDataClientMock } from '../../../risk_engine/risk_engine_data_client.mock';

export const createMockClients = () => {
const core = coreMock.createRequestHandlerContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ describe('calculateRiskScores()', () => {
);
});

it('drops an empty object filter if specified by the caller', async () => {
params.filter = {};
await calculateRiskScores(params);
expect(esClient.search).toHaveBeenCalledWith(
expect.objectContaining({
query: {
bool: {
filter: expect.not.arrayContaining([{}]),
},
},
})
);
});

it('drops an empty array filter if specified by the caller', async () => {
params.filter = [];
await calculateRiskScores(params);
expect(esClient.search).toHaveBeenCalledWith(
expect.objectContaining({
query: {
bool: {
filter: expect.not.arrayContaining([[]]),
},
},
})
);
});

describe('identifierType', () => {
it('creates aggs for both host and user by default', async () => {
await calculateRiskScores(params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { isEmpty } from 'lodash';
import type {
AggregationsAggregationContainer,
QueryDslQueryContainer,
Expand Down Expand Up @@ -213,7 +214,7 @@ export const calculateRiskScores = async ({
const now = new Date().toISOString();

const filter = [{ exists: { field: ALERT_RISK_SCORE } }, filterFromRange(range)];
if (userFilter) {
if (!isEmpty(userFilter)) {
filter.push(userFilter as QueryDslQueryContainer);
}
const identifierTypes: IdentifierType[] = identifierType ? [identifierType] : ['host', 'user'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'
import type { Logger, SavedObjectsClientContract } from '@kbn/core/server';
import type { DataViewAttributes } from '@kbn/data-views-plugin/common';

interface RiskInputsIndexResponse {
export interface RiskInputsIndexResponse {
index: string;
runtimeMappings: MappingRuntimeFields;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 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 { isRiskScoreCalculationComplete } from './helpers';

describe('isRiskScoreCalculationComplete', () => {
it('is true if both after_keys.host and after_keys.user are empty', () => {
const result = {
after_keys: {
host: {},
user: {},
},
};
// @ts-expect-error using a minimal result object for testing
expect(isRiskScoreCalculationComplete(result)).toEqual(true);
});

it('is true if after_keys is an empty object', () => {
const result = {
after_keys: {},
};
// @ts-expect-error using a minimal result object for testing
expect(isRiskScoreCalculationComplete(result)).toEqual(true);
});

it('is false if the host key has a key/value', () => {
const result = {
after_keys: {
host: {
key: 'value',
},
},
};
// @ts-expect-error using a minimal result object for testing
expect(isRiskScoreCalculationComplete(result)).toEqual(false);
});

it('is false if the user key has a key/value', () => {
const result = {
after_keys: {
user: {
key: 'value',
},
},
};
// @ts-expect-error using a minimal result object for testing
expect(isRiskScoreCalculationComplete(result)).toEqual(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import type { AfterKey, AfterKeys, IdentifierType } from '../../../common/risk_engine';
import type { CalculateAndPersistScoresResponse } from './types';

export const getFieldForIdentifierAgg = (identifierType: IdentifierType): string =>
identifierType === 'host' ? 'host.name' : 'user.name';
Expand All @@ -17,3 +18,9 @@ export const getAfterKeyForIdentifierType = ({
afterKeys: AfterKeys;
identifierType: IdentifierType;
}): AfterKey | undefined => afterKeys[identifierType];

export const isRiskScoreCalculationComplete = (
result: CalculateAndPersistScoresResponse
): boolean =>
Object.keys(result.after_keys.host ?? {}).length === 0 &&
Object.keys(result.after_keys.user ?? {}).length === 0;
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
* 2.0.
*/

import type { RiskEngineDataClient } from '../risk_engine_data_client';
import type { RiskEngineDataClient } from './risk_engine_data_client';

const createRiskEngineDataClientMock = () =>
({
disableLegacyRiskEngine: jest.fn(),
disableRiskEngine: jest.fn(),
enableRiskEngine: jest.fn(),
getConfiguration: jest.fn(),
getRiskInputsIndex: jest.fn(),
getStatus: jest.fn(),
getWriter: jest.fn(),
initializeResources: jest.fn(),
init: jest.fn(),
initializeResources: jest.fn(),
} as unknown as jest.Mocked<RiskEngineDataClient>);

export const riskEngineDataClientMock = { create: createRiskEngineDataClientMock };
Loading