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] Create Map of upgradable rule fields by type #190128

Merged
merged 33 commits into from
Sep 12, 2024

Conversation

jpdjere
Copy link
Contributor

@jpdjere jpdjere commented Aug 8, 2024

Summary

  • Partially addresses [Security Solution] Extend the POST /upgrade/_perform API endpoint's contract and functionality #166376 (see step 1 of plan)

  • Partially addresses: [Security Solution] Add InvestigationFields and AlertSuppression fields to the upgrade workflow #190597

  • Creates a Map of the fields that are upgradable during the Upgrade workflow, by type.

    • Creating this Map dynamically, based of BaseCreateProps and TypeSpecificFields, ensures that we don't need to:
      • manually add rule types to this Map if they are created
      • manually add or remove any fields if they are added or removed to a specific rule type
      • manually add or remove any fields if we decide that they should not be part of the upgradable fields.
    • This Map will be used as part of the /upgrade/_perform endpoint handler logic to build the payload of fields that will be upgraded to their different versions (BASE, CURRENT, TARGET, MERGED,RESOLVED)
  • Creates RuleFieldsToUpgrade Zod schema and FieldUpgradeSpecifier type, part of the /upgrade/_perform payload, which defines which fields can be upgraded and how.


See output: UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP
new Map([
    [
        "eql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "language",
            "index",
            "data_view_id",
            "filters",
            "event_category_override",
            "tiebreaker_field",
            "timestamp_field",
            "alert_suppression"
        ]
    ],
    [
        "query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "saved_query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "saved_id",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "threshold",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threshold",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "threat_match",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threat_query",
            "threat_mapping",
            "threat_index",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "threat_filters",
            "threat_indicator_path",
            "threat_language",
            "concurrent_searches",
            "items_per_search",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "machine_learning",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "anomaly_threshold",
            "machine_learning_job_id",
            "alert_suppression"
        ]
    ],
    [
        "new_terms",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "new_terms_fields",
            "history_window_start",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "esql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "alert_suppression",
            "type",
            "language",
            "query"
        ]
    ]
])

### For maintainers

@jpdjere jpdjere changed the title [Security Solution] Create map of upgradable rule fields by type [Security Solution] Create Map of upgradable rule fields by type Aug 8, 2024
@jpdjere jpdjere self-assigned this Aug 8, 2024
@jpdjere jpdjere added release_note:skip Skip the PR/issue when compiling release notes backport:skip This commit does not require backporting Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v6.8.16 Team:Detection Rule Management Security Detection Rule Management Team Feature:Prebuilt Detection Rules Security Solution Prebuilt Detection Rules labels Aug 8, 2024
@jpdjere jpdjere marked this pull request as ready for review August 8, 2024 13:04
@jpdjere jpdjere requested a review from a team as a code owner August 8, 2024 13:04
@jpdjere jpdjere requested a review from maximpn August 8, 2024 13:04
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detections-response (Team:Detections and Resp)

@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detection-rule-management (Team:Detection Rule Management)

@jpdjere jpdjere removed the request for review from maximpn August 8, 2024 13:04
@jpdjere jpdjere assigned xcrzx and banderror and unassigned xcrzx and banderror Aug 8, 2024
@jpdjere jpdjere requested review from xcrzx and banderror August 8, 2024 13:04
// the type of TypeSpecificFields is actually z.ZodEffects, and therefore does
// not have access to an options property. We need to transform it back into
// a z.ZodDiscriminatedUnion, accessing its underlying schema.
const TypeSpecificFieldsSchema = TypeSpecificFields._def.schema;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, we have a zod transform that removes response_actions from certain rule types. Therefore, I expect response_actions not to be part of UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP. However, _def.schema actually accesses the initial definitions where response_actions are included so they become upgradeable fields.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you need is to use omit instead of relying on transform:

export type TypeSpecificFields = z.infer<typeof TypeSpecificFields>;
export const TypeSpecificFields = z.discriminatedUnion('type', [
  EqlRuleCreateFields,
  QueryRuleCreateFields.omit({ response_actions: true }),
  SavedQueryRuleCreateFields.omit({ response_actions: true }),
  ThresholdRuleCreateFields,
  ThreatMatchRuleCreateFields,
  MachineLearningRuleCreateFields,
  NewTermsRuleCreateFields,
  EsqlRuleCreateFields,
]);

Copy link
Contributor Author

@jpdjere jpdjere Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect response_actions not to be part of UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP

Yes, thanks for that catch. It indeed includes response_actions, which is exactly what I'm trying to prevent.

The solution you proposed above would be the easiest way forward, and would solve the issue with the response_action field being present in the Map, but the reason we initially went with the more complex Zod .transform() is because we discussed with @banderror that we want to avoid having and additional place where the rule types are listed, and which needs to be modified if a new rule type is added.

I think it makes sense: when ES|QL was added, and the Detection Engine team added the functionality for a corresponding security rule, but we did not go through our codebase to check where rule types where listed and if we needed to take some action.

But, yeah, not sure how strict we want/need to be with this decision. I'll try to find another way of generating the Map without the response_action field using the output of the transform.

One possibility is just filtering out unwanted fields from the type specific fields within the function that creates the map. Something like:

const TYPE_SPECIFIC_FIELDS_TO_OMIT = new Set(['response_actions']);

function createUpgradableRuleFieldsByTypeMap() {
  const baseFields = Object.keys(
    BaseCreateProps.omit(BASE_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET).shape
  );

  return new Map(
    TypeSpecificFieldsSchema.options.map((option) => {
      const typeName = option.shape.type.value;
      const typeSpecificFields = Object.keys(option.shape).filter(field => !TYPE_SPECIFIC_FIELDS_TO_OMIT.has(field));
      return [typeName, [...baseFields, ...typeSpecificFields]];
    })
  );
}

And also use TYPE_SPECIFIC_FIELDS_TO_OMIT in the transform() function somehow, to avoid repeating the list of fields to omit.

I'll give that a try, otherwise we can fallback to recreating the discriminated union as you proposed above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xcrzx and FYI @banderror

OK, the apporach I proposed above is technically feasible, and we are still able to generate the derived types without listing all the rule types.

I pushed the changes. Take a look and let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the transformation now turns all type-specific props into Record<string, any>:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense: when ES|QL was added, and the Detection Engine team added the functionality for a corresponding security rule, but we did not go through our codebase to check where rule types where listed and if we needed to take some action.

Can we use the power of Typescript to ensure those types are always in sync?

import type { IsEqual } from 'type-fest';
// Make sure the type-specific fields contain all the same rule types as the type-specific rule params
// Here will be a type error if the types are not equal
const areTypesEqual: IsEqual<
  typeof TypeSpecificRuleParams._type.type,
  typeof TypeSpecificFields._type.type
> = true;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option, just use tests:

expect(TypeSpecificRuleParams._type.type).toEqual(TypeSpecificFields._type.type);

@jpdjere
Copy link
Contributor Author

jpdjere commented Aug 12, 2024

@xcrzx Thanks for all the suggestions. I applied both checks for extra security, the static TS check and an additional test to the test suite.

Type behaves as it should now:

image
image
image

@jpdjere jpdjere requested a review from xcrzx August 12, 2024 13:08
@@ -83,3 +105,31 @@ export const PrebuiltRuleAsset = BaseCreateProps.omit(BASE_PROPS_REMOVED_FROM_PR
version: RuleVersion,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule version should also be part of upgradeable rule fields.

Copy link
Contributor

@banderror banderror Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xcrzx Not sure I understand why, can you elaborate?

From my perspective, version should be returned from the upgrade/_review endpoint as part of the diff (as it currently is), but shouldn't be accepted by the upgrade/_perform endpoint as part of the fields object. We pass it separately for concurrency control reasons, and it should be always picked from the target version by the endpoint handler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By upgradeable I meant that the version field can be upgraded to the target version. But in the context of the upgrade/_perform API request, I agree that it shouldn’t be part of the fields structure. So it really depends on how the upgradeable field list is intended to be used.

// Filter out type-specific fields that should not be part of the upgradable fields
(field) => !specificTypesToOmit.includes(field)
);
return [typeName, [...baseFields, ...typeSpecificFields]];
Copy link
Contributor

@xcrzx xcrzx Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought, maybe we should derive the upgradeable fields from the PrebuiltRuleAsset schema minus rule_id? Instead of composing it from base + type-specific fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I don't start of directly from PrebuiltRuleAsset is because it has a type of ZodIntersection, which does not have the options or shape operands that Zod discriminated unions have.

Also, if I wanna do something like:

PrebuiltRuleAsset.transform((data) => { ...

that data is an object of only the properties common for all types (type-specific props are missing)

Copy link
Contributor

@banderror banderror left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpdjere I reviewed code changes and left some comments.

import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from './prebuilt_rule_asset.mock';
import { TypeSpecificCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine';

describe('TypeSpecificFields', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it have its own top-level describe block? Isn't it in fact a part of 'Prebuilt rule asset schema'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved within Prebuilt rule asset schema

);
}

export const UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP = createUpgradableRuleFieldsByTypeMap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand how and where this map is gonna be used, can you elaborate?

Also, what I'm missing in this PR is changes to the upgrade/_perform request schema. We talked that its fields property's schema should be derived from the rule schema, I think this needs to be done in this PR:

fields: z
.object({
name: createUpgradeFieldSchema(RuleName),
tags: createUpgradeFieldSchema(RuleTagArray),
description: createUpgradeFieldSchema(RuleDescription),
severity: createUpgradeFieldSchema(Severity),
severity_mapping: createUpgradeFieldSchema(SeverityMapping),
risk_score: createUpgradeFieldSchema(RiskScore),
risk_score_mapping: createUpgradeFieldSchema(RiskScoreMapping),
references: createUpgradeFieldSchema(RuleReferenceArray),
false_positives: createUpgradeFieldSchema(RuleFalsePositiveArray),
threat: createUpgradeFieldSchema(ThreatArray),
note: createUpgradeFieldSchema(InvestigationGuide),
setup: createUpgradeFieldSchema(SetupGuide),
related_integrations: createUpgradeFieldSchema(RelatedIntegrationArray),
required_fields: createUpgradeFieldSchema(RequiredFieldArray),
max_signals: createUpgradeFieldSchema(MaxSignals),
building_block_type: createUpgradeFieldSchema(BuildingBlockType),
from: createUpgradeFieldSchema(RuleIntervalFrom),
interval: createUpgradeFieldSchema(RuleInterval),
exceptions_list: createUpgradeFieldSchema(RuleExceptionList),
rule_name_override: createUpgradeFieldSchema(RuleNameOverride),
timestamp_override: createUpgradeFieldSchema(TimestampOverride),
timestamp_override_fallback_disabled: createUpgradeFieldSchema(
TimestampOverrideFallbackDisabled
),
timeline_id: createUpgradeFieldSchema(TimelineTemplateId),
timeline_title: createUpgradeFieldSchema(TimelineTemplateTitle),
index: createUpgradeFieldSchema(IndexPatternArray),
data_view_id: createUpgradeFieldSchema(DataViewId),
query: createUpgradeFieldSchema(RuleQuery),
language: createUpgradeFieldSchema(QueryLanguage),
filters: createUpgradeFieldSchema(RuleFilterArray),
saved_id: createUpgradeFieldSchema(SavedQueryId),
machine_learning_job_id: createUpgradeFieldSchema(MachineLearningJobId),
anomaly_threshold: createUpgradeFieldSchema(AnomalyThreshold),
threat_query: createUpgradeFieldSchema(ThreatQuery),
threat_mapping: createUpgradeFieldSchema(ThreatMapping),
threat_index: createUpgradeFieldSchema(ThreatIndex),
threat_filters: createUpgradeFieldSchema(ThreatFilters),
threat_indicator_path: createUpgradeFieldSchema(ThreatIndicatorPath),
threat_language: createUpgradeFieldSchema(KqlQueryLanguage),
new_terms_fields: createUpgradeFieldSchema(NewTermsFields),
history_window_start: createUpgradeFieldSchema(HistoryWindowStart),
})

Copy link
Contributor Author

@jpdjere jpdjere Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand how and where this map is gonna be used, can you elaborate?

The idea is to take the type of the target rule to retrieve from our Map the list of upgradable fields that we should use to create a payload to send over to our Detections Client. For example, the user is updating a new_term rules to its next version, and our Map says that the upgradable fields for a new_terms rule are:

    [
        "new_terms",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "new_terms_fields",
            "history_window_start",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "language"
        ]
    ],

So we retrieve these fields from the Map, so can loop over them, and based on the payload of the request, create a value to update them to. Using this list, we'd create an object like:

{
            "name": "Juan" // calculated from the diffs if pick_version was MERGED
            "description": "My Description" // passed in the payload since pick_version for field was RESOLVED
            "risk_score": 11 // retrieved from BASE version since pick_version for field was BASW
            "severity": 'hi', //etc
           // ...etc including all upgradable fields
}

This object will be created for each rule, and then the whole payload passed over to the Detection Client's upgradePrebuiltRule() method.

Also, this map allows us to do additional validation: if we are upgrading a new_terms rule and the user includes in the fields object of the payload a anomaly_threshold field (or any non-matching) field, we can throw for that rule.

Copy link
Contributor Author

@jpdjere jpdjere Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what I'm missing in this PR is changes to the upgrade/_perform request schema. We talked that its fields property's schema should be derived from the rule schema, I think this needs to be done in this PR:

Yes, you're right, I'm trying to do some Zod acrobatics to get this schema created. It's WIP, but I will include it in this PR.

@jpdjere jpdjere marked this pull request as draft August 13, 2024 10:25
@xcrzx xcrzx removed the v6.8.16 label Aug 13, 2024
@jpdjere jpdjere force-pushed the create-upgradable-rule-fields-map branch from 2f6773d to 5b3e6c1 Compare September 11, 2024 13:24
@jpdjere jpdjere requested a review from a team as a code owner September 11, 2024 13:24
@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 19.7MB 19.7MB +1.5KB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @jpdjere

Copy link
Contributor

@vitaliidm vitaliidm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detection engine area LGTM

@banderror banderror removed the backport:skip This commit does not require backporting label Sep 12, 2024
@banderror banderror self-requested a review September 12, 2024 16:15
Copy link
Contributor

@banderror banderror left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very quickly looked through the diff and the comments, LGTM 👍

@jpdjere I'll probably take a deeper look tomorrow and maybe leave a few comments post-merge, but I wouldn't like to block the PR for another 24 hours, so approving the PR ✅ Thank you!

@jpdjere jpdjere merged commit d801f5d into elastic:main Sep 12, 2024
40 checks passed
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Sep 12, 2024
gergoabraham pushed a commit to gergoabraham/kibana that referenced this pull request Sep 13, 2024
…stic#190128)

## Summary

- Partially addresses elastic#166376
(see step 1 of
[plan](elastic#166376 (comment)))
- Partially addresses: elastic#190597

- Creates a Map of the fields that are upgradable during the Upgrade
workflow, by type.
- Creating this Map dynamically, based of BaseCreateProps and
TypeSpecificFields, ensures that we don't need to:
      - manually add rule types to this Map if they are created
- manually add or remove any fields if they are added or removed to a
specific rule type
- manually add or remove any fields if we decide that they should not be
part of the upgradable fields.
- This Map will be used as part of the `/upgrade/_perform` endpoint
handler logic to build the payload of fields that will be upgraded to
their different versions (`BASE`, `CURRENT`, `TARGET`,
`MERGED`,`RESOLVED`)
- Creates `RuleFieldsToUpgrade` Zod schema and `FieldUpgradeSpecifier`
type, part of the `/upgrade/_perform` payload, which defines which
fields can be upgraded and how.

<br>
<details>
<summary>See output:
<b>UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP</b></summary>


```ts
new Map([
    [
        "eql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "language",
            "index",
            "data_view_id",
            "filters",
            "event_category_override",
            "tiebreaker_field",
            "timestamp_field",
            "alert_suppression"
        ]
    ],
    [
        "query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "saved_query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "saved_id",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "threshold",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threshold",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "threat_match",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threat_query",
            "threat_mapping",
            "threat_index",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "threat_filters",
            "threat_indicator_path",
            "threat_language",
            "concurrent_searches",
            "items_per_search",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "machine_learning",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "anomaly_threshold",
            "machine_learning_job_id",
            "alert_suppression"
        ]
    ],
    [
        "new_terms",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "new_terms_fields",
            "history_window_start",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "esql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "alert_suppression",
            "type",
            "language",
            "query"
        ]
    ]
])
```
</details>
<br>
### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <[email protected]>
markov00 pushed a commit to markov00/kibana that referenced this pull request Sep 18, 2024
…stic#190128)

## Summary

- Partially addresses elastic#166376
(see step 1 of
[plan](elastic#166376 (comment)))
- Partially addresses: elastic#190597

- Creates a Map of the fields that are upgradable during the Upgrade
workflow, by type.
- Creating this Map dynamically, based of BaseCreateProps and
TypeSpecificFields, ensures that we don't need to:
      - manually add rule types to this Map if they are created
- manually add or remove any fields if they are added or removed to a
specific rule type
- manually add or remove any fields if we decide that they should not be
part of the upgradable fields.
- This Map will be used as part of the `/upgrade/_perform` endpoint
handler logic to build the payload of fields that will be upgraded to
their different versions (`BASE`, `CURRENT`, `TARGET`,
`MERGED`,`RESOLVED`)
- Creates `RuleFieldsToUpgrade` Zod schema and `FieldUpgradeSpecifier`
type, part of the `/upgrade/_perform` payload, which defines which
fields can be upgraded and how.

<br>
<details>
<summary>See output:
<b>UPGRADABLE_RULES_FIELDS_BY_TYPE_MAP</b></summary>


```ts
new Map([
    [
        "eql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "language",
            "index",
            "data_view_id",
            "filters",
            "event_category_override",
            "tiebreaker_field",
            "timestamp_field",
            "alert_suppression"
        ]
    ],
    [
        "query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "saved_query",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "saved_id",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "query",
            "language"
        ]
    ],
    [
        "threshold",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threshold",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "threat_match",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "threat_query",
            "threat_mapping",
            "threat_index",
            "index",
            "data_view_id",
            "filters",
            "saved_id",
            "threat_filters",
            "threat_indicator_path",
            "threat_language",
            "concurrent_searches",
            "items_per_search",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "machine_learning",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "anomaly_threshold",
            "machine_learning_job_id",
            "alert_suppression"
        ]
    ],
    [
        "new_terms",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "type",
            "query",
            "new_terms_fields",
            "history_window_start",
            "index",
            "data_view_id",
            "filters",
            "alert_suppression",
            "language"
        ]
    ],
    [
        "esql",
        [
            "name",
            "description",
            "risk_score",
            "severity",
            "rule_name_override",
            "timestamp_override",
            "timestamp_override_fallback_disabled",
            "timeline_id",
            "timeline_title",
            "license",
            "note",
            "building_block_type",
            "investigation_fields",
            "version",
            "tags",
            "enabled",
            "risk_score_mapping",
            "severity_mapping",
            "interval",
            "from",
            "to",
            "exceptions_list",
            "author",
            "false_positives",
            "references",
            "max_signals",
            "threat",
            "setup",
            "related_integrations",
            "required_fields",
            "alert_suppression",
            "type",
            "language",
            "query"
        ]
    ]
])
```
</details>
<br>
### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Prebuilt Detection Rules Security Solution Prebuilt Detection Rules release_note:skip Skip the PR/issue when compiling release notes Team:Detection Rule Management Security Detection Rule Management Team Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.16.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants