mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Entity Analytics] [Asset Criticality] Add "unassigned" as an asset criticality level for bulk_upload (#208884)
This pull request introduces the new `unassigned` criticality level for the asset criticality's `bulk_upload` . ### Key Changes: #### Schema and Configuration Updates: * Added `unassigned` to the list of criticality levels in multiple schema files (`kibana.serverless.yaml`, `kibana.yaml`, `common.schema.yaml`, `ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml`, `serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml`). #### TypeScript and Constants: * Updated `AssetCriticalityLevel` enum in `common.gen.ts` to include `unassigned`. * Added `UNASSIGNED` to `CriticalityLevels` enum and `CriticalityModifiers` in `constants.ts`. #### Tests: * Updated test cases to include `unassigned` as a valid criticality level in `parse_asset_criticality_csv_row.test.ts`, `validations.test.ts`, `asset_criticality_data_client.test.ts`, and `asset_criticality_csv_upload.ts`. #### Backend Logic: * Modified `AssetCriticalityDataClient` to handle `unassigned` criticality level appropriately. <img width="1488" alt="Screenshot 2025-01-30 at 2 03 11 PM" src="https://github.com/user-attachments/assets/938411c4-725a-451c-ab38-aca36a704e91" /> ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Testing Steps The main idea for this change is that the `unassigned` criticality level is actually marked as `deleted` in the ES documents. ES index name for default space : `.asset-criticality.asset-criticality-default` #### API 1. `POST /api/asset_criticality/upload_csv` ``` curl --location 'http://localhost:5601/api/asset_criticality/upload_csv?output=stream' \ --header 'kbn-xsrf: hello' \ --header 'Accept: multipart/form-data' \ --header 'Authorization: *******' \ --form 'file=@"<Full path of the CSV file for asset criticality>" ``` Error response : ``` {"errors":[{"message":"Invalid criticality level \"unassigned_impact\", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned","index":4}],"stats":{"successful":3,"failed":1,"total":4}}% ``` Success response : ``` {"errors":[],"stats":{"successful":4,"failed":0,"total":4}}% ``` Query the ES using below query to see if the criticality level is `deleted` ``` GET .asset-criticality.asset-criticality-default/_search { "query": { "match": { "asset.criticality": "deleted" } } } ``` ### UI 1. Navigate to Entity Store page 2. Upload a csv file with incorrect asset criticality level  3. Rectify and upload the same file with correct criticality levels.  4. Navigate to EA Dashboard and scroll down to the Entities section. 5. Select an entity and open the flyout. 6. Try changing the asset criticality of the entity. No blank/empty value should be present or assigned to asset criticality. 7. Should be able to successfully modify the asset criticality for the entity.   Confirm this by querying the ES with the query : ``` GET .asset-criticality.asset-criticality-default/_search { "query": { "match": { "asset.criticality": "deleted" } } } ``` Ensure that the Elastic search document's `_source` contains three keys, with `deleted` present as the ` `criticality_level` for the below three keys. `criticality_level` `host.asset.criticality` `asset.criticality` Example :  ### Bulk upload ``` curl --location 'http://localhost:5601/api/asset_criticality/bulk' \ --header 'kbn-xsrf: hello' \ --header 'Content-Type: application/json' \ --header 'Authorization: Basic ****' \ --data '{ "records": [ { "id_value": "host-1", "id_field": "host.name", "criticality_level": "low_impact" }, { "id_value": "host-2", "id_field": "host.name", "criticality_level": "medium_impact" }, { "id_value": "host-6", "id_field": "host.name", "criticality_level": "medium_impact" }, { "id_value": "host-3", "id_field": "host.name", "criticality_level": "high_impact" }, { "id_value": "host-4", "id_field": "host.name", "criticality_level": "high_impact" }, { "id_value": "host-bulkupload", "id_field": "host.name", "criticality_level": "unassigned" } ] } ```  --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e527f2b79a
commit
5fbbcf97a1
16 changed files with 219 additions and 36 deletions
|
@ -5054,7 +5054,14 @@ paths:
|
|||
properties:
|
||||
records:
|
||||
items:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_CreateAssetCriticalityRecord'
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityRecordIdParts'
|
||||
- type: object
|
||||
properties:
|
||||
criticality_level:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityLevelsForBulkUpload'
|
||||
required:
|
||||
- criticality_level
|
||||
maxItems: 1000
|
||||
minItems: 1
|
||||
type: array
|
||||
|
@ -51915,6 +51922,15 @@ components:
|
|||
- high_impact
|
||||
- extreme_impact
|
||||
type: string
|
||||
Security_Entity_Analytics_API_AssetCriticalityLevelsForBulkUpload:
|
||||
description: The criticality level of the asset for bulk upload. The value `unassigned` is used to indicate that the criticality level is not assigned and is only used for bulk upload.
|
||||
enum:
|
||||
- low_impact
|
||||
- medium_impact
|
||||
- high_impact
|
||||
- extreme_impact
|
||||
- unassigned
|
||||
type: string
|
||||
Security_Entity_Analytics_API_AssetCriticalityRecord:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_CreateAssetCriticalityRecord'
|
||||
|
|
|
@ -5379,7 +5379,14 @@ paths:
|
|||
properties:
|
||||
records:
|
||||
items:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_CreateAssetCriticalityRecord'
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityRecordIdParts'
|
||||
- type: object
|
||||
properties:
|
||||
criticality_level:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityLevelsForBulkUpload'
|
||||
required:
|
||||
- criticality_level
|
||||
maxItems: 1000
|
||||
minItems: 1
|
||||
type: array
|
||||
|
@ -58682,6 +58689,15 @@ components:
|
|||
- high_impact
|
||||
- extreme_impact
|
||||
type: string
|
||||
Security_Entity_Analytics_API_AssetCriticalityLevelsForBulkUpload:
|
||||
description: The criticality level of the asset for bulk upload. The value `unassigned` is used to indicate that the criticality level is not assigned and is only used for bulk upload.
|
||||
enum:
|
||||
- low_impact
|
||||
- medium_impact
|
||||
- high_impact
|
||||
- extreme_impact
|
||||
- unassigned
|
||||
type: string
|
||||
Security_Entity_Analytics_API_AssetCriticalityRecord:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_CreateAssetCriticalityRecord'
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { z } from '@kbn/zod';
|
||||
|
||||
import { CreateAssetCriticalityRecord } from './common.gen';
|
||||
import { AssetCriticalityRecordIdParts } from './common.gen';
|
||||
|
||||
export type AssetCriticalityBulkUploadErrorItem = z.infer<
|
||||
typeof AssetCriticalityBulkUploadErrorItem
|
||||
|
@ -33,11 +33,37 @@ export const AssetCriticalityBulkUploadStats = z.object({
|
|||
total: z.number().int(),
|
||||
});
|
||||
|
||||
/**
|
||||
* The criticality level of the asset for bulk upload. The value `unassigned` is used to indicate that the criticality level is not assigned and is only used for bulk upload.
|
||||
*/
|
||||
export type AssetCriticalityLevelsForBulkUpload = z.infer<
|
||||
typeof AssetCriticalityLevelsForBulkUpload
|
||||
>;
|
||||
export const AssetCriticalityLevelsForBulkUpload = z.enum([
|
||||
'low_impact',
|
||||
'medium_impact',
|
||||
'high_impact',
|
||||
'extreme_impact',
|
||||
'unassigned',
|
||||
]);
|
||||
export type AssetCriticalityLevelsForBulkUploadEnum =
|
||||
typeof AssetCriticalityLevelsForBulkUpload.enum;
|
||||
export const AssetCriticalityLevelsForBulkUploadEnum = AssetCriticalityLevelsForBulkUpload.enum;
|
||||
|
||||
export type BulkUpsertAssetCriticalityRecordsRequestBody = z.infer<
|
||||
typeof BulkUpsertAssetCriticalityRecordsRequestBody
|
||||
>;
|
||||
export const BulkUpsertAssetCriticalityRecordsRequestBody = z.object({
|
||||
records: z.array(CreateAssetCriticalityRecord).min(1).max(1000),
|
||||
records: z
|
||||
.array(
|
||||
AssetCriticalityRecordIdParts.merge(
|
||||
z.object({
|
||||
criticality_level: AssetCriticalityLevelsForBulkUpload,
|
||||
})
|
||||
)
|
||||
)
|
||||
.min(1)
|
||||
.max(1000),
|
||||
});
|
||||
export type BulkUpsertAssetCriticalityRecordsRequestBodyInput = z.input<
|
||||
typeof BulkUpsertAssetCriticalityRecordsRequestBody
|
||||
|
|
|
@ -33,7 +33,14 @@ paths:
|
|||
minItems: 1
|
||||
maxItems: 1000
|
||||
items:
|
||||
$ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord'
|
||||
allOf:
|
||||
- $ref: './common.schema.yaml#/components/schemas/AssetCriticalityRecordIdParts'
|
||||
- type: object
|
||||
properties:
|
||||
criticality_level:
|
||||
$ref: '#/components/schemas/AssetCriticalityLevelsForBulkUpload'
|
||||
required:
|
||||
- criticality_level
|
||||
required:
|
||||
- records
|
||||
responses:
|
||||
|
@ -90,3 +97,13 @@ components:
|
|||
- successful
|
||||
- failed
|
||||
- total
|
||||
|
||||
AssetCriticalityLevelsForBulkUpload:
|
||||
type: string
|
||||
description: The criticality level of the asset for bulk upload. The value `unassigned` is used to indicate that the criticality level is not assigned and is only used for bulk upload.
|
||||
enum:
|
||||
- low_impact
|
||||
- medium_impact
|
||||
- high_impact
|
||||
- extreme_impact
|
||||
- unassigned
|
||||
|
|
|
@ -34,8 +34,18 @@ export enum CriticalityLevels {
|
|||
MEDIUM_IMPACT = 'medium_impact',
|
||||
LOW_IMPACT = 'low_impact',
|
||||
}
|
||||
export enum CriticalityLevelsForBulkUpload {
|
||||
EXTREME_IMPACT = 'extreme_impact',
|
||||
HIGH_IMPACT = 'high_impact',
|
||||
MEDIUM_IMPACT = 'medium_impact',
|
||||
LOW_IMPACT = 'low_impact',
|
||||
UNASSIGNED = 'unassigned',
|
||||
}
|
||||
|
||||
export const ValidCriticalityLevels = Object.values(CriticalityLevels);
|
||||
export const ValidCriticalityLevels = [
|
||||
...Object.values(CriticalityLevels),
|
||||
CriticalityLevelsForBulkUpload.UNASSIGNED,
|
||||
];
|
||||
|
||||
/**
|
||||
* CriticalityModifiers are used to adjust the risk score based on the criticality of the asset.
|
||||
|
|
|
@ -89,12 +89,15 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the criticality level is invalid', () => {
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', 'invalid'], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['host', 'host-1', 'unassigned_impact'],
|
||||
experimentalFeatures
|
||||
);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
expect(result.error).toMatchInlineSnapshot(
|
||||
`"Invalid criticality level \\"invalid\\", expected one of extreme_impact, high_impact, medium_impact, low_impact"`
|
||||
`"Invalid criticality level \\"unassigned_impact\\", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned"`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -108,7 +111,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
expect(result.error).toMatchInlineSnapshot(
|
||||
`"Invalid criticality level \\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...\\", expected one of extreme_impact, high_impact, medium_impact, low_impact"`
|
||||
`"Invalid criticality level \\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...\\", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned"`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -151,4 +154,16 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
it('should return the parsed row if criticality level is UNASSIGNED', () => {
|
||||
expect(
|
||||
parseAssetCriticalityCsvRow(['host', 'host-1', 'UNASSIGNED'], experimentalFeatures)
|
||||
).toEqual({
|
||||
valid: true,
|
||||
record: {
|
||||
idField: 'host.name',
|
||||
idValue: 'host-1',
|
||||
criticalityLevel: 'unassigned',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { CriticalityLevels } from './constants';
|
||||
import { ValidCriticalityLevels } from './constants';
|
||||
import { type AssetCriticalityUpsert, type CriticalityLevel } from './types';
|
||||
import { type AssetCriticalityUpsertForBulkUpload, type CriticalityLevel } from './types';
|
||||
import { EntityTypeToIdentifierField, type EntityType } from '../types';
|
||||
import { getAssetCriticalityEntityTypes } from './utils';
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
|
@ -16,7 +16,7 @@ const MAX_COLUMN_CHARS = 1000;
|
|||
|
||||
interface ValidRecord {
|
||||
valid: true;
|
||||
record: AssetCriticalityUpsert;
|
||||
record: AssetCriticalityUpsertForBulkUpload;
|
||||
}
|
||||
interface InvalidRecord {
|
||||
valid: false;
|
||||
|
|
|
@ -11,10 +11,17 @@ export type CriticalityLevel = AssetCriticalityRecord['criticality_level'];
|
|||
|
||||
export type CriticalityLevelWithUnassigned = CriticalityLevel | 'unassigned';
|
||||
|
||||
export interface AssetCriticalityUpsert {
|
||||
interface BaseAssetCriticalityUpsert {
|
||||
idField: AssetCriticalityRecord['id_field'];
|
||||
idValue: AssetCriticalityRecord['id_value'];
|
||||
}
|
||||
|
||||
export interface AssetCriticalityUpsert extends BaseAssetCriticalityUpsert {
|
||||
criticalityLevel: AssetCriticalityRecord['criticality_level'];
|
||||
}
|
||||
|
||||
export interface AssetCriticalityUpsertForBulkUpload extends BaseAssetCriticalityUpsert {
|
||||
criticalityLevel: AssetCriticalityRecord['criticality_level'] | 'unassigned';
|
||||
}
|
||||
|
||||
export * from '../../api/entity_analytics/asset_criticality';
|
||||
|
|
|
@ -161,7 +161,15 @@ paths:
|
|||
properties:
|
||||
records:
|
||||
items:
|
||||
$ref: '#/components/schemas/CreateAssetCriticalityRecord'
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AssetCriticalityRecordIdParts'
|
||||
- type: object
|
||||
properties:
|
||||
criticality_level:
|
||||
$ref: >-
|
||||
#/components/schemas/AssetCriticalityLevelsForBulkUpload
|
||||
required:
|
||||
- criticality_level
|
||||
maxItems: 1000
|
||||
minItems: 1
|
||||
type: array
|
||||
|
@ -860,6 +868,18 @@ components:
|
|||
- high_impact
|
||||
- extreme_impact
|
||||
type: string
|
||||
AssetCriticalityLevelsForBulkUpload:
|
||||
description: >-
|
||||
The criticality level of the asset for bulk upload. The value
|
||||
`unassigned` is used to indicate that the criticality level is not
|
||||
assigned and is only used for bulk upload.
|
||||
enum:
|
||||
- low_impact
|
||||
- medium_impact
|
||||
- high_impact
|
||||
- extreme_impact
|
||||
- unassigned
|
||||
type: string
|
||||
AssetCriticalityRecord:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CreateAssetCriticalityRecord'
|
||||
|
|
|
@ -161,7 +161,15 @@ paths:
|
|||
properties:
|
||||
records:
|
||||
items:
|
||||
$ref: '#/components/schemas/CreateAssetCriticalityRecord'
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AssetCriticalityRecordIdParts'
|
||||
- type: object
|
||||
properties:
|
||||
criticality_level:
|
||||
$ref: >-
|
||||
#/components/schemas/AssetCriticalityLevelsForBulkUpload
|
||||
required:
|
||||
- criticality_level
|
||||
maxItems: 1000
|
||||
minItems: 1
|
||||
type: array
|
||||
|
@ -860,6 +868,18 @@ components:
|
|||
- high_impact
|
||||
- extreme_impact
|
||||
type: string
|
||||
AssetCriticalityLevelsForBulkUpload:
|
||||
description: >-
|
||||
The criticality level of the asset for bulk upload. The value
|
||||
`unassigned` is used to indicate that the criticality level is not
|
||||
assigned and is only used for bulk upload.
|
||||
enum:
|
||||
- low_impact
|
||||
- medium_impact
|
||||
- high_impact
|
||||
- extreme_impact
|
||||
- unassigned
|
||||
type: string
|
||||
AssetCriticalityRecord:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CreateAssetCriticalityRecord'
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('validateParsedContent', () => {
|
|||
errors: [
|
||||
{
|
||||
message:
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact',
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned',
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -360,6 +360,27 @@ describe('AssetCriticalityDataClient', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns valid stats for unassigned', async () => {
|
||||
const recordsStream = [
|
||||
{ idField: 'host.name', idValue: 'host1', criticalityLevel: 'unassigned' },
|
||||
];
|
||||
|
||||
const result = await subject.bulkUpsertFromStream({
|
||||
recordsStream: Readable.from(recordsStream),
|
||||
retries: 3,
|
||||
flushBytes: 1_000,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
errors: [],
|
||||
stats: {
|
||||
failed: 0,
|
||||
successful: 1,
|
||||
total: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
|||
import type {
|
||||
BulkUpsertAssetCriticalityRecordsResponse,
|
||||
AssetCriticalityUpsert,
|
||||
AssetCriticalityUpsertForBulkUpload,
|
||||
} from '../../../../common/entity_analytics/asset_criticality/types';
|
||||
import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics';
|
||||
import { createOrUpdateIndex } from '../utils/create_or_update_index';
|
||||
|
@ -286,7 +287,9 @@ export class AssetCriticalityDataClient {
|
|||
const processedEntities = new Set<string>();
|
||||
|
||||
for await (const untypedRecord of recordsStream) {
|
||||
const record = untypedRecord as unknown as AssetCriticalityUpsert | Error;
|
||||
const record = untypedRecord as unknown as AssetCriticalityUpsert as
|
||||
| AssetCriticalityUpsertForBulkUpload
|
||||
| Error;
|
||||
|
||||
stats.total++;
|
||||
if (record instanceof Error) {
|
||||
|
@ -321,22 +324,32 @@ export class AssetCriticalityDataClient {
|
|||
flushBytes,
|
||||
retries,
|
||||
refreshOnCompletion: this.getIndex(),
|
||||
onDocument: ({ record }) => [
|
||||
{ update: { _id: createId(record) } },
|
||||
{
|
||||
doc: {
|
||||
id_field: record.idField,
|
||||
id_value: record.idValue,
|
||||
criticality_level: record.criticalityLevel,
|
||||
asset: {
|
||||
criticality: record.criticalityLevel,
|
||||
onDocument: ({ record }) => {
|
||||
const criticalityLevel =
|
||||
record.criticalityLevel === 'unassigned'
|
||||
? CRITICALITY_VALUES.DELETED
|
||||
: record.criticalityLevel;
|
||||
|
||||
return [
|
||||
{ update: { _id: createId(record) } },
|
||||
{
|
||||
doc: {
|
||||
id_field: record.idField,
|
||||
id_value: record.idValue,
|
||||
criticality_level: criticalityLevel,
|
||||
asset: {
|
||||
criticality: criticalityLevel,
|
||||
},
|
||||
...getImplicitEntityFields({
|
||||
...record,
|
||||
criticalityLevel,
|
||||
}),
|
||||
'@timestamp': new Date().toISOString(),
|
||||
},
|
||||
...getImplicitEntityFields(record),
|
||||
'@timestamp': new Date().toISOString(),
|
||||
doc_as_upsert: true,
|
||||
},
|
||||
doc_as_upsert: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
onDrop: ({ document, error }) => {
|
||||
errors.push({
|
||||
message: error?.reason || 'Unknown error',
|
||||
|
|
|
@ -13,7 +13,7 @@ import type {
|
|||
CriticalityLevel,
|
||||
} from '../../../../common/entity_analytics/asset_criticality/types';
|
||||
import { RISK_SCORING_NORMALIZATION_MAX } from '../risk_score/constants';
|
||||
import type { CriticalityValues } from './constants';
|
||||
import { type CriticalityValues } from './constants';
|
||||
|
||||
/**
|
||||
* Retrieves the criticality modifier for a given criticality level.
|
||||
|
@ -89,7 +89,9 @@ export const getImplicitEntityFields = (record: AssetCriticalityUpsertWithDelete
|
|||
const entityType = entityTypeByIdField[record.idField];
|
||||
return {
|
||||
[entityType]: {
|
||||
asset: { criticality: record.criticalityLevel },
|
||||
asset: {
|
||||
criticality: record.criticalityLevel,
|
||||
},
|
||||
name: record.idValue,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import { Transform } from 'stream';
|
||||
import type { ExperimentalFeatures } from '../../../../common';
|
||||
import type { AssetCriticalityUpsert } from '../../../../common/entity_analytics/asset_criticality/types';
|
||||
import type { AssetCriticalityUpsertForBulkUpload } from '../../../../common/entity_analytics/asset_criticality/types';
|
||||
import {
|
||||
parseAssetCriticalityCsvRow,
|
||||
isErrorResult,
|
||||
|
@ -25,7 +25,7 @@ class TransformCSVToUpsertRecords extends Transform {
|
|||
public _transform(
|
||||
chunk: string[],
|
||||
encoding: string,
|
||||
callback: (error: Error | null, data?: AssetCriticalityUpsert | Error) => void
|
||||
callback: (error: Error | null, data?: AssetCriticalityUpsertForBulkUpload | Error) => void
|
||||
) {
|
||||
try {
|
||||
const parseResult = parseAssetCriticalityCsvRow(chunk, this.experimentalFeatures);
|
||||
|
|
|
@ -104,7 +104,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
{
|
||||
index: 1,
|
||||
message:
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact',
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned',
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
|
@ -157,7 +157,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
{
|
||||
index: 2,
|
||||
message:
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact',
|
||||
'Invalid criticality level "invalid_criticality", expected one of extreme_impact, high_impact, medium_impact, low_impact, unassigned',
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue