Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Detection Engine] adds preview logged requests for new terms, threshold, query, ML rule types #203320

Merged
merged 46 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
6f38076
[Security Solution][Detection Engine] adds preview logged requests fo…
vitaliidm Dec 6, 2024
c7eb832
[Security Solution][Detection Engine] refactoring and type fixing
vitaliidm Dec 9, 2024
328987e
[Security Solution][Detection Engine] fix FTR test
vitaliidm Dec 9, 2024
3eabaed
[Security Solution][Detection Engine] add unit tests
vitaliidm Dec 9, 2024
6ed36ce
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Dec 9, 2024
2f6651f
[Security Solution][Detection Engine] fix i18n
vitaliidm Dec 9, 2024
6014a0f
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Dec 9, 2024
6cfc5bd
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Dec 10, 2024
97c506b
[Security Solution][Detection Engine] add ML rule type
vitaliidm Dec 11, 2024
47276ee
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Dec 11, 2024
a3cbc8f
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Dec 11, 2024
35ba770
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 13, 2025
bb32143
[Security Solution][Detection Engine] fix types
vitaliidm Jan 13, 2025
8ae45c0
[Security Solution][Detection Engine] fix tests
vitaliidm Jan 14, 2025
30591cf
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 14, 2025
d395a29
[Security Solution][Detection Engine] ass query
vitaliidm Jan 15, 2025
fb12a68
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 15, 2025
677b6a5
[Security Solution][Detection Engine] add translations
vitaliidm Jan 15, 2025
e0a2bba
[Security Solution][Detection Engine] fixes logger
vitaliidm Jan 15, 2025
3ac03c4
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 15, 2025
084a47e
[Security Solution][Detection Engine] refactoring
vitaliidm Jan 15, 2025
7639413
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 15, 2025
395f2dc
[Security Solution][Detection Engine] fix type error
vitaliidm Jan 15, 2025
68995fb
[Security Solution][Detection Engine] refactoring + new terms POC
vitaliidm Jan 20, 2025
83dc6c2
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 20, 2025
6f0743c
[CI] Auto-commit changed files from 'yarn openapi:bundle'
kibanamachine Jan 20, 2025
112be84
query rule paging
vitaliidm Jan 21, 2025
a4b6080
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 21, 2025
92a1c85
[Security Solution][Detection Engine] threshold rule
vitaliidm Jan 21, 2025
b020a2c
[Security Solution][Detection Engine] FUNCTIONAL CHANGES
vitaliidm Jan 21, 2025
448f038
refactoring
vitaliidm Jan 21, 2025
36d667d
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Jan 21, 2025
26a87a5
[Security Solution][Detection Engine] add unit tests
vitaliidm Jan 21, 2025
ecd7774
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 21, 2025
e14f30b
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 21, 2025
82dc3da
fix tests
vitaliidm Jan 22, 2025
b6445b3
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 22, 2025
abf429c
[Security Solution][Detection Engine] fix more tests
vitaliidm Jan 22, 2025
804e316
[Security Solution][Detection Engine] refactoring
vitaliidm Jan 22, 2025
59ea26a
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 23, 2025
5a0ca5a
[Security Solution][Detection Engine]fix styles
vitaliidm Jan 23, 2025
23ebe99
Merge branch 'de_8_18/adds-logged-requests' of https://github.com/vit…
vitaliidm Jan 23, 2025
154ec1f
[Security Solution][Detection Engine] tests
vitaliidm Jan 23, 2025
ec9707d
[Security Solution][Detection Engine]additional test assertion
vitaliidm Jan 24, 2025
d1f0bdc
Merge branch 'main' into de_8_18/adds-logged-requests
vitaliidm Jan 27, 2025
dfeb094
[Security Solution][Detection Engine] rename argument name
vitaliidm Jan 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions oas_docs/output/kibana.serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47349,8 +47349,8 @@ components:
type: integer
request:
$ref: '#/components/schemas/Security_Detections_API_NonEmptyString'
required:
- request
request_type:
$ref: '#/components/schemas/Security_Detections_API_NonEmptyString'
Security_Detections_API_RulePreviewLogs:
type: object
properties:
Expand Down
4 changes: 2 additions & 2 deletions oas_docs/output/kibana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54194,8 +54194,8 @@ components:
type: integer
request:
$ref: '#/components/schemas/Security_Detections_API_NonEmptyString'
required:
- request
request_type:
$ref: '#/components/schemas/Security_Detections_API_NonEmptyString'
Security_Detections_API_RulePreviewLogs:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ export const RulePreviewParams = z.object({

export type RulePreviewLoggedRequest = z.infer<typeof RulePreviewLoggedRequest>;
export const RulePreviewLoggedRequest = z.object({
request: NonEmptyString,
request: NonEmptyString.optional(),
description: NonEmptyString.optional(),
duration: z.number().int().optional(),
request_type: NonEmptyString.optional(),
});

export type RulePreviewLogs = z.infer<typeof RulePreviewLogs>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ components:
$ref: '../../model/primitives.schema.yaml#/components/schemas/NonEmptyString'
duration:
type: integer
required:
- request
request_type:
$ref: '../../model/primitives.schema.yaml#/components/schemas/NonEmptyString'

RulePreviewLogs:
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5282,8 +5282,8 @@ components:
type: integer
request:
$ref: '#/components/schemas/NonEmptyString'
required:
- request
request_type:
$ref: '#/components/schemas/NonEmptyString'
RulePreviewLogs:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4431,8 +4431,8 @@ components:
type: integer
request:
$ref: '#/components/schemas/NonEmptyString'
required:
- request
request_type:
$ref: '#/components/schemas/NonEmptyString'
RulePreviewLogs:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,30 @@ export const previewLogs: RulePreviewLogs[] = [
],
},
];

export const queryRuleTypePreviewLogs: RulePreviewLogs[] = [
{
errors: [],
warnings: [
'This rule reached the maximum alert limit for the rule execution. Some alerts were not created.',
],
startedAt: '2025-01-21T16:48:50.891Z',
duration: 1103,
requests: [
{
request:
'POST /apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*,very-unique/_search?allow_no_indices=true&ignore_unavailable=true\n{\n "size": 100,\n "query": {\n "bool": {\n "filter": [\n {\n "bool": {\n "must": [],\n "filter": [\n {\n "query_string": {\n "query": "*"\n }\n }\n ],\n "should": [],\n "must_not": []\n }\n },\n {\n "range": {\n "@timestamp": {\n "lte": "2025-01-21T16:48:50.891Z",\n "gte": "2025-01-21T15:17:50.891Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "fields": [\n {\n "field": "*",\n "include_unmapped": true\n },\n {\n "field": "@timestamp",\n "format": "strict_date_optional_time"\n }\n ],\n "runtime_mappings": {},\n "sort": [\n {\n "@timestamp": {\n "order": "asc",\n "unmapped_type": "date"\n }\n }\n ]\n}',
description: 'Find documents',
request_type: 'findDocuments',
duration: 137,
},
{
request:
'POST /apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*,very-unique/_search?allow_no_indices=true&ignore_unavailable=true\n{\n "size": 100,\n "query": {\n "bool": {\n "filter": [\n {\n "bool": {\n "must": [],\n "filter": [\n {\n "query_string": {\n "query": "*"\n }\n }\n ],\n "should": [],\n "must_not": []\n }\n },\n {\n "range": {\n "@timestamp": {\n "lte": "2025-01-21T16:48:50.891Z",\n "gte": "2025-01-21T15:17:50.891Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "fields": [\n {\n "field": "*",\n "include_unmapped": true\n },\n {\n "field": "@timestamp",\n "format": "strict_date_optional_time"\n }\n ],\n "runtime_mappings": {},\n "sort": [\n {\n "@timestamp": {\n "order": "asc",\n "unmapped_type": "date"\n }\n }\n ],\n "search_after": [\n 1737472675562\n ]\n}',
description: 'Find documents after cursor [1737472675562]',
request_type: 'findDocuments',
duration: 192,
},
],
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,18 @@ jest.mock('../../../../common/hooks/use_experimental_features', () => ({
}));

// rule types that do not support logged requests
const doNotSupportLoggedRequests: Type[] = [
const doNotSupportLoggedRequests: Type[] = ['threat_match'];

const supportLoggedRequests: Type[] = [
'esql',
'eql',
'threshold',
'threat_match',
'machine_learning',
'query',
'saved_query',
'new_terms',
];

const supportLoggedRequests: Type[] = ['esql', 'eql'];

const getMockIndexPattern = (): DataViewBase => ({
fields,
id: '1234',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ import { usePreviewInvocationCount } from './use_preview_invocation_count';

export const REASONABLE_INVOCATION_COUNT = 200;

const RULE_TYPES_SUPPORTING_LOGGED_REQUESTS: Type[] = ['esql', 'eql'];
const RULE_TYPES_SUPPORTING_LOGGED_REQUESTS: Type[] = [
'esql',
'eql',
'threshold',
'machine_learning',
'query',
'saved_query',
'new_terms',
];

const timeRanges = [
{ start: 'now/d', end: 'now', label: 'Today' },
Expand Down Expand Up @@ -316,6 +324,7 @@ const RulePreviewComponent: React.FC<RulePreviewProps> = ({
hasNoiseWarning={hasNoiseWarning}
isAborted={isAborted}
showElasticsearchRequests={showElasticsearchRequests && isLoggedRequestsSupported}
ruleType={ruleType}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import userEvent from '@testing-library/user-event';
import { TestProviders } from '../../../../common/mock/test_providers';
import { LoggedRequests } from './logged_requests';

import { previewLogs } from './__mocks__/preview_logs';
import { previewLogs, queryRuleTypePreviewLogs } from './__mocks__/preview_logs';

describe('LoggedRequests', () => {
it('should not render component if logs are empty', () => {
render(<LoggedRequests logs={[]} />, { wrapper: TestProviders });
render(<LoggedRequests logs={[]} ruleType="esql" />, { wrapper: TestProviders });

expect(screen.queryByTestId('preview-logged-requests-accordion')).toBeNull();
});

it('should open accordion on click and render list of request items', async () => {
render(<LoggedRequests logs={previewLogs} />, { wrapper: TestProviders });
render(<LoggedRequests logs={previewLogs} ruleType="esql" />, { wrapper: TestProviders });

expect(screen.queryByTestId('preview-logged-requests-accordion')).toBeInTheDocument();

Expand All @@ -32,7 +32,7 @@ describe('LoggedRequests', () => {
});

it('should render code content on logged request item accordion click', async () => {
render(<LoggedRequests logs={previewLogs} />, { wrapper: TestProviders });
render(<LoggedRequests logs={previewLogs} ruleType="esql" />, { wrapper: TestProviders });

expect(screen.queryByTestId('preview-logged-requests-accordion')).toBeInTheDocument();

Expand Down Expand Up @@ -65,4 +65,32 @@ describe('LoggedRequests', () => {
/POST \/packetbeat-8\.14\.2\/_search\?ignore_unavailable=true/
);
});

it('should render code content when rule supports page view', async () => {
render(<LoggedRequests logs={queryRuleTypePreviewLogs} ruleType="query" />, {
wrapper: TestProviders,
});

expect(screen.queryByTestId('preview-logged-requests-accordion')).toBeInTheDocument();

await userEvent.click(screen.getByText('Preview logged requests'));

const loggedRequestsItem = screen.getAllByTestId('preview-logged-requests-item-accordion')[0];

expect(loggedRequestsItem).toHaveTextContent('Rule execution started at');
expect(loggedRequestsItem).toHaveTextContent('[1103ms]');

await userEvent.click(loggedRequestsItem.querySelector('button') as HTMLElement);

expect(screen.getAllByTestId('preview-logged-requests-page-accordion')).toHaveLength(2);

await userEvent.click(screen.getByText('Page 1 of search queries'));

expect(screen.getAllByTestId('preview-logged-request-description')[0]).toHaveTextContent(
'Find documents [137ms]'
);
expect(screen.getAllByTestId('preview-logged-request-code-block')[0]).toHaveTextContent(
'POST /apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*,very-unique/_search?allow_no_indices=true&ignore_unavailable=true'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@
import type { FC } from 'react';
import React, { useMemo } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { css } from '@emotion/css';
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import { css } from '@emotion/react';

import type { RulePreviewLogs } from '../../../../../common/api/detection_engine';
import * as i18n from './translations';
import { OptimizedAccordion } from './optimized_accordion';
import { LoggedRequestsItem } from './logged_requests_item';
import { useAccordionStyling } from './use_accordion_styling';

const LoggedRequestsComponent: FC<{ logs: RulePreviewLogs[] }> = ({ logs }) => {
const LoggedRequestsComponent: FC<{ logs: RulePreviewLogs[]; ruleType: Type }> = ({
logs,
ruleType,
}) => {
const cssStyles = useAccordionStyling();

const AccordionContent = useMemo(
Expand All @@ -25,12 +29,12 @@ const LoggedRequestsComponent: FC<{ logs: RulePreviewLogs[] }> = ({ logs }) => {
<EuiSpacer size="m" />
{logs.map((log) => (
<React.Fragment key={log.startedAt}>
<LoggedRequestsItem {...log} />
<LoggedRequestsItem {...log} ruleType={ruleType} />
</React.Fragment>
))}
</>
),
[logs]
[logs, ruleType]
);

if (logs.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,27 @@

import type { FC, PropsWithChildren } from 'react';
import React from 'react';
import { css } from '@emotion/css';
import { css } from '@emotion/react';

import { EuiSpacer, EuiCodeBlock, useEuiPaddingSize, EuiFlexItem } from '@elastic/eui';
import { useEuiPaddingSize, EuiText, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import type { RulePreviewLogs } from '../../../../../common/api/detection_engine';
import * as i18n from './translations';
import { PreferenceFormattedDate } from '../../../../common/components/formatted_date';
import { OptimizedAccordion } from './optimized_accordion';
import { LoggedRequestsQuery } from './logged_requests_query';
import { useAccordionStyling } from './use_accordion_styling';
import { LoggedRequestsPages, isPageViewSupported } from './logged_requests_pages';

const LoggedRequestsItemComponent: FC<PropsWithChildren<RulePreviewLogs>> = ({
const LoggedRequestsItemComponent: FC<PropsWithChildren<RulePreviewLogs & { ruleType: Type }>> = ({
startedAt,
duration,
requests,
requests = [],
ruleType,
}) => {
const paddingLarge = useEuiPaddingSize('l');
const cssStyles = useAccordionStyling();

return (
<OptimizedAccordion
data-test-subj="preview-logged-requests-item-accordion"
Expand All @@ -48,29 +51,26 @@ const LoggedRequestsItemComponent: FC<PropsWithChildren<RulePreviewLogs>> = ({
${cssStyles}
`}
>
{(requests ?? []).map((request, key) => (
<EuiFlexItem
key={key}
css={css`
padding-left: ${paddingLarge};
`}
>
<EuiSpacer size="l" />
<span data-test-subj="preview-logged-request-description">
{request?.description ?? null} {request?.duration ? `[${request.duration}ms]` : null}
</span>
{requests.length > 2 ? (
<>
<EuiSpacer size="s" />
<EuiCodeBlock
language="json"
isCopyable
overflowHeight={300}
isVirtualized
data-test-subj="preview-logged-request-code-block"
<EuiText
color="warning"
size="s"
css={css`
margin-left: ${paddingLarge};
`}
>
{request.request}
</EuiCodeBlock>
</EuiFlexItem>
))}
{i18n.REQUESTS_SAMPLE_WARNING}
</EuiText>
<EuiSpacer size="s" />
</>
) : null}
{isPageViewSupported(ruleType) ? (
<LoggedRequestsPages requests={requests} ruleType={ruleType} />
) : (
requests.map((request, key) => <LoggedRequestsQuery key={key} {...request} />)
)}
</OptimizedAccordion>
);
};
Expand Down
Loading