-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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] Migrate remaining public Detection Engine APIs to OpenAPI and code generation #170330
Conversation
3ebb54d
to
e9ec18a
Compare
Pinging @elastic/security-detections-response (Team:Detections and Resp) |
Pinging @elastic/security-solution (Team: SecuritySolution) |
e9ec18a
to
899bf8e
Compare
Files by Code Ownerelastic/security-defend-workflows
elastic/security-detection-engine
elastic/security-detection-rule-management
elastic/security-entity-analytics
elastic/security-solution
elastic/security-threat-hunting-investigations
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, Defend Workflows LGTM :)
Not sure why we use ResponseActionTypesEnum['.osquery']
instead of ResponseActionTypesEnum.OSQUERY
here (same for .endpoint) - but it's not a big deal. However would be great to see what you think about this :)
Thanks!
@@ -47,13 +38,13 @@ export const OsqueryParamsCamelCase = t.type({ | |||
// When we create new response action types, create a union of types | |||
export type RuleResponseOsqueryAction = t.TypeOf<typeof RuleResponseOsqueryAction>; | |||
export const RuleResponseOsqueryAction = t.strict({ | |||
actionTypeId: t.literal(RESPONSE_ACTION_TYPES.OSQUERY), | |||
actionTypeId: t.literal('.osquery'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use ResponseActionTypesEnum.OSQUERY
, or at least ResponseActionTypesEnum['.osquery']
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a legacy schema set for removal in upcoming PRs, I've inlined the literal to prevent creating dependencies between the new and the old schemas.
@@ -85,7 +90,7 @@ export const transformRuleToAlertResponseAction = ({ | |||
action_type_id: actionTypeId, | |||
params, | |||
}: ResponseAction): RuleResponseAction => { | |||
if (actionTypeId === RESPONSE_ACTION_TYPES.OSQUERY) { | |||
if (actionTypeId === ResponseActionTypesEnum['.osquery']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not ResponseActionTypesEnum.OSQUERY
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the OpenAPI schema is now the source of truth for data structures and enums in our codebase, I am replacing legacy structures with the generated ones. The generated enum looks as follows:
export const ResponseActionTypes = z.enum(['.osquery', '.endpoint']);
export type ResponseActionTypes = z.infer<typeof ResponseActionTypes>;
export const ResponseActionTypesEnum = ResponseActionTypes.enum;
The enum keys mirror the enum values, which is why we reference them using ResponseActionTypesEnum['.osquery']
.
But for convenience, we could introduce an alias like this and use it everywhere:
const RESPONSE_ACTION_TYPES = {
OSQUERY: ResponseActionTypesEnum['.osquery'],
ENDPOINT: ResponseActionTypesEnum['.endpoint'],
};
I don't have a strong opinion on that tbh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
.default('false') | ||
.transform((value) => value === 'true') | ||
), | ||
exclude_export_details: BooleanFromString.optional().default(false), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, just to understand what happened here:
You created the BooleanFromString
helper in this same PR, but the schema file for export_rules_route
wasn't updated in any way.
So how did this get updated? Did you do it manually?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the code generator for booleans and arrays in request queries. You can find the changes in the template file at packages/kbn-openapi-generator/src/template_service/templates/zod_query_item.handlebars
.
- 'created_at' | ||
- 'createdAt' | ||
- 'enabled' | ||
- 'execution_summary.last_execution.date' | ||
- 'execution_summary.last_execution.metrics.execution_gap_duration_s' | ||
- 'execution_summary.last_execution.metrics.total_indexing_duration_ms' | ||
- 'execution_summary.last_execution.metrics.total_search_duration_ms' | ||
- 'execution_summary.last_execution.status' | ||
- 'name' | ||
- 'risk_score' | ||
- 'riskScore' | ||
- 'severity' | ||
- 'updated_at' | ||
- 'updatedAt' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we keep the comments here for the camelCase fields being legacy, and we're keeping them for backwards compatibility?
|
||
/** | ||
* An array of supported log levels. | ||
*/ | ||
export const LOG_LEVELS = Object.values(LogLevel); | ||
export const LOG_LEVELS = LogLevel.options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It holds an array of all possible LogLevel values: ["trace", "debug", "info", "warn", "error"]
description: End date of the time range to query | ||
schema: | ||
type: string | ||
format: date-time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not modified in this PR, but I see that the parms sort_order
, page
and per_page
are marked as required: false, while in GetRuleExecutionEventsRequestQuery
in the deleted x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_events/get_rule_execution_events_route.ts
they are not marked as partial
.
I realize that these three fields have a default value here in the schema, but just wanted to note the difference in the schemas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it looks like a mistake in the previous schema. Defaultable fields should not be required
*/ | ||
import type { ErrorSchema } from './error_schema'; | ||
|
||
export const getErrorSchemaMock = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where's this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, there was a wrong import. It is used in adjacent error_schema.test.ts
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested all affected endpoints and found no regressions 👍
Left some questions/small comments, but overall LGTM!
Thanks for this refactor!
page: Page.optional(), | ||
per_page: PerPage.optional(), | ||
/** | ||
* Total number of items | ||
*/ | ||
total: z.number().int().min(0).optional(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xcrzx Are all of these really optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should not be optional, and thanks for pointing that out! 👍
899bf8e
to
68464e2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Threat Hunting changes lgtm 🎉
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Module Count
Async chunks
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: cc @xcrzx |
Related to: https://github.com/elastic/security-team/issues/7491
Summary
Migrated remaining public Detection Engine endpoints to OpenAPI schema and code generation:
POST /api/detection_engine/rules/_bulk_action
GET /api/detection_engine/rules/_find
Also completed the migration of internal APIs:
GET /internal/detection_engine/rules/{ruleId}/execution/events
GET /internal/detection_engine/rules/{ruleId}/execution/results
Other notable changes
packages/kbn-zod-helpers/src/stringify_zod_error.ts
. Now we are trying to list the validation errors of all union members but limiting the total number of validation errors displayed to users.TODO https://github.com/elastic/security-team/issues/7491
BooleanFromString
andArrayFromString