Files
sigma-rules/tests/kuery/test_evaluator.py
T
Sergey Polzunov 1fb60d6475 fix: type hinting fixes and additional code checks (#4790)
* first pass

* Adding a dedicated code checking workflow

* Type fixes

* linting config and python version bump

* Type hints

* Drop incorrect config option

* More fixes

* Style fixes

* CI adjustments

* Pyproject fixes

* CI & pyproject fixes

* Proper version bump

* Tests formatting

* Resolve cirtular dependency

* Test fixes

* Make sure the tests are formatted correctly

* Check tweaks

* Bumping python version in CI images

* Pin marshmallow do 3.x because 4.x is not supported

* License fix

* Convert path to str

* Making myself a codeowner

* Missing kwargs param

* Adding a missing kwargs to `set_score`

* Update .github/CODEOWNERS

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>

* Dropping unnecessary raise

* Dropping skipped test

* Drop unnecessary var

* Drop unused commented-out func

* Disable typehinting for the whole func

* Update linting command

* Invalid type hist on the input param

* Incorrect field type

* Incorrect value used fix

* Stricter values check

* Simpler function call

* Type condition fix

* TOML formatter fix

* Simpligy output conditions

* Formatting

* Use proper types instead of aliases

* MITRE attack fixes

* Using pathlib.Path for an argument

* Use proper method to update a set from a dict

* First round of `ruff` fixes

* More fixes

* More fixes

* Hack against cyclic dependency

* Ignore `PLC0415`

* Remove unused markers

* Cleanup

* Fixing the incorrect condition

* Update .github/CODEOWNERS

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>

* Set explicit default values for optional fields

* Update the guidelines

* Adding None Defaults

---------

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>
Co-authored-by: eric-forte-elastic <eric.forte@elastic.co>
2025-07-01 08:20:55 -05:00

114 lines
4.2 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.
import unittest
import kql
document = {
"number": 1,
"boolean": True,
"ip": "192.168.16.3",
"string": "hello world",
"string_list": ["hello world", "example"],
"number_list": [1, 2, 3],
"boolean_list": [True, False],
"structured": [{"a": [{"b": 1}]}],
}
class EvaluatorTests(unittest.TestCase):
def evaluate(self, source_text):
evaluator = kql.get_evaluator(source_text, optimize=False)
return evaluator(document)
def test_single_value(self):
self.assertTrue(self.evaluate("number:1"))
self.assertTrue(self.evaluate('number:"1"'))
self.assertTrue(self.evaluate("boolean:true"))
self.assertTrue(self.evaluate('string:"hello world"'))
self.assertFalse(self.evaluate("number:0"))
self.assertFalse(self.evaluate("boolean:false"))
self.assertFalse(self.evaluate('string:"missing"'))
def test_list_value(self):
self.assertTrue(self.evaluate("number_list:1"))
self.assertTrue(self.evaluate("number_list:2"))
self.assertTrue(self.evaluate("number_list:3"))
self.assertTrue(self.evaluate("boolean_list:true"))
self.assertTrue(self.evaluate("boolean_list:false"))
self.assertTrue(self.evaluate('string_list:"hello world"'))
self.assertTrue(self.evaluate("string_list:example"))
self.assertFalse(self.evaluate("number_list:4"))
self.assertFalse(self.evaluate('string_list:"missing"'))
def test_and_values(self):
self.assertTrue(self.evaluate("number_list:(1 and 2)"))
self.assertTrue(self.evaluate("boolean_list:(false and true)"))
self.assertFalse(self.evaluate('string:("missing" and "hello world")'))
self.assertFalse(self.evaluate("number:(0 and 1)"))
self.assertFalse(self.evaluate("boolean:(false and true)"))
def test_not_value(self):
self.assertTrue(self.evaluate("number_list:1"))
self.assertFalse(self.evaluate("not number_list:1"))
self.assertFalse(self.evaluate("number_list:(not 1)"))
def test_or_values(self):
self.assertTrue(self.evaluate("number:(0 or 1)"))
self.assertTrue(self.evaluate("number:(1 or 2)"))
self.assertTrue(self.evaluate("boolean:(false or true)"))
self.assertTrue(self.evaluate('string:("missing" or "hello world")'))
self.assertFalse(self.evaluate("number:(0 or 3)"))
def test_and_expr(self):
self.assertTrue(self.evaluate("number:1 and boolean:true"))
self.assertFalse(self.evaluate("number:1 and boolean:false"))
def test_or_expr(self):
self.assertTrue(self.evaluate("number:1 or boolean:false"))
self.assertFalse(self.evaluate("number:0 or boolean:false"))
def test_range(self):
self.assertTrue(self.evaluate("number < 2"))
self.assertFalse(self.evaluate("number > 2"))
def test_cidr_match(self):
self.assertTrue(self.evaluate("ip:192.168.0.0/16"))
self.assertFalse(self.evaluate("ip:10.0.0.0/8"))
def test_quoted_wildcard(self):
self.assertFalse(self.evaluate("string:'*'"))
self.assertFalse(self.evaluate("string:'?'"))
def test_wildcard(self):
self.assertTrue(self.evaluate("string:hello*"))
self.assertTrue(self.evaluate("string:*world"))
self.assertFalse(self.evaluate("string:foobar*"))
def test_field_exists(self):
self.assertTrue(self.evaluate("number:*"))
self.assertTrue(self.evaluate("boolean:*"))
self.assertTrue(self.evaluate("ip:*"))
self.assertTrue(self.evaluate("string:*"))
self.assertTrue(self.evaluate("string_list:*"))
self.assertTrue(self.evaluate("number_list:*"))
self.assertTrue(self.evaluate("boolean_list:*"))
self.assertFalse(self.evaluate("a:*"))
def test_flattening(self):
self.assertTrue(self.evaluate("structured.a.b:*"))
self.assertTrue(self.evaluate("structured.a.b:1"))
self.assertFalse(self.evaluate("structured.a.b:2"))