[8.x] [Cloud Security] Fixed failing FTR (#199683) (#200530)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Cloud Security] Fixed failing FTR
(#199683)](https://github.com/elastic/kibana/pull/199683)

<!--- Backport version: 8.9.8 -->

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

<!--BACKPORT [{"author":{"name":"Kfir
Peled","email":"61654899+kfirpeled@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-11-13T01:43:58Z","message":"[Cloud
Security] Fixed failing FTR (#199683)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/kibana/issues/198632\r\n\r\nFTR failed due to
the usage of relative start and end when querying for\r\nthe
graph.\r\n\r\nTogether with @tinnytintin10 we decided it is safe to
assume the time to\r\nquery would be 30 minutes before and after the
alert.\r\n\r\n**Some enhancements and fixes:**\r\n- Disabled re-fetch
/graph API when window focus returned\r\n\r\n### Checklist\r\n\r\nDelete
any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"5add2c82db7d26b96b66e078606fbdc428fefc28","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport
missing","v9.0.0","backport:prev-minor"],"number":199683,"url":"https://github.com/elastic/kibana/pull/199683","mergeCommit":{"message":"[Cloud
Security] Fixed failing FTR (#199683)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/kibana/issues/198632\r\n\r\nFTR failed due to
the usage of relative start and end when querying for\r\nthe
graph.\r\n\r\nTogether with @tinnytintin10 we decided it is safe to
assume the time to\r\nquery would be 30 minutes before and after the
alert.\r\n\r\n**Some enhancements and fixes:**\r\n- Disabled re-fetch
/graph API when window focus returned\r\n\r\n### Checklist\r\n\r\nDelete
any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"5add2c82db7d26b96b66e078606fbdc428fefc28"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/199683","number":199683,"mergeCommit":{"message":"[Cloud
Security] Fixed failing FTR (#199683)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/kibana/issues/198632\r\n\r\nFTR failed due to
the usage of relative start and end when querying for\r\nthe
graph.\r\n\r\nTogether with @tinnytintin10 we decided it is safe to
assume the time to\r\nquery would be 30 minutes before and after the
alert.\r\n\r\n**Some enhancements and fixes:**\r\n- Disabled re-fetch
/graph API when window focus returned\r\n\r\n### Checklist\r\n\r\nDelete
any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests
changed","sha":"5add2c82db7d26b96b66e078606fbdc428fefc28"}}]}]
BACKPORT-->
This commit is contained in:
Kfir Peled 2024-11-18 13:58:38 +00:00 committed by GitHub
parent 480e4999cc
commit 519d46209b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 169 additions and 80 deletions

View file

@ -28,15 +28,6 @@ jest.mock('../hooks/use_fetch_graph_data', () => ({
}));
const mockUseFetchGraphData = useFetchGraphData as jest.Mock;
const mockUseUiSetting = jest.fn().mockReturnValue([false]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
const mockGraph = () => <div data-test-subj={GRAPH_PREVIEW_TEST_ID} />;
jest.mock('@kbn/cloud-security-posture-graph', () => {
@ -64,7 +55,11 @@ describe('<GraphPreviewContainer />', () => {
data: { nodes: [], edges: [] },
});
const timestamp = new Date().toISOString();
(useGraphPreview as jest.Mock).mockReturnValue({
timestamp,
eventIds: [],
isAuditLog: true,
});
@ -87,9 +82,23 @@ describe('<GraphPreviewContainer />', () => {
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(mockUseFetchGraphData).toHaveBeenCalled();
expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({
req: {
query: {
eventIds: [],
start: `${timestamp}||-30m`,
end: `${timestamp}||+30m`,
},
},
options: {
enabled: true,
refetchOnWindowFocus: false,
},
});
});
it('should render error message and text in header', () => {
it('should not render when graph data is not available', () => {
mockUseFetchGraphData.mockReturnValue({
isLoading: false,
isError: false,
@ -100,10 +109,10 @@ describe('<GraphPreviewContainer />', () => {
isAuditLog: false,
});
const { getByTestId } = renderGraphPreview();
const { queryByTestId } = renderGraphPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
});

View file

@ -14,57 +14,60 @@ import { useFetchGraphData } from '../hooks/use_fetch_graph_data';
import { useGraphPreview } from '../hooks/use_graph_preview';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
const DEFAULT_FROM = 'now-60d/d';
const DEFAULT_TO = 'now/d';
/**
* Graph preview under Overview, Visualizations. It shows a graph representation of entities.
*/
export const GraphPreviewContainer: React.FC = () => {
const { dataAsNestedObject, getFieldsData } = useDocumentDetailsContext();
const { eventIds } = useGraphPreview({
const {
eventIds,
timestamp = new Date().toISOString(),
isAuditLog,
} = useGraphPreview({
getFieldsData,
ecsData: dataAsNestedObject,
});
// TODO: default start and end might not capture the original event
const graphFetchQuery = useFetchGraphData({
const { isLoading, isError, data } = useFetchGraphData({
req: {
query: {
eventIds,
start: DEFAULT_FROM,
end: DEFAULT_TO,
start: `${timestamp}||-30m`,
end: `${timestamp}||+30m`,
},
},
options: {
enabled: isAuditLog,
refetchOnWindowFocus: false,
},
});
return (
<ExpandablePanel
header={{
title: (
<FormattedMessage
id="xpack.securitySolution.flyout.right.visualizations.graphPreview.graphPreviewTitle"
defaultMessage="Graph preview"
/>
),
iconType: 'indexMapping',
}}
data-test-subj={GRAPH_PREVIEW_TEST_ID}
content={
!graphFetchQuery.isLoading && !graphFetchQuery.isError
? {
paddingSize: 'none',
}
: undefined
}
>
<GraphPreview
isLoading={graphFetchQuery.isLoading}
isError={graphFetchQuery.isError}
data={graphFetchQuery.data}
/>
</ExpandablePanel>
isAuditLog && (
<ExpandablePanel
header={{
title: (
<FormattedMessage
id="xpack.securitySolution.flyout.right.visualizations.graphPreview.graphPreviewTitle"
defaultMessage="Graph preview"
/>
),
iconType: 'indexMapping',
}}
data-test-subj={GRAPH_PREVIEW_TEST_ID}
content={
!isLoading && !isError
? {
paddingSize: 'none',
}
: undefined
}
>
<GraphPreview isLoading={isLoading} isError={isError} data={data} />
</ExpandablePanel>
)
);
};

View file

@ -18,7 +18,7 @@ describe('useGraphPreview', () => {
it(`should return false when missing actor`, () => {
const getFieldsData: GetFieldsData = (field: string) => {
if (field === 'kibana.alert.original_event.id') {
return field;
return 'eventId';
}
return mockFieldData[field];
};
@ -35,7 +35,12 @@ describe('useGraphPreview', () => {
},
});
expect(hookResult.result.current.isAuditLog).toEqual(false);
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(false);
expect(timestamp).toEqual(mockFieldData['@timestamp'][0]);
expect(eventIds).toEqual(['eventId']);
expect(actorIds).toEqual([]);
expect(action).toEqual(['action']);
});
it(`should return false when missing event.action`, () => {
@ -57,7 +62,12 @@ describe('useGraphPreview', () => {
},
});
expect(hookResult.result.current.isAuditLog).toEqual(false);
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(false);
expect(timestamp).toEqual(mockFieldData['@timestamp'][0]);
expect(eventIds).toEqual(['eventId']);
expect(actorIds).toEqual(['actorId']);
expect(action).toEqual(undefined);
});
it(`should return false when missing original_event.id`, () => {
@ -80,7 +90,45 @@ describe('useGraphPreview', () => {
},
});
expect(hookResult.result.current.isAuditLog).toEqual(false);
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(false);
expect(timestamp).toEqual(mockFieldData['@timestamp'][0]);
expect(eventIds).toEqual([]);
expect(actorIds).toEqual(['actorId']);
expect(action).toEqual(['action']);
});
it(`should return false when timestamp is missing`, () => {
const getFieldsData: GetFieldsData = (field: string) => {
if (field === '@timestamp') {
return;
} else if (field === 'kibana.alert.original_event.id') {
return 'eventId';
} else if (field === 'actor.entity.id') {
return 'actorId';
}
return mockFieldData[field];
};
hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), {
initialProps: {
getFieldsData,
ecsData: {
_id: 'id',
event: {
action: ['action'],
},
},
},
});
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(false);
expect(timestamp).toEqual(null);
expect(eventIds).toEqual(['eventId']);
expect(actorIds).toEqual(['actorId']);
expect(action).toEqual(['action']);
});
it(`should return true when alert is has graph preview`, () => {
@ -106,7 +154,12 @@ describe('useGraphPreview', () => {
},
});
expect(hookResult.result.current.isAuditLog).toEqual(true);
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(true);
expect(timestamp).toEqual(mockFieldData['@timestamp'][0]);
expect(eventIds).toEqual(['eventId']);
expect(actorIds).toEqual(['actorId']);
expect(action).toEqual(['action']);
});
it(`should return true when alert is has graph preview with multiple values`, () => {
@ -132,6 +185,11 @@ describe('useGraphPreview', () => {
},
});
expect(hookResult.result.current.isAuditLog).toEqual(true);
const { isAuditLog, timestamp, eventIds, actorIds, action } = hookResult.result.current;
expect(isAuditLog).toEqual(true);
expect(timestamp).toEqual(mockFieldData['@timestamp'][0]);
expect(eventIds).toEqual(['id1', 'id2']);
expect(actorIds).toEqual(['actorId1', 'actorId2']);
expect(action).toEqual(['action1', 'action2']);
});
});

View file

@ -8,7 +8,7 @@
import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
import { get } from 'lodash/fp';
import type { GetFieldsData } from '../../shared/hooks/use_get_fields_data';
import { getFieldArray } from '../../shared/utils';
import { getField, getFieldArray } from '../../shared/utils';
export interface UseGraphPreviewParams {
/**
@ -25,6 +25,11 @@ export interface UseGraphPreviewParams {
* Interface for the result of the useGraphPreview hook
*/
export interface UseGraphPreviewResult {
/**
* The timestamp of the event
*/
timestamp: string | null;
/**
* Array of event IDs associated with the alert
*/
@ -38,7 +43,7 @@ export interface UseGraphPreviewResult {
/**
* Action associated with the event
*/
action: string | undefined;
action?: string[];
/**
* Boolean indicating if the event is an audit log (contains event ids, actor ids and action)
@ -53,13 +58,15 @@ export const useGraphPreview = ({
getFieldsData,
ecsData,
}: UseGraphPreviewParams): UseGraphPreviewResult => {
const timestamp = getField(getFieldsData('@timestamp'));
const originalEventId = getFieldsData('kibana.alert.original_event.id');
const eventId = getFieldsData('event.id');
const eventIds = originalEventId ? getFieldArray(originalEventId) : getFieldArray(eventId);
const actorIds = getFieldArray(getFieldsData('actor.entity.id'));
const action = get(['event', 'action'], ecsData);
const isAuditLog = actorIds.length > 0 && action?.length > 0 && eventIds.length > 0;
const action: string[] | undefined = get(['event', 'action'], ecsData);
const isAuditLog =
Boolean(timestamp) && actorIds.length > 0 && Boolean(action?.length) && eventIds.length > 0;
return { eventIds, actorIds, action, isAuditLog };
return { timestamp, eventIds, actorIds, action, isAuditLog };
};

View file

@ -399,7 +399,7 @@ export default function (providerContext: FtrProviderContext) {
});
});
it('Should filter unknown targets', async () => {
it('should filter unknown targets', async () => {
const response = await postGraph(supertest, {
query: {
eventIds: [],
@ -424,7 +424,7 @@ export default function (providerContext: FtrProviderContext) {
expect(response.body).not.to.have.property('messages');
});
it('Should return unknown targets', async () => {
it('should return unknown targets', async () => {
const response = await postGraph(supertest, {
showUnknownTarget: true,
query: {
@ -450,7 +450,7 @@ export default function (providerContext: FtrProviderContext) {
expect(response.body).not.to.have.property('messages');
});
it('Should limit number of nodes', async () => {
it('should limit number of nodes', async () => {
const response = await postGraph(supertest, {
nodesLimit: 1,
query: {
@ -476,6 +476,20 @@ export default function (providerContext: FtrProviderContext) {
expect(response.body).to.have.property('messages').length(1);
expect(response.body.messages[0]).equal(ApiMessageCode.ReachedNodesLimit);
});
it('should support date math', async () => {
const response = await postGraph(supertest, {
query: {
eventIds: ['kabcd1234efgh5678'],
start: '2024-09-01T12:30:00.000Z||-30m',
end: '2024-09-01T12:30:00.000Z||+30m',
},
}).expect(result(200));
expect(response.body).to.have.property('nodes').length(3);
expect(response.body).to.have.property('edges').length(2);
expect(response.body).not.to.have.property('messages');
});
});
});
}

View file

@ -4,7 +4,7 @@
"id": "589e086d7ceec7d4b353340578bd607e96fbac7eab9e2926f110990be15122f1",
"index": ".internal.alerts-security.alerts-default-000001",
"source": {
"@timestamp": "2024-09-01T20:44:02.109Z",
"@timestamp": "2024-09-01T12:44:02.109Z",
"actor": {
"entity": {
"id": "admin@example.com"
@ -34,7 +34,7 @@
],
"dataset": "gcp.audit",
"id": "kabcd1234efgh5678",
"ingested": "2024-09-01T20:40:17Z",
"ingested": "2024-09-01T12:40:17Z",
"module": "gcp",
"outcome": "success",
"provider": "activity",
@ -95,8 +95,8 @@
}
],
"kibana.alert.depth": 1,
"kibana.alert.intended_timestamp": "2024-09-01T20:44:02.117Z",
"kibana.alert.last_detected": "2024-09-01T20:44:02.117Z",
"kibana.alert.intended_timestamp": "2024-09-01T12:44:02.117Z",
"kibana.alert.last_detected": "2024-09-01T12:44:02.117Z",
"kibana.alert.original_event.action": "google.iam.admin.v1.CreateRole",
"kibana.alert.original_event.agent_id_status": "missing",
"kibana.alert.original_event.category": [
@ -106,7 +106,7 @@
],
"kibana.alert.original_event.dataset": "gcp.audit",
"kibana.alert.original_event.id": "kabcd1234efgh5678",
"kibana.alert.original_event.ingested": "2024-09-01T20:40:17Z",
"kibana.alert.original_event.ingested": "2024-09-01T12:40:17Z",
"kibana.alert.original_event.kind": "event",
"kibana.alert.original_event.module": "gcp",
"kibana.alert.original_event.outcome": "success",
@ -126,13 +126,13 @@
],
"kibana.alert.rule.category": "Custom Query Rule",
"kibana.alert.rule.consumer": "siem",
"kibana.alert.rule.created_at": "2024-09-01T20:38:49.650Z",
"kibana.alert.rule.created_at": "2024-09-01T12:38:49.650Z",
"kibana.alert.rule.created_by": "elastic",
"kibana.alert.rule.description": "Identifies an Identity and Access Management (IAM) custom role creation in Google Cloud Platform (GCP). Custom roles are user-defined, and allow for the bundling of one or more supported permissions to meet specific needs. Custom roles will not be updated automatically and could lead to privilege creep if not carefully scrutinized.",
"kibana.alert.rule.enabled": true,
"kibana.alert.rule.exceptions_list": [
],
"kibana.alert.rule.execution.timestamp": "2024-09-01T20:44:02.117Z",
"kibana.alert.rule.execution.timestamp": "2024-09-01T12:44:02.117Z",
"kibana.alert.rule.execution.uuid": "a440f349-1900-4087-b507-f2b98c6cfa79",
"kibana.alert.rule.false_positives": [
"Custom role creations may be done by a system or network administrator. Verify whether the user email, resource name, and/or hostname should be making changes in your environment. Role creations by unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule."
@ -300,12 +300,12 @@
"kibana.alert.rule.timestamp_override": "event.ingested",
"kibana.alert.rule.to": "now",
"kibana.alert.rule.type": "query",
"kibana.alert.rule.updated_at": "2024-09-01T20:39:00.099Z",
"kibana.alert.rule.updated_at": "2024-09-01T12:39:00.099Z",
"kibana.alert.rule.updated_by": "elastic",
"kibana.alert.rule.uuid": "c6f64115-5941-46ef-bfa3-61a4ecb4f3ba",
"kibana.alert.rule.version": 104,
"kibana.alert.severity": "medium",
"kibana.alert.start": "2024-09-01T20:44:02.117Z",
"kibana.alert.start": "2024-09-01T12:44:02.117Z",
"kibana.alert.status": "active",
"kibana.alert.uuid": "589e086d7ceec7d4b353340578bd607e96fbac7eab9e2926f110990be15122f1",
"kibana.alert.workflow_assignee_ids": [
@ -361,7 +361,7 @@
"id": "838ea37ab43ab7d2754d007fbe8191be53d7d637bea62f6189f8db1503c0e250",
"index": ".internal.alerts-security.alerts-default-000001",
"source": {
"@timestamp": "2024-09-01T20:39:03.646Z",
"@timestamp": "2024-09-01T12:39:03.646Z",
"actor": {
"entity": {
"id": "admin@example.com"
@ -391,7 +391,7 @@
],
"dataset": "gcp.audit",
"id": "kabcd1234efgh5678",
"ingested": "2024-09-01T20:38:13Z",
"ingested": "2024-09-01T12:38:13Z",
"module": "gcp",
"outcome": "success",
"provider": "activity",
@ -452,8 +452,8 @@
}
],
"kibana.alert.depth": 1,
"kibana.alert.intended_timestamp": "2024-09-01T20:39:03.657Z",
"kibana.alert.last_detected": "2024-09-01T20:39:03.657Z",
"kibana.alert.intended_timestamp": "2024-09-01T12:39:03.657Z",
"kibana.alert.last_detected": "2024-09-01T12:39:03.657Z",
"kibana.alert.original_event.action": "google.iam.admin.v1.CreateRole",
"kibana.alert.original_event.agent_id_status": "missing",
"kibana.alert.original_event.category": [
@ -463,7 +463,7 @@
],
"kibana.alert.original_event.dataset": "gcp.audit",
"kibana.alert.original_event.id": "kabcd1234efgh5678",
"kibana.alert.original_event.ingested": "2024-09-01T20:38:13Z",
"kibana.alert.original_event.ingested": "2024-09-01T12:38:13Z",
"kibana.alert.original_event.kind": "event",
"kibana.alert.original_event.module": "gcp",
"kibana.alert.original_event.outcome": "success",
@ -483,13 +483,13 @@
],
"kibana.alert.rule.category": "Custom Query Rule",
"kibana.alert.rule.consumer": "siem",
"kibana.alert.rule.created_at": "2024-09-01T20:38:49.650Z",
"kibana.alert.rule.created_at": "2024-09-01T12:38:49.650Z",
"kibana.alert.rule.created_by": "elastic",
"kibana.alert.rule.description": "Identifies an Identity and Access Management (IAM) custom role creation in Google Cloud Platform (GCP). Custom roles are user-defined, and allow for the bundling of one or more supported permissions to meet specific needs. Custom roles will not be updated automatically and could lead to privilege creep if not carefully scrutinized.",
"kibana.alert.rule.enabled": true,
"kibana.alert.rule.exceptions_list": [
],
"kibana.alert.rule.execution.timestamp": "2024-09-01T20:39:03.657Z",
"kibana.alert.rule.execution.timestamp": "2024-09-01T12:39:03.657Z",
"kibana.alert.rule.execution.uuid": "939d34e1-1e74-480d-90ae-24079d9b40d3",
"kibana.alert.rule.false_positives": [
"Custom role creations may be done by a system or network administrator. Verify whether the user email, resource name, and/or hostname should be making changes in your environment. Role creations by unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule."
@ -657,12 +657,12 @@
"kibana.alert.rule.timestamp_override": "event.ingested",
"kibana.alert.rule.to": "now",
"kibana.alert.rule.type": "query",
"kibana.alert.rule.updated_at": "2024-09-01T20:39:00.099Z",
"kibana.alert.rule.updated_at": "2024-09-01T12:39:00.099Z",
"kibana.alert.rule.updated_by": "elastic",
"kibana.alert.rule.uuid": "c6f64115-5941-46ef-bfa3-61a4ecb4f3ba",
"kibana.alert.rule.version": 104,
"kibana.alert.severity": "medium",
"kibana.alert.start": "2024-09-01T20:39:03.657Z",
"kibana.alert.start": "2024-09-01T12:39:03.657Z",
"kibana.alert.status": "active",
"kibana.alert.uuid": "838ea37ab43ab7d2754d007fbe8191be53d7d637bea62f6189f8db1503c0e250",
"kibana.alert.workflow_assignee_ids": [

View file

@ -17,9 +17,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'header', 'alerts']);
const alertsPage = pageObjects.alerts;
// Failing: See https://github.com/elastic/kibana/issues/198632
// Failing: See https://github.com/elastic/kibana/issues/198632
describe.skip('Security Alerts Page - Graph visualization', function () {
describe('Security Alerts Page - Graph visualization', function () {
this.tags(['cloud_security_posture_graph_viz']);
before(async () => {