|
4 | 4 | # 2.0.
|
5 | 5 |
|
6 | 6 | """CLI commands for internal detection_rules dev team."""
|
| 7 | +import dataclasses |
7 | 8 | import hashlib
|
8 | 9 | import io
|
9 | 10 | import json
|
|
23 | 24 | from .main import root
|
24 | 25 | from .misc import PYTHON_LICENSE, add_client, GithubClient, Manifest, client_error, getdefault
|
25 | 26 | from .packaging import PACKAGE_FILE, Package, manage_versions, RELEASE_DIR
|
26 |
| -from .rule import Rule |
| 27 | +from .rule import TOMLRule, TOMLRuleContents, BaseQueryRuleData |
27 | 28 | from .rule_loader import get_rule
|
28 |
| -from .utils import get_path |
29 |
| - |
| 29 | +from .utils import get_path, dict_hash |
30 | 30 |
|
31 | 31 | RULES_DIR = get_path('rules')
|
32 | 32 |
|
@@ -96,7 +96,7 @@ def kibana_diff(rule_id, repo, branch, threads):
|
96 | 96 | repo_hashes = {r.id: r.get_hash() for r in rules.values()}
|
97 | 97 |
|
98 | 98 | kibana_rules = {r['rule_id']: r for r in get_kibana_rules(repo=repo, branch=branch, threads=threads).values()}
|
99 |
| - kibana_hashes = {r['rule_id']: Rule.dict_hash(r) for r in kibana_rules.values()} |
| 99 | + kibana_hashes = {r['rule_id']: dict_hash(r) for r in kibana_rules.values()} |
100 | 100 |
|
101 | 101 | missing_from_repo = list(set(kibana_hashes).difference(set(repo_hashes)))
|
102 | 102 | missing_from_kibana = list(set(repo_hashes).difference(set(kibana_hashes)))
|
@@ -309,17 +309,27 @@ def deprecate_rule(ctx: click.Context, rule_file: str):
|
309 | 309 | version_info = load_versions()
|
310 | 310 | rule_file = Path(rule_file)
|
311 | 311 | contents = pytoml.loads(rule_file.read_text())
|
312 |
| - rule = Rule(path=rule_file, contents=contents) |
| 312 | + rule = TOMLRule(path=rule_file, contents=contents) |
313 | 313 |
|
314 | 314 | if rule.id not in version_info:
|
315 | 315 | click.echo('Rule has not been version locked and so does not need to be deprecated. '
|
316 | 316 | 'Delete the file or update the maturity to `development` instead')
|
317 | 317 | ctx.exit()
|
318 | 318 |
|
319 | 319 | today = time.strftime('%Y/%m/%d')
|
320 |
| - rule.metadata.update(updated_date=today, deprecation_date=today, maturity='deprecated') |
| 320 | + |
| 321 | + new_meta = dataclasses.replace(rule.contents.metadata, |
| 322 | + updated_date=today, |
| 323 | + deprecation_date=today, |
| 324 | + maturity='deprecated') |
| 325 | + contents = dataclasses.replace(rule.contents, metadata=new_meta) |
321 | 326 | deprecated_path = get_path('rules', '_deprecated', rule_file.name)
|
322 |
| - rule.save(new_path=deprecated_path, as_rule=True) |
| 327 | + |
| 328 | + # create the new rule and save it |
| 329 | + new_rule = TOMLRule(contents=contents, path=Path(deprecated_path)) |
| 330 | + new_rule.save_toml() |
| 331 | + |
| 332 | + # remove the old rule |
323 | 333 | rule_file.unlink()
|
324 | 334 | click.echo(f'Rule moved to {deprecated_path} - remember to git add this file')
|
325 | 335 |
|
@@ -375,27 +385,31 @@ def event_search(query, index, language, date_range, count, max_results, verbose
|
375 | 385 | def rule_event_search(ctx, rule_file, rule_id, date_range, count, max_results, verbose,
|
376 | 386 | elasticsearch_client: Elasticsearch = None):
|
377 | 387 | """Search using a rule file against an Elasticsearch instance."""
|
378 |
| - rule = None |
| 388 | + rule: TOMLRule |
379 | 389 |
|
380 | 390 | if rule_id:
|
381 | 391 | rule = get_rule(rule_id, verbose=False)
|
382 | 392 | elif rule_file:
|
383 |
| - rule = Rule(rule_file, load_dump(rule_file)) |
| 393 | + rule = TOMLRule(path=rule_file, contents=TOMLRuleContents.from_dict(load_dump(rule_file))) |
384 | 394 | else:
|
385 | 395 | client_error('Must specify a rule file or rule ID')
|
386 | 396 |
|
387 |
| - if rule.query and rule.contents.get('language'): |
| 397 | + if isinstance(rule.contents.data, BaseQueryRuleData): |
388 | 398 | if verbose:
|
389 | 399 | click.echo(f'Searching rule: {rule.name}')
|
390 | 400 |
|
391 |
| - rule_lang = rule.contents.get('language') |
| 401 | + data = rule.contents.data |
| 402 | + rule_lang = data.language |
| 403 | + |
392 | 404 | if rule_lang == 'kuery':
|
393 |
| - language = None |
| 405 | + language_flag = None |
394 | 406 | elif rule_lang == 'eql':
|
395 |
| - language = True |
| 407 | + language_flag = True |
396 | 408 | else:
|
397 |
| - language = False |
398 |
| - ctx.invoke(event_search, query=rule.query, index=rule.contents.get('index', ['*']), language=language, |
| 409 | + language_flag = False |
| 410 | + |
| 411 | + index = data.index or ['*'] |
| 412 | + ctx.invoke(event_search, query=data.query, index=index, language=language_flag, |
399 | 413 | date_range=date_range, count=count, max_results=max_results, verbose=verbose,
|
400 | 414 | elasticsearch_client=elasticsearch_client)
|
401 | 415 | else:
|
|
0 commit comments