diff --git a/x-pack/packages/kbn-cloud-security-posture-common/index.ts b/x-pack/packages/kbn-cloud-security-posture-common/index.ts index e01401f37ef23..d5ee781c39b20 100644 --- a/x-pack/packages/kbn-cloud-security-posture-common/index.ts +++ b/x-pack/packages/kbn-cloud-security-posture-common/index.ts @@ -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'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_table.tsx index 09c2989c1eb23..e8dde902a9f46 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_table.tsx @@ -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 ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.test.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.test.ts new file mode 100644 index 0000000000000..209ec81168271 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.test.ts @@ -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}"` + ); + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.ts index a09f9130836b2..b723c60f9ee3d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/create_detection_rule_from_vulnerability.ts @@ -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, }, }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_detection_rule_counter.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_detection_rule_counter.tsx index 1c726a450655b..c4680177f72c0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_detection_rule_counter.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_detection_rule_counter.tsx @@ -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 ; }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx index 8c7e3341424d9..1fe61e97506ef 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx @@ -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 (