[8.x] [Cloud Security] Fix vulnerability detection rule creation logic (#195291) (#195596)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Cloud Security] Fix vulnerability detection rule creation logic
(#195291)](https://github.com/elastic/kibana/pull/195291)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Jordan","email":"51442161+JordanSh@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-09T13:18:33Z","message":"[Cloud
Security] Fix vulnerability detection rule creation logic
(#195291)","sha":"fbf3f8b8b24575bd9fdc10e05ed0e5032a1a4340","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Cloud
Security","backport:prev-minor"],"title":"[Cloud Security] Fix
vulnerability detection rule creation
logic","number":195291,"url":"https://github.com/elastic/kibana/pull/195291","mergeCommit":{"message":"[Cloud
Security] Fix vulnerability detection rule creation logic
(#195291)","sha":"fbf3f8b8b24575bd9fdc10e05ed0e5032a1a4340"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195291","number":195291,"mergeCommit":{"message":"[Cloud
Security] Fix vulnerability detection rule creation logic
(#195291)","sha":"fbf3f8b8b24575bd9fdc10e05ed0e5032a1a4340"}}]}]
BACKPORT-->

Co-authored-by: Jordan <51442161+JordanSh@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-10-10 02:21:01 +11:00 committed by GitHub
parent 4de542f2c4
commit 0556cfc88f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 142 additions and 44 deletions

View file

@ -18,7 +18,10 @@ export type {
CspSetupStatus,
} from './types/status';
export type { CspFinding, CspFindingResult } from './types/findings';
export type { CspVulnerabilityFinding } from './schema/vulnerabilities/csp_vulnerability_finding';
export type {
CspVulnerabilityFinding,
Vulnerability,
} from './schema/vulnerabilities/csp_vulnerability_finding';
export type { BenchmarksCisId } from './types/benchmark';
export type { VulnSeverity } from './types/vulnerabilities';
export * from './constants';

View file

@ -108,11 +108,11 @@ export const LatestVulnerabilitiesTable = ({
});
const createVulnerabilityRuleFn = (rowIndex: number) => {
const finding = getCspVulnerabilityFinding(rows[rowIndex].raw._source);
if (!finding) return;
const vulnerabilityFinding = getCspVulnerabilityFinding(rows[rowIndex].raw._source);
if (!vulnerabilityFinding) return;
return async (http: HttpSetup) =>
createDetectionRuleFromVulnerabilityFinding(http, finding.vulnerability);
createDetectionRuleFromVulnerabilityFinding(http, vulnerabilityFinding);
};
return (

View file

@ -0,0 +1,98 @@
/*
* 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 {
getVulnerabilityTags,
getVulnerabilityRuleName,
generateVulnerabilitiesRuleQuery,
} from './create_detection_rule_from_vulnerability';
import { CspVulnerabilityFinding, Vulnerability } from '@kbn/cloud-security-posture-common';
import { isNativeCspFinding } from '../../../common/utils/is_native_csp_finding';
// Mocking the isNativeCspFinding function
jest.mock('../../../common/utils/is_native_csp_finding', () => ({
isNativeCspFinding: jest.fn(),
}));
describe('CreateDetectionRuleFromVulnerability', () => {
describe('getVulnerabilityTags', () => {
it('should return tags with CSP_RULE_TAG and vulnerability id', () => {
const mockVulnerability = {
vulnerability: { id: 'CVE-2024-00001' },
observer: undefined,
data_stream: undefined,
} as unknown as CspVulnerabilityFinding;
(isNativeCspFinding as jest.Mock).mockReturnValue(false);
const tags = getVulnerabilityTags(mockVulnerability);
expect(tags).toEqual(['Cloud Security', 'CVE-2024-00001']);
});
it('should include vendor tag if available', () => {
const mockVulnerability = {
vulnerability: { id: 'CVE-2024-00002' },
observer: { vendor: 'Wiz' },
data_stream: undefined,
} as unknown as CspVulnerabilityFinding;
(isNativeCspFinding as jest.Mock).mockReturnValue(false);
const tags = getVulnerabilityTags(mockVulnerability);
expect(tags).toEqual(['Cloud Security', 'CVE-2024-00002', 'Wiz']);
});
it('should include CNVM tags for native findings', () => {
const mockVulnerability = {
vulnerability: { id: 'CVE-2024-00003' },
observer: undefined,
data_stream: undefined,
} as unknown as CspVulnerabilityFinding;
(isNativeCspFinding as jest.Mock).mockReturnValue(true);
const tags = getVulnerabilityTags(mockVulnerability);
expect(tags).toEqual([
'Cloud Security',
'CNVM',
'Data Source: Cloud Native Vulnerability Management',
'Use Case: Vulnerability',
'OS: Linux',
'CVE-2024-00003',
]);
});
});
describe('getVulnerabilityRuleName', () => {
it('should return correct rule name for a vulnerability', () => {
const mockVulnerability = {
id: 'CVE-2024-00004',
description: '',
reference: '',
} as Vulnerability;
const ruleName = getVulnerabilityRuleName(mockVulnerability);
expect(ruleName).toEqual('Vulnerability: CVE-2024-00004');
});
});
describe('generateVulnerabilitiesRuleQuery', () => {
it('should generate correct query for a vulnerability', () => {
const mockVulnerability = {
id: 'CVE-2024-00005',
description: '',
reference: '',
} as Vulnerability;
const currentTimestamp = new Date().toISOString();
const query = generateVulnerabilitiesRuleQuery(mockVulnerability);
expect(query).toEqual(
`vulnerability.id: "CVE-2024-00005" AND event.ingested >= "${currentTimestamp}"`
);
});
});
});

View file

@ -8,10 +8,12 @@
import { HttpSetup } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import {
CspVulnerabilityFinding,
LATEST_VULNERABILITIES_RETENTION_POLICY,
VULNERABILITIES_SEVERITY,
} from '@kbn/cloud-security-posture-common';
import type { Vulnerability } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import { isNativeCspFinding } from '../../../common/utils/is_native_csp_finding';
import { VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants';
import { createDetectionRule } from '../../../common/api/create_detection_rule';
@ -42,15 +44,7 @@ enum AlertSuppressionMissingFieldsStrategy {
Suppress = 'suppress',
}
const CSP_RULE_TAG = 'Cloud Security';
const STATIC_RULE_TAGS = [CSP_RULE_TAG];
const generateVulnerabilitiesTags = (tags?: string[]) => {
return [...STATIC_RULE_TAGS, ...(!!tags?.length ? tags : [])];
};
const getVulnerabilityRuleName = (vulnerability: Vulnerability) => {
export const getVulnerabilityRuleName = (vulnerability: Vulnerability) => {
return i18n.translate('xpack.csp.vulnerabilities.detectionRuleNamePrefix', {
defaultMessage: 'Vulnerability: {vulnerabilityId}',
values: {
@ -59,20 +53,42 @@ const getVulnerabilityRuleName = (vulnerability: Vulnerability) => {
});
};
const generateVulnerabilitiesRuleQuery = (vulnerability: Vulnerability) => {
export const generateVulnerabilitiesRuleQuery = (vulnerability: Vulnerability) => {
const currentTimestamp = new Date().toISOString();
return `vulnerability.id: "${vulnerability.id}" AND event.ingested >= "${currentTimestamp}"`;
};
const CSP_RULE_TAG = 'Cloud Security';
const CNVM_TAG = 'CNVM';
const CNVM_RULE_TAG_DATA_SOURCE = 'Data Source: Cloud Native Vulnerability Management';
const CNVM_RULE_TAG_USE_CASE = 'Use Case: Vulnerability';
const CNVM_RULE_TAG_OS = 'OS: Linux';
export const getVulnerabilityTags = (vulnerabilityFinding: CspVulnerabilityFinding) => {
let tags = [vulnerabilityFinding.vulnerability.id];
const vendor =
vulnerabilityFinding.observer?.vendor || vulnerabilityFinding?.data_stream?.dataset;
if (isNativeCspFinding(vulnerabilityFinding)) {
tags = [CNVM_TAG, CNVM_RULE_TAG_DATA_SOURCE, CNVM_RULE_TAG_USE_CASE, CNVM_RULE_TAG_OS, ...tags];
} else if (!!vendor) {
tags.push(vendor);
}
return [CSP_RULE_TAG, ...tags];
};
/*
* Creates a detection rule from a Vulnerability
*/
export const createDetectionRuleFromVulnerabilityFinding = async (
http: HttpSetup,
vulnerability: Vulnerability,
tags?: string[]
vulnerabilityFinding: CspVulnerabilityFinding
) => {
const tags = getVulnerabilityTags(vulnerabilityFinding);
const vulnerability = vulnerabilityFinding.vulnerability;
return await createDetectionRule({
http,
rule: {
@ -135,7 +151,7 @@ export const createDetectionRuleFromVulnerabilityFinding = async (
references: vulnerability.reference ? [vulnerability.reference] : [],
name: getVulnerabilityRuleName(vulnerability),
description: vulnerability.description,
tags: generateVulnerabilitiesTags(tags),
tags,
investigation_fields: DEFAULT_INVESTIGATION_FIELDS,
},
});

View file

@ -8,40 +8,21 @@
import React from 'react';
import type { HttpSetup } from '@kbn/core/public';
import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import { isNativeCspFinding } from '../../../common/utils/is_native_csp_finding';
import { DetectionRuleCounter } from '../../../components/detection_rule_counter';
import { createDetectionRuleFromVulnerabilityFinding } from '../utils/create_detection_rule_from_vulnerability';
const CNVM_TAG = 'CNVM';
const CNVM_RULE_TAG_DATA_SOURCE = 'Data Source: Cloud Native Vulnerability Management';
const CNVM_RULE_TAG_USE_CASE = 'Use Case: Vulnerability';
const CNVM_RULE_TAG_OS = 'OS: Linux';
const getTags = (vulnerabilityRecord: CspVulnerabilityFinding) => {
let tags = [vulnerabilityRecord.vulnerability.id];
const vendor = vulnerabilityRecord.observer?.vendor || vulnerabilityRecord?.data_stream?.dataset;
if (isNativeCspFinding(vulnerabilityRecord)) {
tags = [CNVM_TAG, CNVM_RULE_TAG_DATA_SOURCE, CNVM_RULE_TAG_USE_CASE, CNVM_RULE_TAG_OS, ...tags];
} else if (!!vendor) {
tags.push(vendor);
}
return tags;
};
import {
createDetectionRuleFromVulnerabilityFinding,
getVulnerabilityTags,
} from '../utils/create_detection_rule_from_vulnerability';
export const VulnerabilityDetectionRuleCounter = ({
vulnerabilityRecord,
}: {
vulnerabilityRecord: CspVulnerabilityFinding;
}) => {
const tags = getTags(vulnerabilityRecord);
const tags = getVulnerabilityTags(vulnerabilityRecord);
const createVulnerabilityRuleFn = async (http: HttpSetup) =>
await createDetectionRuleFromVulnerabilityFinding(
http,
vulnerabilityRecord.vulnerability,
tags
);
await createDetectionRuleFromVulnerabilityFinding(http, vulnerabilityRecord);
return <DetectionRuleCounter tags={tags} createRuleFn={createVulnerabilityRuleFn} />;
};

View file

@ -162,7 +162,7 @@ export const VulnerabilityFindingFlyout = ({
const vulnerabilityReference = vulnerability?.reference;
const createVulnerabilityRuleFn = async (http: HttpSetup) =>
await createDetectionRuleFromVulnerabilityFinding(http, vulnerabilityRecord.vulnerability);
await createDetectionRuleFromVulnerabilityFinding(http, vulnerabilityRecord);
return (
<EuiFlyout onClose={closeFlyout}>