Files
sigma-rules/detection_rules/schemas/v7_8.py
T
Ross Wolf c0af222e7e Move Rule into a dataclass (#1029)
* WIP: Convert Rule to a dataclass
* Fix make release
* Lint fixes
* Remove dead code
* Fix lint and tests
* Use Python 3.8 in GitHub actions
* Update README to 3.8+
* Add Python 3.8 assertion
* Fix is_dirty property
* Remove incorrect pop from contents
* Add mixin with from_dict() and to_dict() methods
* Bypass validation for deprecated rules
* Fix rule_prompt
* Fix dict_hash usage
* Fix rule_event_search
* Switch to definitions.Date
* Fix toml-lint command, ignoring 'unneeded defaults'
* Moved severity Literal to definitions.Severity
* Remove BaseMarshmallowDataclass
* Fix lint and tests
* Add maturity to metadata for rule prompt loop
* Fix typo in devtools
* Use rule loader to load single rule in toml-lint
* Add Schema hint to __schema method
* Add MITREAttackURL definition
* Fix is_dirty to compare sha<-->sha
* Normalize the autoformatted rule output for API and toml-lint
* Make the package hash match
* Make the rule object mutable but not rule contents
* Restore the rules
2021-03-24 10:24:32 -06:00

128 lines
5.5 KiB
Python

# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License
# 2.0; you may not use this file except in compliance with the Elastic License
# 2.0.
"""Definitions for rule metadata and schemas."""
import jsl
from .base import BaseApiSchema, MarkdownField
from .definitions import INTERVAL_PATTERN, TACTIC_URL, TECHNIQUE_URL, MACHINE_LEARNING, SAVED_QUERY, QUERY
from ..attack import tactics, tactics_map, technique_id_list
# kibana/.../siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts
# /detection_engine/routes/schemas/schemas.ts
# rule_id is required here
# output_index is not allowed (and instead the space index must be used)
# immutable defaults to true instead of to false and if it is there can only be true
# enabled defaults to false instead of true
# version is a required field that must exist
# rule types
class Filters(jsl.Document):
"""Intermediate schema for handling DSL-like filters."""
class FilterMetadata(jsl.Document):
negate = jsl.BooleanField()
type = jsl.StringField()
key = jsl.StringField()
value = jsl.StringField()
disabled = jsl.BooleanField()
indexRefName = jsl.StringField()
alias = jsl.StringField() # null acceptable
params = jsl.DictField(properties={'query': jsl.StringField()})
class FilterQuery(jsl.Document):
match = jsl.DictField({
'event.action': jsl.DictField(properties={
'query': jsl.StringField(),
'type': jsl.StringField()
})
})
class FilterState(jsl.Document):
store = jsl.StringField()
class FilterExists(jsl.Document):
field = jsl.StringField()
exists = jsl.DocumentField(FilterExists)
meta = jsl.DocumentField(FilterMetadata)
state = jsl.DocumentField(FilterState, name='$state')
query = jsl.DictField()
class Threat(jsl.Document):
"""Threat framework mapping such as MITRE ATT&CK."""
class ThreatTactic(jsl.Document):
id = jsl.StringField(enum=tactics_map.values(), required=True)
name = jsl.StringField(enum=tactics, required=True)
reference = jsl.StringField(pattern=TACTIC_URL, required=True)
class ThreatTechnique(jsl.Document):
id = jsl.StringField(enum=technique_id_list, required=True)
name = jsl.StringField(required=True)
reference = jsl.StringField(pattern=TECHNIQUE_URL, required=True)
framework = jsl.StringField(default='MITRE ATT&CK', required=True)
tactic = jsl.DocumentField(ThreatTactic, required=True)
technique = jsl.ArrayField(jsl.DocumentField(ThreatTechnique), required=True)
class ApiSchema78(BaseApiSchema):
"""Schema for siem rule in API format."""
STACK_VERSION = "7.8"
RULE_TYPES = [MACHINE_LEARNING, SAVED_QUERY, QUERY]
actions = jsl.ArrayField(required=False)
description = jsl.StringField(required=True)
# api defaults to false if blank
enabled = jsl.BooleanField(default=False, required=False)
# _ required since `from` is a reserved word in python
from_ = jsl.StringField(required=False, default='now-6m', name='from')
false_positives = jsl.ArrayField(jsl.StringField(), required=False)
filters = jsl.ArrayField(jsl.DocumentField(Filters))
interval = jsl.StringField(pattern=INTERVAL_PATTERN, default='5m', required=False)
max_signals = jsl.IntField(minimum=1, required=False, default=100) # cap a max?
meta = jsl.DictField(required=False)
name = jsl.StringField(required=True)
note = MarkdownField(required=False)
# output_index =jsl.StringField(required=False) # this is NOT allowed!
references = jsl.ArrayField(jsl.StringField(), required=False)
risk_score = jsl.IntField(minimum=0, maximum=100, required=True, default=21)
severity = jsl.StringField(enum=['low', 'medium', 'high', 'critical'], default='low', required=True)
tags = jsl.ArrayField(jsl.StringField(), required=False)
throttle = jsl.StringField(required=False)
timeline_id = jsl.StringField(required=False)
timeline_title = jsl.StringField(required=False)
to = jsl.StringField(required=False, default='now')
type = jsl.StringField(enum=[MACHINE_LEARNING, QUERY, SAVED_QUERY], required=True)
threat = jsl.ArrayField(jsl.DocumentField(Threat), required=False, min_items=1)
with jsl.Scope(MACHINE_LEARNING) as ml_scope:
ml_scope.anomaly_threshold = jsl.IntField(required=True, minimum=0)
ml_scope.machine_learning_job_id = jsl.StringField(required=True)
ml_scope.type = jsl.StringField(enum=[MACHINE_LEARNING], required=True, default=MACHINE_LEARNING)
with jsl.Scope(SAVED_QUERY) as saved_id_scope:
saved_id_scope.index = jsl.ArrayField(jsl.StringField(), required=False)
saved_id_scope.saved_id = jsl.StringField(required=True)
saved_id_scope.type = jsl.StringField(enum=[SAVED_QUERY], required=True, default=SAVED_QUERY)
with jsl.Scope(QUERY) as query_scope:
query_scope.index = jsl.ArrayField(jsl.StringField(), required=False)
# this is not required per the API but we will enforce it here
query_scope.language = jsl.StringField(enum=['kuery', 'lucene'], required=True, default='kuery')
query_scope.query = jsl.StringField(required=True)
query_scope.type = jsl.StringField(enum=[QUERY], required=True, default=QUERY)
with jsl.Scope(jsl.DEFAULT_ROLE) as default_scope:
default_scope.type = type