Skip to content

Commit 31bf0b4

Browse files
Mikaayensongithub-actions[bot]
authored andcommitted
[Bug] Strip Non-Public Fields Prior to Uploading Rules (#2986)
(cherry picked from commit 3f9e7ac)
1 parent 0440c6c commit 31bf0b4

File tree

4 files changed

+34
-28
lines changed

4 files changed

+34
-28
lines changed

detection_rules/kbwrap.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55

66
"""Kibana cli commands."""
77
import sys
8-
import uuid
98

109
import click
1110

11+
1212
import kql
1313
from kibana import Signal, RuleResource
1414
from .cli_utils import multi_collection
1515
from .main import root
1616
from .misc import add_params, client_error, kibana_options, get_kibana_client, nested_set
17-
from .schemas import downgrade
17+
from .rule import downgrade_contents_from_rule
1818
from .utils import format_command_options
1919

2020

@@ -45,14 +45,7 @@ def upload_rule(ctx, rules, replace_id):
4545

4646
for rule in rules:
4747
try:
48-
payload = rule.contents.to_api_format()
49-
payload.setdefault("meta", {}).update(rule.contents.metadata.to_dict())
50-
51-
if replace_id:
52-
payload["rule_id"] = str(uuid.uuid4())
53-
54-
payload = downgrade(payload, target_version=kibana.version)
55-
48+
payload = downgrade_contents_from_rule(rule, kibana.version, replace_id=replace_id)
5649
except ValueError as e:
5750
client_error(f'{e} in version:{kibana.version}, for rule: {rule.name}', e, ctx=ctx)
5851

detection_rules/rule.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
from .mixins import MarshmallowDataclassMixin, StackCompatMixin
3232
from .rule_formatter import nested_normalize, toml_write
3333
from .schemas import (SCHEMA_DIR, definitions, downgrade,
34-
get_min_supported_stack_version, get_stack_schemas)
34+
get_min_supported_stack_version, get_stack_schemas,
35+
strip_non_public_fields)
3536
from .schemas.stack_compat import get_restricted_fields
3637
from .utils import cached, convert_time_span, PatchedTemplate
3738

@@ -1300,13 +1301,23 @@ def name(self) -> str:
13001301
return self.contents.name
13011302

13021303

1303-
def downgrade_contents_from_rule(rule: TOMLRule, target_version: str) -> dict:
1304+
def downgrade_contents_from_rule(rule: TOMLRule, target_version: str, replace_id: bool = True) -> dict:
13041305
"""Generate the downgraded contents from a rule."""
1305-
payload = rule.contents.to_api_format()
1306-
meta = payload.setdefault("meta", {})
1307-
meta["original"] = dict(id=rule.id, **rule.contents.metadata.to_dict())
1308-
payload["rule_id"] = str(uuid4())
1309-
payload = downgrade(payload, target_version)
1306+
rule_dict = rule.contents.to_dict()["rule"]
1307+
min_stack_version = target_version or rule.contents.metadata.min_stack_version or "8.3.0"
1308+
min_stack_version = Version.parse(min_stack_version,
1309+
optional_minor_and_patch=True)
1310+
rule_dict.setdefault("meta", {}).update(rule.contents.metadata.to_dict())
1311+
1312+
if replace_id:
1313+
rule_dict["rule_id"] = str(uuid4())
1314+
1315+
rule_dict = downgrade(rule_dict, target_version=str(min_stack_version))
1316+
meta = rule_dict.pop("meta")
1317+
rule_contents = TOMLRuleContents.from_dict({"rule": rule_dict, "metadata": meta,
1318+
"transform": rule.contents.transform})
1319+
payload = rule_contents.to_api_format()
1320+
payload = strip_non_public_fields(min_stack_version, payload)
13101321
return payload
13111322

13121323

detection_rules/schemas/__init__.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@ def get_schema_file(version: Version, rule_type: str) -> dict:
6565
def strip_additional_properties(version: Version, api_contents: dict) -> dict:
6666
"""Remove all fields that the target schema doesn't recognize."""
6767

68-
if Version.parse(version, optional_minor_and_patch=True) >= Version.parse("8.3.0"):
69-
api_contents = strip_build_time_fields(api_contents)
70-
7168
stripped = {}
7269
target_schema = get_schema_file(version, api_contents["type"])
7370

@@ -80,14 +77,13 @@ def strip_additional_properties(version: Version, api_contents: dict) -> dict:
8077
return stripped
8178

8279

83-
def strip_build_time_fields(api_contents: dict) -> dict:
84-
"""Remove all fields that are only used at build time."""
85-
contents = api_contents.copy()
86-
if "related_integrations" in contents:
87-
del contents["related_integrations"]
88-
if "required_fields" in contents:
89-
del contents["required_fields"]
90-
return contents
80+
def strip_non_public_fields(min_stack_version: Version, data_dict: dict) -> dict:
81+
"""Remove all non public fields."""
82+
for field, version_range in definitions.NON_PUBLIC_FIELDS.items():
83+
if version_range[0] <= min_stack_version <= (version_range[1] or min_stack_version):
84+
if field in data_dict:
85+
del data_dict[field]
86+
return data_dict
9187

9288

9389
@migrate("7.8")

detection_rules/schemas/definitions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from marshmallow import validate
1111
from marshmallow_dataclass import NewType
12+
from semver import Version
1213

1314
ASSET_TYPE = "security_rule"
1415
SAVED_OBJECT_TYPE = "security-rule"
@@ -28,6 +29,11 @@
2829
BRANCH_PATTERN = f'{VERSION_PATTERN}|^master$'
2930

3031
NON_DATASET_PACKAGES = ['apm', 'endpoint', 'system', 'windows', 'cloud_defend', 'network_traffic']
32+
NON_PUBLIC_FIELDS = {
33+
"related_integrations": (Version.parse('8.3.0'), None),
34+
"required_fields": (Version.parse('8.3.0'), None),
35+
"setup": (Version.parse('8.3.0'), None)
36+
}
3137
INTERVAL_PATTERN = r'^\d+[mshd]$'
3238
TACTIC_URL = r'^https://attack.mitre.org/tactics/TA[0-9]+/$'
3339
TECHNIQUE_URL = r'^https://attack.mitre.org/techniques/T[0-9]+/$'

0 commit comments

Comments
 (0)