Update cardinality field in schema for threshold rules (#1349)
* Make cardinality array in schema for threshold rules
* update master, 7.12, 7.13, and 7.14 schemas with cardinality fix
* fix 7.12 downgrade to handle cardinality as an array
* Add two new rules to detect agent spoofing
Co-authored-by: Ross Wolf <31489089+rw-access@users.noreply.github.com>
(cherry picked from commit 163d9e3864)
This commit is contained in:
committed by
github-actions[bot]
parent
34b37c0bfd
commit
e9b2ebab2d
@@ -183,7 +183,7 @@ class BaseRuleData(MarshmallowDataclassMixin):
|
||||
def save_schema(cls):
|
||||
"""Save the schema as a jsonschema."""
|
||||
fields: List[dataclasses.Field] = dataclasses.fields(cls)
|
||||
type_field = next(field for field in fields if field.name == "type")
|
||||
type_field = next(f for f in fields if f.name == "type")
|
||||
rule_type = typing.get_args(type_field.type)[0] if cls != BaseRuleData else "base"
|
||||
schema = cls.jsonschema()
|
||||
version_dir = SCHEMA_DIR / "master"
|
||||
@@ -256,9 +256,9 @@ class ThresholdQueryRuleData(QueryRuleData):
|
||||
field: str
|
||||
value: definitions.ThresholdValue
|
||||
|
||||
field: List[definitions.NonEmptyStr]
|
||||
field: definitions.CardinalityFields
|
||||
value: definitions.ThresholdValue
|
||||
cardinality: Optional[ThresholdCardinality]
|
||||
cardinality: Optional[List[ThresholdCardinality]]
|
||||
|
||||
type: Literal["threshold"]
|
||||
threshold: ThresholdMapping
|
||||
|
||||
@@ -206,5 +206,5 @@ def toml_write(rule_contents, outfile=None):
|
||||
_do_write(data, _contents)
|
||||
|
||||
finally:
|
||||
if needs_close:
|
||||
if needs_close and hasattr(outfile, "close"):
|
||||
outfile.close()
|
||||
|
||||
@@ -123,7 +123,7 @@ def downgrade_threshold_to_7_11(version: Version, api_contents: dict) -> dict:
|
||||
if len(threshold_field) > 1:
|
||||
raise ValueError('Cannot downgrade a threshold rule that has multiple threshold fields defined')
|
||||
|
||||
if threshold.get('cardinality', {}).get('field') or threshold.get('cardinality', {}).get('value'):
|
||||
if threshold.get('cardinality'):
|
||||
raise ValueError('Cannot downgrade a threshold rule that has a defined cardinality')
|
||||
|
||||
api_contents = api_contents.copy()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
"""Custom shared definitions for schemas."""
|
||||
|
||||
from typing import Literal, Final
|
||||
from typing import List, Literal, Final
|
||||
|
||||
from marshmallow import validate
|
||||
from marshmallow_dataclass import NewType
|
||||
@@ -44,19 +44,21 @@ TIMELINE_TEMPLATES: Final[dict] = {
|
||||
}
|
||||
|
||||
|
||||
NonEmptyStr = NewType('NonEmptyStr', str, validate=validate.Length(min=1))
|
||||
|
||||
BranchVer = NewType('BranchVer', str, validate=validate.Regexp(BRANCH_PATTERN))
|
||||
CardinalityFields = NewType('CardinalityFields', List[NonEmptyStr], validate=validate.Length(min=0, max=3))
|
||||
CodeString = NewType("CodeString", str)
|
||||
ConditionSemVer = NewType('ConditionSemVer', str, validate=validate.Regexp(CONDITION_VERSION_PATTERN))
|
||||
Date = NewType('Date', str, validate=validate.Regexp(DATE_PATTERN))
|
||||
FilterLanguages = Literal["kuery", "lucene"]
|
||||
Interval = NewType('Interval', str, validate=validate.Regexp(INTERVAL_PATTERN))
|
||||
PositiveInteger = NewType('PositiveInteger', int, validate=validate.Range(min=1))
|
||||
Markdown = NewType("MarkdownField", CodeString)
|
||||
Maturity = Literal['development', 'experimental', 'beta', 'production', 'deprecated']
|
||||
MaxSignals = NewType("MaxSignals", int, validate=validate.Range(min=1))
|
||||
NonEmptyStr = NewType('NonEmptyStr', str, validate=validate.Length(min=1))
|
||||
Operator = Literal['equals']
|
||||
OSType = Literal['windows', 'linux', 'macos']
|
||||
PositiveInteger = NewType('PositiveInteger', int, validate=validate.Range(min=1))
|
||||
RiskScore = NewType("MaxSignals", int, validate=validate.Range(min=1, max=100))
|
||||
RuleType = Literal['query', 'saved_query', 'machine_learning', 'eql', 'threshold', 'threat_match']
|
||||
SemVer = NewType('SemVer', str, validate=validate.Regexp(VERSION_PATTERN))
|
||||
|
||||
@@ -919,35 +919,46 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"cardinality": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"field": {
|
||||
"items": {
|
||||
"default": "",
|
||||
"type": "string"
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"field": {
|
||||
"description": "CardinalityFields",
|
||||
"items": {
|
||||
"description": "NonEmptyStr",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"type": "array"
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
|
||||
@@ -112,6 +112,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"enum": [
|
||||
"equals"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
@@ -151,6 +154,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"enum": [
|
||||
"equals"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"severity": {
|
||||
@@ -178,6 +184,9 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"framework": {
|
||||
"enum": [
|
||||
"MITRE ATT&CK"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"tactic": {
|
||||
@@ -265,30 +274,35 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"cardinality": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
"type": "array"
|
||||
},
|
||||
"field": {
|
||||
"description": "CardinalityFields",
|
||||
"items": {
|
||||
"description": "NonEmptyStr",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"type": "array"
|
||||
},
|
||||
"value": {
|
||||
@@ -336,6 +350,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"threshold"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -274,30 +274,35 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"cardinality": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
"type": "array"
|
||||
},
|
||||
"field": {
|
||||
"description": "CardinalityFields",
|
||||
"items": {
|
||||
"description": "NonEmptyStr",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"type": "array"
|
||||
},
|
||||
"value": {
|
||||
|
||||
@@ -274,30 +274,35 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"cardinality": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"description": "ThresholdValue",
|
||||
"format": "integer",
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"required": [
|
||||
"field",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
"type": "array"
|
||||
},
|
||||
"field": {
|
||||
"description": "CardinalityFields",
|
||||
"items": {
|
||||
"description": "NonEmptyStr",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"type": "array"
|
||||
},
|
||||
"value": {
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
|
||||
"7.14.0":
|
||||
beats: "master" # TODO: 7.14.x
|
||||
ecs: "1.10.0"
|
||||
ecs: "master" # TODO: master came out after 7.13.0 release
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ PyYAML~=5.3
|
||||
eql==0.9.9
|
||||
elasticsearch~=7.9
|
||||
XlsxWriter~=1.3.6
|
||||
marshmallow~=3.10.0
|
||||
marshmallow~=3.12.2
|
||||
marshmallow-dataclass[union]~=8.4
|
||||
|
||||
# test deps
|
||||
|
||||
@@ -86,10 +86,10 @@ class TestSchemas(unittest.TestCase):
|
||||
cls.v712_threshold_rule = dict(copy.deepcopy(cls.v79_threshold_contents), threshold={
|
||||
'field': ['destination.bytes', 'process.args'],
|
||||
'value': 75,
|
||||
'cardinality': {
|
||||
'cardinality': [{
|
||||
'field': 'user.name',
|
||||
'value': 2
|
||||
}
|
||||
}]
|
||||
})
|
||||
|
||||
def test_query_downgrade(self):
|
||||
|
||||
Reference in New Issue
Block a user