Skip to content

Commit de8e153

Browse files
alexreal1314kibanamachinemaxcold
authored
[Cloud Security] Backport 221247 backport qualys related changes (#221302)
## Summary This PR backports changes related to Qualys VMDR added integration support that were merged to version 9.1.0. PRs backported: - #213039 - #216411 ### Identify risks Thorough sanity check need to be made before merging since a lot of conflicts were fixed manually. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Maxim Kholod <[email protected]>
1 parent dd27ee4 commit de8e153

File tree

54 files changed

+1910
-220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1910
-220
lines changed

x-pack/platform/packages/shared/kbn-cloud-security-posture/common/constants.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,16 @@ export const MISCONFIGURATION_STATUS: Record<string, string> = {
5252
PASSED: 'passed',
5353
FAILED: 'failed',
5454
};
55+
56+
export const CSP_MOMENT_FORMAT = 'MMMM D, YYYY @ HH:mm:ss.SSS';
57+
58+
// A mapping of in-development features to their status. These features should be hidden from users but can be easily
59+
// activated via a simple code change in a single location.
60+
export const INTERNAL_FEATURE_FLAGS = {
61+
showManageRulesMock: false,
62+
showFindingFlyoutEvidence: true,
63+
} as const;
64+
65+
export const DETECTION_RULE_RULES_API_CURRENT_VERSION = '2023-10-31';
66+
67+
export const FINDINGS_INDEX_PATTERN = 'logs-cloud_security_posture.findings-default*';

x-pack/platform/packages/shared/kbn-cloud-security-posture/common/schema/vulnerabilities/csp_vulnerability_finding.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ export interface CspVulnerabilityFinding {
7171
commit_time: string;
7272
};
7373
package: {
74-
version?: string;
75-
name?: string;
76-
fixed_version?: string;
74+
version?: string | string[];
75+
name?: string | string[];
76+
fixed_version?: string | string[];
7777
};
7878
data_stream: EcsDataStream;
7979
observer: EcsObserver;
@@ -86,9 +86,9 @@ export interface Vulnerability {
8686
base?: number;
8787
};
8888
cwe: string[];
89-
id: string;
89+
id: string | string[];
9090
title: string;
91-
reference: string;
91+
reference: string | string[];
9292
severity?: VulnSeverity;
9393
cvss?: {
9494
nvd: VectorScoreBase;

x-pack/platform/plugins/private/translations/translations/fr-FR.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15521,7 +15521,6 @@
1552115521
"xpack.csp.vulnerabilities.vulnerabilityOverviewTab.vulnerabilityScores": "Scores de vulnérabilité",
1552215522
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.cvsScore": "CVSS",
1552315523
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.dataSource": "Source de données",
15524-
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDate": "Date de publication",
1552515524
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDateText": "{date}",
1552615525
"xpack.csp.vulnerabilitiesByResource.severityMap.tooltipTitle": "Carte des degrés de gravité",
1552715526
"xpack.csp.vulnerability_dashboard.cspPageTemplate.pageTitle": "Gestion des vulnérabilités natives du cloud",
@@ -41993,7 +41992,6 @@
4199341992
"xpack.securitySolution.flyout.left.insights.tabLabel": "Informations exploitables",
4199441993
"xpack.securitySolution.flyout.left.insights.threatIntelligenceButtonLabel": "Threat Intelligence",
4199541994
"xpack.securitySolution.flyout.left.insights.vulnerabilitiesButtonLabel": "Vulnérabilités",
41996-
"xpack.securitySolution.flyout.left.insights.vulnerability.table.resultColumnName": "Vulnérabilité",
4199741995
"xpack.securitySolution.flyout.left.insights.vulnerability.table.ruleColumnName": "Pack",
4199841996
"xpack.securitySolution.flyout.left.insights.vulnerability.tableTitle": "Vulnérabilité",
4199941997
"xpack.securitySolution.flyout.left.investigation.noDataDescription": "Il n'existe pas de guide d'investigation pour cette règle. {documentation} pour en ajouter un.",

x-pack/platform/plugins/private/translations/translations/ja-JP.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15500,7 +15500,6 @@
1550015500
"xpack.csp.vulnerabilities.vulnerabilityOverviewTab.vulnerabilityScores": "脆弱性スコア",
1550115501
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.cvsScore": "CVSS",
1550215502
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.dataSource": "データソース",
15503-
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDate": "公開日",
1550415503
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDateText": "{date}",
1550515504
"xpack.csp.vulnerabilitiesByResource.severityMap.tooltipTitle": "重要度マップ",
1550615505
"xpack.csp.vulnerability_dashboard.cspPageTemplate.pageTitle": "Cloud Native Vulnerability Management",
@@ -41961,7 +41960,6 @@
4196141960
"xpack.securitySolution.flyout.left.insights.tabLabel": "インサイト",
4196241961
"xpack.securitySolution.flyout.left.insights.threatIntelligenceButtonLabel": "脅威インテリジェンス",
4196341962
"xpack.securitySolution.flyout.left.insights.vulnerabilitiesButtonLabel": "脆弱性",
41964-
"xpack.securitySolution.flyout.left.insights.vulnerability.table.resultColumnName": "脆弱性",
4196541963
"xpack.securitySolution.flyout.left.insights.vulnerability.table.ruleColumnName": "パッケージ",
4196641964
"xpack.securitySolution.flyout.left.insights.vulnerability.tableTitle": "脆弱性",
4196741965
"xpack.securitySolution.flyout.left.investigation.noDataDescription": "このルールに関する調査ガイドはありません。追加するには、{documentation}。",

x-pack/platform/plugins/private/translations/translations/zh-CN.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15537,7 +15537,6 @@
1553715537
"xpack.csp.vulnerabilities.vulnerabilityOverviewTab.vulnerabilityScores": "漏洞分数",
1553815538
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.cvsScore": "CVSS",
1553915539
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.dataSource": "数据源",
15540-
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDate": "发布日期",
1554115540
"xpack.csp.vulnerabilities.vulnerabilityOverviewTile.publishedDateText": "{date}",
1554215541
"xpack.csp.vulnerabilitiesByResource.severityMap.tooltipTitle": "严重性映射",
1554315542
"xpack.csp.vulnerability_dashboard.cspPageTemplate.pageTitle": "云原生漏洞管理",
@@ -42033,7 +42032,6 @@
4203342032
"xpack.securitySolution.flyout.left.insights.tabLabel": "洞见",
4203442033
"xpack.securitySolution.flyout.left.insights.threatIntelligenceButtonLabel": "威胁情报",
4203542034
"xpack.securitySolution.flyout.left.insights.vulnerabilitiesButtonLabel": "漏洞",
42036-
"xpack.securitySolution.flyout.left.insights.vulnerability.table.resultColumnName": "漏洞",
4203742035
"xpack.securitySolution.flyout.left.insights.vulnerability.table.ruleColumnName": "软件包",
4203842036
"xpack.securitySolution.flyout.left.insights.vulnerability.tableTitle": "漏洞",
4203942037
"xpack.securitySolution.flyout.left.investigation.noDataDescription": "没有适用于此规则的调查指南。{documentation}以添加一个。",

x-pack/solutions/security/packages/kbn-cloud-security-posture/public/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ export { getSeverityStatusColor, getCvsScoreColor } from './src/utils/get_vulner
1616
export { getSeverityText } from './src/utils/get_vulnerability_text';
1717
export { getVulnerabilityStats, hasVulnerabilitiesData } from './src/utils/vulnerability_helpers';
1818
export { CVSScoreBadge, SeverityStatusBadge } from './src/components/vulnerability_badges';
19+
export { getNormalizedSeverity } from './src/utils/get_normalized_severity';
20+
export { ActionableBadge, type MultiValueCellAction } from './src/components/actionable_badge';
21+
export { MultiValueCellPopover } from './src/components/multi_value_cell_popover';
22+
export { findReferenceLink } from './src/utils/find_reference_link.util';
23+
export { getVulnerabilitiesQuery } from './src/utils/findings_query_builders';
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React, { useState, useRef } from 'react';
9+
import {
10+
EuiBadge,
11+
EuiText,
12+
EuiButtonIcon,
13+
EuiFlexGroup,
14+
EuiFlexItem,
15+
useEuiTheme,
16+
EuiCopy,
17+
} from '@elastic/eui';
18+
import { i18n } from '@kbn/i18n';
19+
import { createPortal } from 'react-dom';
20+
import { css } from '@emotion/react';
21+
22+
const copyItem = i18n.translate('securitySolutionPackages.csp.actionableBadge.copy', {
23+
defaultMessage: 'Copy',
24+
});
25+
26+
export interface MultiValueCellAction {
27+
iconType: string;
28+
onClick?: () => void;
29+
ariaLabel: string;
30+
title?: string;
31+
}
32+
33+
interface ActionableBadgeProps {
34+
item: string;
35+
index: number;
36+
actions?: MultiValueCellAction[];
37+
}
38+
39+
export const ActionableBadge = ({ item, index, actions = [] }: ActionableBadgeProps) => {
40+
const [showActions, setShowActions] = useState(false);
41+
const { euiTheme } = useEuiTheme();
42+
const buttonRef = useRef<HTMLDivElement | null>(null);
43+
const iconsContainerRef = useRef<HTMLDivElement | null>(null);
44+
45+
const [buttonPosition, setButtonPosition] = useState({ bottom: 0, left: 0 });
46+
47+
const updatePosition = () => {
48+
if (buttonRef.current && iconsContainerRef.current) {
49+
const rect = buttonRef.current.getBoundingClientRect();
50+
const myElement = iconsContainerRef.current.getBoundingClientRect();
51+
52+
setButtonPosition({
53+
bottom: window.innerHeight - rect.top, // offset from the top of the badge
54+
left: rect.right - myElement.width, // align with right edge
55+
});
56+
}
57+
};
58+
59+
const buttonIconCss = css`
60+
color: ${euiTheme.colors.plainLight};
61+
inline-size: ${euiTheme.base}px;
62+
block-size: ${euiTheme.base}px;
63+
svg {
64+
width: ${euiTheme.size.m};
65+
height: ${euiTheme.size.m};
66+
`;
67+
68+
React.useEffect(() => {
69+
if (showActions) {
70+
updatePosition();
71+
}
72+
}, [showActions]);
73+
74+
const handleMouseLeave = () => {
75+
setShowActions(false);
76+
};
77+
78+
const handleMouseEnter = () => {
79+
setShowActions(true);
80+
};
81+
82+
const actionButtons = createPortal(
83+
<EuiFlexGroup
84+
ref={iconsContainerRef}
85+
gutterSize="xs"
86+
alignItems="flexEnd"
87+
onMouseEnter={handleMouseEnter}
88+
responsive={false}
89+
onMouseLeave={handleMouseLeave}
90+
css={css`
91+
position: fixed;
92+
z-index: ${euiTheme.levels.modal};
93+
background-color: ${euiTheme.colors.primary};
94+
border-radius: ${euiTheme.size.xs};
95+
padding: ${euiTheme.size.xxs};
96+
`}
97+
style={{
98+
bottom: buttonPosition.bottom,
99+
left: buttonPosition.left,
100+
}}
101+
>
102+
{actions.map((action, actionIndex) => (
103+
<EuiFlexItem grow={false} key={`${action.iconType}-${actionIndex}`}>
104+
<EuiButtonIcon
105+
css={buttonIconCss}
106+
onClick={action.onClick}
107+
iconType={action.iconType}
108+
aria-label={action.ariaLabel}
109+
title={action.title}
110+
/>
111+
</EuiFlexItem>
112+
))}
113+
<EuiFlexItem grow={false}>
114+
<EuiCopy textToCopy={item}>
115+
{(copy) => (
116+
<EuiButtonIcon
117+
onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {
118+
event.stopPropagation();
119+
copy();
120+
}}
121+
iconType="copy"
122+
css={buttonIconCss}
123+
aria-label={copyItem}
124+
title={copyItem}
125+
/>
126+
)}
127+
</EuiCopy>
128+
</EuiFlexItem>
129+
</EuiFlexGroup>,
130+
document.body
131+
);
132+
133+
return (
134+
<div
135+
ref={buttonRef}
136+
css={css`
137+
position: relative;
138+
display: inline-block;
139+
`}
140+
>
141+
{showActions && actionButtons}
142+
<EuiBadge
143+
onMouseEnter={handleMouseEnter}
144+
onMouseLeave={handleMouseLeave}
145+
color="hollow"
146+
key={`${item}-${index}`}
147+
data-test-subj={`multi-value-copy-badge-${index}`}
148+
>
149+
<EuiText
150+
css={css`
151+
text-overflow: ellipsis;
152+
`}
153+
size="m"
154+
>
155+
{item}
156+
</EuiText>
157+
</EuiBadge>
158+
</div>
159+
);
160+
};
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { act } from 'react-dom/test-utils';
10+
import { render, fireEvent, screen } from '@testing-library/react';
11+
import { MultiValueCellPopover } from './multi_value_cell_popover';
12+
import { EuiBadge, EuiText } from '@elastic/eui';
13+
import { MULTI_VALUE_CELL_FIRST_ITEM_VALUE, MULTI_VALUE_CELL_MORE_BUTTON } from '../constants';
14+
15+
const RENDER_ITEM_TEST_ID = 'item-renderer-test-id';
16+
17+
// Mock EUI theme hook
18+
jest.mock('@elastic/eui', () => ({
19+
...jest.requireActual('@elastic/eui'),
20+
useEuiTheme: () => ({ euiTheme: { size: { s: '8px' } } }),
21+
}));
22+
23+
describe('MultiValueCellPopover', () => {
24+
const mockObject = { id: '1' };
25+
const defaultRenderItem = (item: string) => (
26+
<EuiBadge data-test-subj={RENDER_ITEM_TEST_ID}>{item}</EuiBadge>
27+
);
28+
29+
it('renders single item without badge', () => {
30+
render(
31+
<MultiValueCellPopover
32+
items={['item1']}
33+
field="test"
34+
object={mockObject}
35+
renderItem={defaultRenderItem}
36+
/>
37+
);
38+
39+
expect(screen.getByTestId(MULTI_VALUE_CELL_FIRST_ITEM_VALUE)).toBeInTheDocument();
40+
expect(screen.getByTestId(MULTI_VALUE_CELL_FIRST_ITEM_VALUE)).toHaveTextContent('item1');
41+
expect(screen.queryByTestId(MULTI_VALUE_CELL_MORE_BUTTON)).not.toBeInTheDocument();
42+
});
43+
44+
it('renders multiple items with badge', () => {
45+
render(
46+
<MultiValueCellPopover
47+
items={['item1', 'item2', 'item3']}
48+
field="test"
49+
object={mockObject}
50+
renderItem={defaultRenderItem}
51+
/>
52+
);
53+
54+
expect(screen.getByTestId(MULTI_VALUE_CELL_FIRST_ITEM_VALUE)).toBeInTheDocument();
55+
expect(screen.getByTestId(MULTI_VALUE_CELL_FIRST_ITEM_VALUE)).toHaveTextContent('item1');
56+
expect(screen.getByTestId(MULTI_VALUE_CELL_MORE_BUTTON)).toBeInTheDocument();
57+
expect(screen.getByTestId(MULTI_VALUE_CELL_MORE_BUTTON)).toHaveTextContent('+ 2');
58+
});
59+
60+
it('opens popover on badge click', async () => {
61+
render(
62+
<MultiValueCellPopover
63+
items={['item1', 'item2']}
64+
field="test"
65+
object={mockObject}
66+
renderItem={defaultRenderItem}
67+
/>
68+
);
69+
70+
const badge = screen.getByText('+ 1');
71+
act(() => {
72+
fireEvent.click(badge);
73+
});
74+
75+
expect(screen.getAllByTestId(RENDER_ITEM_TEST_ID)).toHaveLength(2);
76+
});
77+
78+
it('uses custom firstItemRenderer when provided', () => {
79+
const customRenderTestId = 'custom-renderer-test-id';
80+
81+
const customRenderer = (item: string) => (
82+
<EuiText data-test-subj={customRenderTestId}>{`Custom ${item}`}</EuiText>
83+
);
84+
85+
render(
86+
<MultiValueCellPopover
87+
items={['item1']}
88+
field="test"
89+
object={mockObject}
90+
renderItem={defaultRenderItem}
91+
firstItemRenderer={customRenderer}
92+
/>
93+
);
94+
95+
expect(screen.getByTestId(customRenderTestId)).toBeInTheDocument();
96+
expect(screen.getByTestId(customRenderTestId)).toHaveTextContent('Custom item1');
97+
});
98+
});

0 commit comments

Comments
 (0)