mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Cloud Security] [Findings] [Vulnerabilities] [Alerts] - Create detection rule (#163545)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
365a8b93cf
commit
3045b1672c
18 changed files with 370 additions and 159 deletions
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// TODO: this needs to be defined in a versioned schema
|
||||
import type { EcsEvent } from '@kbn/ecs';
|
||||
import { VulnSeverity } from '../types';
|
||||
|
||||
export interface CspVulnerabilityFinding {
|
||||
'@timestamp': string;
|
||||
resource?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
event: EcsEvent;
|
||||
vulnerability: Vulnerability;
|
||||
ecs: {
|
||||
version: string;
|
||||
};
|
||||
host: {
|
||||
os: {
|
||||
name: string;
|
||||
kernel: string;
|
||||
codename: string;
|
||||
type: string;
|
||||
platform: string;
|
||||
version: string;
|
||||
family: string;
|
||||
};
|
||||
id: string;
|
||||
name: string;
|
||||
containerized: boolean;
|
||||
ip: string[];
|
||||
mac: string[];
|
||||
hostname: string;
|
||||
architecture: string;
|
||||
};
|
||||
agent: {
|
||||
ephemeral_id: string;
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
version: string;
|
||||
};
|
||||
cloud: {
|
||||
image?: {
|
||||
id: string;
|
||||
};
|
||||
provider?: string;
|
||||
instance?: {
|
||||
id: string;
|
||||
};
|
||||
machine?: {
|
||||
type: string;
|
||||
};
|
||||
region: string;
|
||||
availability_zone?: string;
|
||||
service?: {
|
||||
name: string;
|
||||
};
|
||||
account?: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
cloudbeat: {
|
||||
version: string;
|
||||
commit_sha: string;
|
||||
commit_time: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Vulnerability {
|
||||
published_date: string;
|
||||
score: {
|
||||
version: string;
|
||||
base: number;
|
||||
};
|
||||
cwe: string[];
|
||||
id: string;
|
||||
title: string;
|
||||
reference: string;
|
||||
severity: VulnSeverity;
|
||||
cvss: {
|
||||
nvd: VectorScoreBase;
|
||||
redhat?: VectorScoreBase;
|
||||
ghsa?: VectorScoreBase;
|
||||
};
|
||||
data_source: {
|
||||
ID: string;
|
||||
Name: string;
|
||||
URL: string;
|
||||
};
|
||||
enumeration: string;
|
||||
description: string;
|
||||
classification: string;
|
||||
scanner: {
|
||||
vendor: string;
|
||||
};
|
||||
package: {
|
||||
version: string;
|
||||
name: string;
|
||||
fixed_version?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface VectorScoreBase {
|
||||
V3Score?: number;
|
||||
V3Vector?: string;
|
||||
V2Score?: number;
|
||||
V2Vector?: string;
|
||||
}
|
|
@ -7,3 +7,4 @@
|
|||
|
||||
export * from './csp_rule_template_metadata';
|
||||
export * from './csp_rule_template';
|
||||
export * from './csp_vulnerability_finding';
|
||||
|
|
|
@ -38,6 +38,7 @@ interface RuleCreateProps {
|
|||
name: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
max_signals: number;
|
||||
}
|
||||
|
||||
export interface RuleResponse extends RuleCreateProps {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 type { CspVulnerabilityFinding } from '../../../common/schemas';
|
||||
|
||||
export const getVulnerabilityReferenceUrl = (
|
||||
finding: CspVulnerabilityFinding
|
||||
): string | undefined => {
|
||||
const nvdDomain = 'https://nvd';
|
||||
const nvdWebsite = `${nvdDomain}.nist.gov/vuln/detail/${finding?.vulnerability?.id}`;
|
||||
|
||||
const vulnerabilityReference = finding.vulnerability?.cvss?.nvd
|
||||
? nvdWebsite
|
||||
: finding.vulnerability?.reference;
|
||||
|
||||
return vulnerabilityReference;
|
||||
};
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import type { CspFinding } from '../../../../common/schemas/csp_finding';
|
||||
import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants';
|
||||
import {
|
||||
FINDINGS_INDEX_PATTERN,
|
||||
LATEST_FINDINGS_RETENTION_POLICY,
|
||||
} from '../../../../common/constants';
|
||||
import { createDetectionRule } from '../../../common/api/create_detection_rule';
|
||||
|
||||
const DEFAULT_RULE_RISK_SCORE = 0;
|
||||
|
@ -15,6 +18,7 @@ const DEFAULT_RULE_SEVERITY = 'low';
|
|||
const DEFAULT_RULE_ENABLED = true;
|
||||
const DEFAULT_RULE_AUTHOR = 'Elastic';
|
||||
const DEFAULT_RULE_LICENSE = 'Elastic License v2';
|
||||
const DEFAULT_MAX_ALERTS_PER_RULE = 100;
|
||||
const ALERT_SUPPRESSION_FIELD = 'resource.id';
|
||||
const ALERT_TIMESTAMP_FIELD = 'event.ingested';
|
||||
|
||||
|
@ -40,23 +44,36 @@ const convertReferencesLinksToArray = (input: string | undefined) => {
|
|||
return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, ''));
|
||||
};
|
||||
|
||||
const STATIC_RULE_TAGS = ['Elastic', 'Cloud Security'];
|
||||
const CSP_RULE_TAG = 'Cloud Security';
|
||||
const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit';
|
||||
const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: ';
|
||||
|
||||
const generateMisconfigurationsTags = (finding: CspFinding) => {
|
||||
const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE];
|
||||
|
||||
const generateFindingsTags = (finding: CspFinding) => {
|
||||
return [STATIC_RULE_TAGS]
|
||||
.concat(finding.rule.tags)
|
||||
.concat(
|
||||
finding.rule.benchmark.posture_type ? [finding.rule.benchmark.posture_type.toUpperCase()] : []
|
||||
finding.rule.benchmark.posture_type
|
||||
? [
|
||||
finding.rule.benchmark.posture_type.toUpperCase(),
|
||||
`${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${finding.rule.benchmark.posture_type.toUpperCase()}`,
|
||||
]
|
||||
: []
|
||||
)
|
||||
.concat(
|
||||
finding.rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container']
|
||||
)
|
||||
.flat();
|
||||
};
|
||||
|
||||
const generateMisconfigurationsRuleQuery = (finding: CspFinding) => {
|
||||
return `
|
||||
rule.benchmark.rule_number: "${finding.rule.benchmark.rule_number}"
|
||||
const generateFindingsRuleQuery = (finding: CspFinding) => {
|
||||
const currentTimestamp = new Date().toISOString();
|
||||
|
||||
return `rule.benchmark.rule_number: "${finding.rule.benchmark.rule_number}"
|
||||
AND rule.benchmark.id: "${finding.rule.benchmark.id}"
|
||||
AND result.evaluation: "failed"
|
||||
`;
|
||||
AND event.ingested >= "${currentTimestamp}"`;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -78,8 +95,9 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C
|
|||
severity_mapping: [],
|
||||
threat: [],
|
||||
interval: '1h',
|
||||
from: 'now-7200s',
|
||||
from: `now-${LATEST_FINDINGS_RETENTION_POLICY}`,
|
||||
to: 'now',
|
||||
max_signals: DEFAULT_MAX_ALERTS_PER_RULE,
|
||||
timestamp_override: ALERT_TIMESTAMP_FIELD,
|
||||
timestamp_override_fallback_disabled: false,
|
||||
actions: [],
|
||||
|
@ -88,12 +106,12 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C
|
|||
group_by: [ALERT_SUPPRESSION_FIELD],
|
||||
missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress,
|
||||
},
|
||||
index: [LATEST_FINDINGS_INDEX_DEFAULT_NS],
|
||||
query: generateMisconfigurationsRuleQuery(finding),
|
||||
index: [FINDINGS_INDEX_PATTERN],
|
||||
query: generateFindingsRuleQuery(finding),
|
||||
references: convertReferencesLinksToArray(finding.rule.references),
|
||||
name: finding.rule.name,
|
||||
description: finding.rule.rationale,
|
||||
tags: generateMisconfigurationsTags(finding),
|
||||
tags: generateFindingsTags(finding),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
import { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
|
||||
export const mockVulnerabilityHit: VulnerabilityRecord = {
|
||||
export const mockVulnerabilityHit: CspVulnerabilityFinding = {
|
||||
'@timestamp': '2023-03-30T10:27:35.013Z',
|
||||
resource: { name: '634yfsdg2.dkr.ecr.eu-central-1.amazon.stage', id: 'ami_12328' },
|
||||
agent: {
|
||||
|
|
|
@ -15,12 +15,12 @@ import {
|
|||
AggregationsStringRareTermsBucketKeys,
|
||||
Sort,
|
||||
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants';
|
||||
import { getSafeVulnerabilitiesQueryFilter } from '../../../../common/utils/get_safe_vulnerabilities_query_filter';
|
||||
import { useKibana } from '../../../common/hooks/use_kibana';
|
||||
import { showErrorToast } from '../../../common/utils/show_error_toast';
|
||||
import { FindingsBaseEsQuery } from '../../../common/types';
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
type LatestFindingsRequest = IKibanaSearchRequest<SearchRequest>;
|
||||
type LatestFindingsResponse = IKibanaSearchResponse<SearchResponse<any, FindingsAggs>>;
|
||||
|
||||
|
@ -60,7 +60,7 @@ export const useLatestVulnerabilities = (options: VulnerabilitiesQuery) => {
|
|||
);
|
||||
|
||||
return {
|
||||
page: hits.hits.map((hit) => hit._source!) as VulnerabilityRecord[],
|
||||
page: hits.hits.map((hit) => hit._source!) as CspVulnerabilityFinding[],
|
||||
total: number.is(hits.total) ? hits.total : 0,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -5,119 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { VulnSeverity } from '../../../common/types';
|
||||
|
||||
export interface VulnerabilityRecord {
|
||||
'@timestamp': string;
|
||||
resource?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
event: {
|
||||
type: string[];
|
||||
category: string[];
|
||||
created: string;
|
||||
id: string;
|
||||
kind: string;
|
||||
sequence: number;
|
||||
outcome: string;
|
||||
};
|
||||
vulnerability: Vulnerability;
|
||||
ecs: {
|
||||
version: string;
|
||||
};
|
||||
host: {
|
||||
os: {
|
||||
name: string;
|
||||
kernel: string;
|
||||
codename: string;
|
||||
type: string;
|
||||
platform: string;
|
||||
version: string;
|
||||
family: string;
|
||||
};
|
||||
id: string;
|
||||
name: string;
|
||||
containerized: boolean;
|
||||
ip: string[];
|
||||
mac: string[];
|
||||
hostname: string;
|
||||
architecture: string;
|
||||
};
|
||||
agent: {
|
||||
ephemeral_id: string;
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
version: string;
|
||||
};
|
||||
cloud: {
|
||||
image?: {
|
||||
id: string;
|
||||
};
|
||||
provider?: string;
|
||||
instance?: {
|
||||
id: string;
|
||||
};
|
||||
machine?: {
|
||||
type: string;
|
||||
};
|
||||
region: string;
|
||||
availability_zone?: string;
|
||||
service?: {
|
||||
name: string;
|
||||
};
|
||||
account?: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
cloudbeat: {
|
||||
version: string;
|
||||
commit_sha: string;
|
||||
commit_time: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Vulnerability {
|
||||
published_date: string;
|
||||
score: {
|
||||
version: string;
|
||||
base: number;
|
||||
};
|
||||
cwe: string[];
|
||||
id: string;
|
||||
title: string;
|
||||
reference: string;
|
||||
severity: VulnSeverity;
|
||||
cvss: {
|
||||
nvd: VectorScoreBase;
|
||||
redhat?: VectorScoreBase;
|
||||
ghsa?: VectorScoreBase;
|
||||
};
|
||||
data_source: {
|
||||
ID: string;
|
||||
Name: string;
|
||||
URL: string;
|
||||
};
|
||||
enumeration: string;
|
||||
description: string;
|
||||
classification: string;
|
||||
scanner: {
|
||||
vendor: string;
|
||||
};
|
||||
package: {
|
||||
version: string;
|
||||
name: string;
|
||||
fixed_version?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface VectorScoreBase {
|
||||
V3Score?: number;
|
||||
V3Vector?: string;
|
||||
V2Score?: number;
|
||||
V2Vector?: string;
|
||||
}
|
||||
import { VectorScoreBase, CspVulnerabilityFinding } from '../../../common/schemas';
|
||||
|
||||
export type Vendor = 'NVD' | 'Red Hat' | 'GHSA';
|
||||
|
||||
|
@ -133,7 +21,7 @@ export interface Vector {
|
|||
}
|
||||
|
||||
export interface VulnerabilitiesQueryData {
|
||||
page: VulnerabilityRecord[];
|
||||
page: CspVulnerabilityFinding[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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 { HttpSetup } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getVulnerabilityReferenceUrl } from '../../../common/utils/get_vulnerability_reference_url';
|
||||
import type { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
import {
|
||||
LATEST_VULNERABILITIES_RETENTION_POLICY,
|
||||
VULNERABILITIES_INDEX_PATTERN,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
} from '../../../../common/constants';
|
||||
import { createDetectionRule } from '../../../common/api/create_detection_rule';
|
||||
|
||||
const DEFAULT_RULE_RISK_SCORE = 0;
|
||||
const DEFAULT_RULE_SEVERITY = 'low';
|
||||
const DEFAULT_RULE_ENABLED = true;
|
||||
const DEFAULT_RULE_AUTHOR = 'Elastic';
|
||||
const DEFAULT_RULE_LICENSE = 'Elastic License v2';
|
||||
const DEFAULT_MAX_ALERTS_PER_RULE = 100;
|
||||
const ALERT_SUPPRESSION_FIELD = 'resource.id';
|
||||
const ALERT_TIMESTAMP_FIELD = 'event.ingested';
|
||||
const ALERT_SEVERITY_MAP_FIELD = 'vulnerability.severity';
|
||||
|
||||
enum RuleSeverityMapping {
|
||||
Low = 'low',
|
||||
Medium = 'medium',
|
||||
High = 'high',
|
||||
Critical = 'critical',
|
||||
}
|
||||
|
||||
enum AlertSuppressionMissingFieldsStrategy {
|
||||
// per each document a separate alert will be created
|
||||
DoNotSuppress = 'doNotSuppress',
|
||||
// only one alert will be created per suppress by bucket
|
||||
Suppress = 'suppress',
|
||||
}
|
||||
|
||||
const CSP_RULE_TAG = 'Cloud Security';
|
||||
const CNVM_RULE_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 STATIC_RULE_TAGS = [
|
||||
CSP_RULE_TAG,
|
||||
CNVM_RULE_TAG,
|
||||
CNVM_RULE_TAG_DATA_SOURCE,
|
||||
CNVM_RULE_TAG_USE_CASE,
|
||||
CNVM_RULE_TAG_OS,
|
||||
];
|
||||
|
||||
const generateVulnerabilitiesTags = (finding: CspVulnerabilityFinding) => {
|
||||
return [...STATIC_RULE_TAGS, finding.vulnerability.id];
|
||||
};
|
||||
|
||||
const getVulnerabilityRuleName = (finding: CspVulnerabilityFinding) => {
|
||||
return i18n.translate('xpack.csp.vulnerabilities.detectionRuleNamePrefix', {
|
||||
defaultMessage: 'Vulnerability: {vulnerabilityId}',
|
||||
values: {
|
||||
vulnerabilityId: finding.vulnerability.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const generateVulnerabilitiesRuleQuery = (finding: CspVulnerabilityFinding) => {
|
||||
const currentTimestamp = new Date().toISOString();
|
||||
|
||||
return `vulnerability.id: "${finding.vulnerability.id}" AND event.ingested >= "${currentTimestamp}"`;
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a detection rule from a CspVulnerabilityFinding
|
||||
*/
|
||||
export const createDetectionRuleFromVulnerabilityFinding = async (
|
||||
http: HttpSetup,
|
||||
finding: CspVulnerabilityFinding
|
||||
) => {
|
||||
const referenceUrl = getVulnerabilityReferenceUrl(finding);
|
||||
|
||||
return await createDetectionRule({
|
||||
http,
|
||||
rule: {
|
||||
type: 'query',
|
||||
language: 'kuery',
|
||||
license: DEFAULT_RULE_LICENSE,
|
||||
author: [DEFAULT_RULE_AUTHOR],
|
||||
filters: [],
|
||||
false_positives: [],
|
||||
risk_score: DEFAULT_RULE_RISK_SCORE,
|
||||
risk_score_mapping: [],
|
||||
severity: DEFAULT_RULE_SEVERITY,
|
||||
severity_mapping: [
|
||||
{
|
||||
field: ALERT_SEVERITY_MAP_FIELD,
|
||||
value: VULNERABILITIES_SEVERITY.LOW,
|
||||
operator: 'equals',
|
||||
severity: RuleSeverityMapping.Low,
|
||||
},
|
||||
{
|
||||
field: ALERT_SEVERITY_MAP_FIELD,
|
||||
value: VULNERABILITIES_SEVERITY.MEDIUM,
|
||||
operator: 'equals',
|
||||
severity: RuleSeverityMapping.Medium,
|
||||
},
|
||||
{
|
||||
field: ALERT_SEVERITY_MAP_FIELD,
|
||||
value: VULNERABILITIES_SEVERITY.HIGH,
|
||||
operator: 'equals',
|
||||
severity: RuleSeverityMapping.High,
|
||||
},
|
||||
{
|
||||
field: ALERT_SEVERITY_MAP_FIELD,
|
||||
value: VULNERABILITIES_SEVERITY.CRITICAL,
|
||||
operator: 'equals',
|
||||
severity: RuleSeverityMapping.Critical,
|
||||
},
|
||||
{
|
||||
field: ALERT_SEVERITY_MAP_FIELD,
|
||||
value: VULNERABILITIES_SEVERITY.UNKNOWN,
|
||||
operator: 'equals',
|
||||
severity: RuleSeverityMapping.Low,
|
||||
},
|
||||
],
|
||||
threat: [],
|
||||
interval: '1h',
|
||||
from: `now-${LATEST_VULNERABILITIES_RETENTION_POLICY}`,
|
||||
to: 'now',
|
||||
max_signals: DEFAULT_MAX_ALERTS_PER_RULE,
|
||||
timestamp_override: ALERT_TIMESTAMP_FIELD,
|
||||
timestamp_override_fallback_disabled: false,
|
||||
actions: [],
|
||||
enabled: DEFAULT_RULE_ENABLED,
|
||||
alert_suppression: {
|
||||
group_by: [ALERT_SUPPRESSION_FIELD],
|
||||
missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress,
|
||||
},
|
||||
index: [VULNERABILITIES_INDEX_PATTERN],
|
||||
query: generateVulnerabilitiesRuleQuery(finding),
|
||||
references: referenceUrl ? [referenceUrl] : [],
|
||||
name: getVulnerabilityRuleName(finding),
|
||||
description: finding.vulnerability.description,
|
||||
tags: generateVulnerabilitiesTags(finding),
|
||||
},
|
||||
});
|
||||
};
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { VectorScoreBase, Vector } from '../types';
|
||||
import { VectorScoreBase } from '../../../../common/schemas';
|
||||
import { Vector } from '../types';
|
||||
|
||||
export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => {
|
||||
const result: Vector[] = [];
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
import React from 'react';
|
||||
import { EuiDataGridColumn, EuiDataGridColumnCellAction, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
import { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
import { getFilters } from './get_filters';
|
||||
import { FILTER_IN, FILTER_OUT } from '../translations';
|
||||
|
||||
export const getVulnerabilitiesGridCellActions = <T extends Array<Partial<VulnerabilityRecord>>>({
|
||||
export const getVulnerabilitiesGridCellActions = <
|
||||
T extends Array<Partial<CspVulnerabilityFinding>>
|
||||
>({
|
||||
data,
|
||||
columns,
|
||||
columnGridFn,
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Routes, Route } from '@kbn/shared-ux-router';
|
|||
import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../common/constants';
|
||||
import { useCloudPostureTable } from '../../common/hooks/use_cloud_posture_table';
|
||||
import { useLatestVulnerabilities } from './hooks/use_latest_vulnerabilities';
|
||||
import type { VulnerabilityRecord, VulnerabilitiesQueryData } from './types';
|
||||
import type { VulnerabilitiesQueryData } from './types';
|
||||
import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../common/constants';
|
||||
import { ErrorCallout } from '../configurations/layout/error_callout';
|
||||
import { FindingsSearchBar } from '../configurations/layout/findings_search_bar';
|
||||
|
@ -160,9 +160,9 @@ const VulnerabilitiesDataGrid = ({
|
|||
});
|
||||
|
||||
const onOpenFlyout = useCallback(
|
||||
(vulnerabilityRow: VulnerabilityRecord) => {
|
||||
(vulnerabilityRow: VulnerabilitiesQueryData['page'][number]) => {
|
||||
const vulnerabilityIndex = data?.page.findIndex(
|
||||
(vulnerabilityRecord: VulnerabilityRecord) =>
|
||||
(vulnerabilityRecord: VulnerabilitiesQueryData['page'][number]) =>
|
||||
vulnerabilityRecord.vulnerability?.id === vulnerabilityRow.vulnerability?.id &&
|
||||
vulnerabilityRecord.resource?.id === vulnerabilityRow.resource?.id &&
|
||||
vulnerabilityRecord.vulnerability.package.name ===
|
||||
|
@ -204,7 +204,7 @@ const VulnerabilitiesDataGrid = ({
|
|||
}): React.ReactElement | null => {
|
||||
const rowIndexFromPage = rowIndex > pageSize - 1 ? rowIndex % pageSize : rowIndex;
|
||||
|
||||
const vulnerabilityRow = data?.page[rowIndexFromPage] as VulnerabilityRecord;
|
||||
const vulnerabilityRow = data?.page[rowIndexFromPage];
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedVulnerabilityIndex === rowIndex) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import type { BoolQuery } from '@kbn/es-query';
|
|||
import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../../../common/constants';
|
||||
import { useCloudPostureTable } from '../../../../common/hooks/use_cloud_posture_table';
|
||||
import { useLatestVulnerabilities } from '../../hooks/use_latest_vulnerabilities';
|
||||
import type { VulnerabilityRecord, VulnerabilitiesQueryData } from '../../types';
|
||||
import type { VulnerabilitiesQueryData } from '../../types';
|
||||
import { ErrorCallout } from '../../../configurations/layout/error_callout';
|
||||
import { FindingsSearchBar } from '../../../configurations/layout/findings_search_bar';
|
||||
import { CVSScoreBadge, SeverityStatusBadge } from '../../../../components/vulnerability_badges';
|
||||
|
@ -119,9 +119,9 @@ const ResourceVulnerabilitiesDataGrid = ({
|
|||
};
|
||||
|
||||
const onOpenFlyout = useCallback(
|
||||
(vulnerabilityRow: VulnerabilityRecord) => {
|
||||
(vulnerabilityRow: VulnerabilitiesQueryData['page'][number]) => {
|
||||
const vulnerabilityIndex = data?.page.findIndex(
|
||||
(vulnerabilityRecord: VulnerabilityRecord) =>
|
||||
(vulnerabilityRecord: VulnerabilitiesQueryData['page'][number]) =>
|
||||
vulnerabilityRecord.vulnerability?.id === vulnerabilityRow.vulnerability?.id &&
|
||||
vulnerabilityRecord.resource?.id === vulnerabilityRow.resource?.id &&
|
||||
vulnerabilityRecord.vulnerability.package.name ===
|
||||
|
@ -169,7 +169,7 @@ const ResourceVulnerabilitiesDataGrid = ({
|
|||
}): React.ReactElement | null => {
|
||||
const rowIndexFromPage = rowIndex > pageSize - 1 ? rowIndex % pageSize : rowIndex;
|
||||
|
||||
const vulnerabilityRow = data?.page[rowIndexFromPage] as VulnerabilityRecord;
|
||||
const vulnerabilityRow = data?.page[rowIndexFromPage];
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedVulnerabilityIndex === rowIndex) {
|
||||
|
|
|
@ -19,30 +19,35 @@ import {
|
|||
EuiSkeletonText,
|
||||
EuiTab,
|
||||
EuiTabs,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { css } from '@emotion/react';
|
||||
import { HttpSetup } from '@kbn/core-http-browser';
|
||||
import { TakeAction } from '../../../components/take_action';
|
||||
import { getVulnerabilityReferenceUrl } from '../../../common/utils/get_vulnerability_reference_url';
|
||||
import { truthy } from '../../../../common/utils/helpers';
|
||||
import { CspInlineDescriptionList } from '../../../components/csp_inline_description_list';
|
||||
import { VulnerabilityOverviewTab } from './vulnerability_overview_tab';
|
||||
import { VulnerabilityJsonTab } from './vulnerability_json_tab';
|
||||
import { SeverityStatusBadge } from '../../../components/vulnerability_badges';
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
import type { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
import {
|
||||
FINDINGS_VULNERABILITY_FLYOUT_DESCRIPTION_LIST,
|
||||
TAB_ID_VULNERABILITY_FLYOUT,
|
||||
} from '../test_subjects';
|
||||
import { VulnerabilityTableTab } from './vulnerability_table_tab';
|
||||
import { createDetectionRuleFromVulnerabilityFinding } from '../utils/create_detection_rule_from_vulnerability';
|
||||
|
||||
const overviewTabId = 'vuln-flyout-overview-tab';
|
||||
const tableTabId = 'vuln-flyout-table-tab';
|
||||
const jsonTabId = 'vuln-flyout-json-tab';
|
||||
|
||||
const getFlyoutDescriptionList = (
|
||||
vulnerabilityRecord: VulnerabilityRecord
|
||||
vulnerabilityRecord: CspVulnerabilityFinding
|
||||
): EuiDescriptionListProps['listItems'] =>
|
||||
[
|
||||
vulnerabilityRecord.resource?.name && {
|
||||
|
@ -80,7 +85,7 @@ export const VulnerabilityFindingFlyout = ({
|
|||
onPaginate: (pageIndex: number) => void;
|
||||
totalVulnerabilitiesCount: number;
|
||||
flyoutIndex?: number;
|
||||
vulnerabilityRecord: VulnerabilityRecord;
|
||||
vulnerabilityRecord: CspVulnerabilityFinding;
|
||||
isLoading: boolean;
|
||||
}) => {
|
||||
const [selectedTabId, setSelectedTabId] = useState(overviewTabId);
|
||||
|
@ -140,16 +145,17 @@ export const VulnerabilityFindingFlyout = ({
|
|||
() => tabs.find((obj) => obj.id === selectedTabId)?.content,
|
||||
[selectedTabId, tabs]
|
||||
);
|
||||
const nvdDomain = 'https://nvd';
|
||||
const nvdWebsite = `${nvdDomain}.nist.gov/vuln/detail/${vulnerabilityRecord?.vulnerability?.id}`;
|
||||
|
||||
const vulnerabilityReference = vulnerability?.cvss?.nvd ? nvdWebsite : vulnerability?.reference;
|
||||
|
||||
const LOADING_ARIA_LABEL = i18n.translate(
|
||||
'xpack.csp.vulnerabilities.vulnerabilityFindingFlyout.loadingAriaLabel',
|
||||
{ defaultMessage: 'Loading' }
|
||||
);
|
||||
|
||||
const vulnerabilityReference = getVulnerabilityReferenceUrl(vulnerabilityRecord);
|
||||
|
||||
const createVulnerabilityRuleFn = async (http: HttpSetup) =>
|
||||
await createDetectionRuleFromVulnerabilityFinding(http, vulnerabilityRecord);
|
||||
|
||||
return (
|
||||
<EuiFlyout onClose={closeFlyout}>
|
||||
<EuiFlyoutHeader>
|
||||
|
@ -183,9 +189,13 @@ export const VulnerabilityFindingFlyout = ({
|
|||
line-height: 32px;
|
||||
`}
|
||||
>
|
||||
<EuiLink target={'_blank'} href={vulnerabilityReference}>
|
||||
{vulnerability?.id}
|
||||
</EuiLink>
|
||||
{vulnerabilityReference ? (
|
||||
<EuiLink target="_blank" href={vulnerabilityReference}>
|
||||
{vulnerability?.id}
|
||||
</EuiLink>
|
||||
) : (
|
||||
<EuiText>{vulnerability?.id}</EuiText>
|
||||
)}
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
|
@ -220,7 +230,7 @@ export const VulnerabilityFindingFlyout = ({
|
|||
</EuiSkeletonText>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup gutterSize="none" justifyContent="flexEnd">
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPagination
|
||||
pageCount={totalVulnerabilitiesCount}
|
||||
|
@ -229,6 +239,9 @@ export const VulnerabilityFindingFlyout = ({
|
|||
compressed
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<TakeAction createRuleFn={createVulnerabilityRuleFn} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import { CodeEditor } from '@kbn/kibana-react-plugin/public';
|
||||
import React from 'react';
|
||||
import { XJsonLang } from '@kbn/monaco';
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
import { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
import { JSON_TAB_VULNERABILITY_FLYOUT } from '../test_subjects';
|
||||
interface VulnerabilityJsonTabProps {
|
||||
vulnerabilityRecord: VulnerabilityRecord;
|
||||
vulnerabilityRecord: CspVulnerabilityFinding;
|
||||
}
|
||||
export const VulnerabilityJsonTab = ({ vulnerabilityRecord }: VulnerabilityJsonTabProps) => {
|
||||
const offsetTopHeight = 188;
|
||||
|
|
|
@ -19,10 +19,11 @@ import moment from 'moment';
|
|||
import React from 'react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VectorScoreBase, Vulnerability } from '../../../../common/schemas';
|
||||
import { CspFlyoutMarkdown } from '../../configurations/findings_flyout/findings_flyout';
|
||||
import { NvdLogo } from '../../../assets/icons/nvd_logo_svg';
|
||||
import { CVSScoreBadge } from '../../../components/vulnerability_badges';
|
||||
import { CVSScoreProps, VectorScoreBase, Vendor, Vulnerability } from '../types';
|
||||
import { CVSScoreProps, Vendor } from '../types';
|
||||
import { getVectorScoreList } from '../utils/get_vector_score_list';
|
||||
import { OVERVIEW_TAB_VULNERABILITY_FLYOUT } from '../test_subjects';
|
||||
import redhatLogo from '../../../assets/icons/redhat_logo.svg';
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
import React from 'react';
|
||||
import { getFlattenedObject } from '@kbn/std';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VulnerabilityRecord } from '../types';
|
||||
import { CspVulnerabilityFinding } from '../../../../common/schemas';
|
||||
|
||||
interface FlattenedItem {
|
||||
key: string; // flattened dot notation object path for Vulnerability;
|
||||
|
@ -74,13 +74,13 @@ const columns: EuiInMemoryTableProps<FlattenedItem>['columns'] = [
|
|||
},
|
||||
];
|
||||
|
||||
const getFlattenedItems = (vulnerabilityRecord: VulnerabilityRecord) =>
|
||||
const getFlattenedItems = (vulnerabilityRecord: CspVulnerabilityFinding) =>
|
||||
Object.entries(getFlattenedObject(vulnerabilityRecord)).map(([key, value]) => ({ key, value }));
|
||||
|
||||
export const VulnerabilityTableTab = ({
|
||||
vulnerabilityRecord,
|
||||
}: {
|
||||
vulnerabilityRecord: VulnerabilityRecord;
|
||||
vulnerabilityRecord: CspVulnerabilityFinding;
|
||||
}) => (
|
||||
<EuiInMemoryTable
|
||||
items={getFlattenedItems(vulnerabilityRecord)}
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
"@kbn/shared-ux-router",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/share-plugin",
|
||||
"@kbn/core-http-server"
|
||||
"@kbn/core-http-server",
|
||||
"@kbn/core-http-browser"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue