2020-08-13 22:46:25 +02:00
|
|
|
import pytest
|
2020-08-13 23:44:42 +02:00
|
|
|
|
Graph Equivalence (#449)
* new packages for graph and object-based semantic equivalence
* new method graphically_equivalent for Environment, move equivalence methods out
* object equivalence function, methods used for object-based moved here.
* new graph_equivalence methods
* add notes
* add support for versioning checks (default disabled)
* new tests to cover graph equivalence and new methods
* added more imports to environment.py to prevent breaking changes
* variable changes, new fields for checks, reset depth check per call
* flexibility when object is not available on graph.
* refactor debug logging message
* new file stix2.equivalence.graph_equivalence.rst and stix2.equivalence.object_equivalence.rst for docs
* API documentation for new modules
* additional text required to build docs
* add more test methods for list_semantic_check an graphically_equivalent/versioning
* add logging debug messages, code clean-up
* include individual scoring on results dict, fix issue on list_semantic_check not keeping highest score
* include results as summary in prop_scores, minor tweaks
* Update __init__.py
doctrings update
* apply feedback from pull request
- rename semantic_check to reference_check
- rename modules to graph and object respectively to eliminate redundancy
- remove created_by_ref and object_marking_refs from graph WEIGHTS and rebalance
* update docs/ entries
* add more checks, make max score based on actual objects checked instead of the full list, only create entry when type is present in WEIGHTS dictionary
update tests to reflect changes
* rename package patterns -> pattern
* documentation, moving weights around
* more documentation moving
* rename WEIGHTS variable for graph_equivalence
2020-10-16 17:35:26 +02:00
|
|
|
from stix2.equivalence.pattern import (
|
2020-08-13 23:44:42 +02:00
|
|
|
equivalent_patterns, find_equivalent_patterns,
|
2020-08-13 23:09:04 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# # # #
|
|
|
|
# # Observation expression equivalence tests # #
|
|
|
|
# # # #
|
2020-08-13 22:46:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] OR [a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR [a:b=1] OR [a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_dupe_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] AND [a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY [a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_dupe_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
("[a:b=1]", "([a:b=1])"),
|
|
|
|
("(((([a:b=1]))))", "([a:b=1])"),
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] AND [a:b=3])",
|
|
|
|
"[a:b=1] AND [a:b=2] AND [a:b=3]",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] AND [a:b=2]) AND [a:b=3]",
|
|
|
|
"[a:b=1] AND ([a:b=2] AND [a:b=3])",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR ([a:b=2] OR [a:b=3])",
|
|
|
|
"[a:b=1] OR [a:b=2] OR [a:b=3]",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] OR [a:b=2]) OR [a:b=3]",
|
|
|
|
"[a:b=1] OR ([a:b=2] OR [a:b=3])",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY [a:b=3])",
|
|
|
|
"[a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=2]) FOLLOWEDBY [a:b=3]",
|
|
|
|
"[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY [a:b=3])",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] AND ([a:b=3] AND [a:b=4])) AND ([a:b=5])",
|
|
|
|
"([a:b=1] AND ([a:b=2] AND [a:b=3]) AND ([a:b=4] AND [a:b=5]))",
|
2020-08-13 23:44:42 +02:00
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_flatten_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"([a:b=1] AND [a:b=2]) OR [a:b=3]",
|
|
|
|
"[a:b=1] AND ([a:b=2] OR [a:b=3])",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] OR [a:b=2]) FOLLOWEDBY [a:b=3]",
|
|
|
|
"[a:b=1] OR ([a:b=2] FOLLOWEDBY [a:b=3])",
|
|
|
|
),
|
|
|
|
("[a:b=1]", "([a:b=1]) REPEATS 2 TIMES"),
|
|
|
|
("(((([a:b=1]))))", "([a:b=1] REPEATS 2 TIMES)"),
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] AND [a:b=3]) WITHIN 2 SECONDS",
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS AND [a:b=2] AND [a:b=3]",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR ([a:b=2] OR [a:b=3]) WITHIN 2 SECONDS",
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS OR [a:b=2] OR [a:b=3]",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY [a:b=3]) WITHIN 2 SECONDS",
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]",
|
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_flatten_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] AND [a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2] AND [a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR [a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2] OR [a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR ([a:b=2] AND [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=3] AND [a:b=2]) OR [a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS AND [a:b=2] REPEATS 2 TIMES",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2] REPEATS 2 TIMES AND [a:b=1] WITHIN 2 SECONDS",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_order_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY [a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2] FOLLOWEDBY [a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS AND [a:b=2] REPEATS 2 TIMES",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] REPEATS 2 TIMES AND [a:b=2] WITHIN 2 SECONDS",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_order_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] OR ([a:b=1] AND [a:b=2])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] OR ([a:b=1] FOLLOWEDBY [a:b=2])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=3] AND [a:b=1]) OR ([a:b=1] AND [a:b=2] AND [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=3] AND [a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=4] FOLLOWEDBY [a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] FOLLOWEDBY [a:b=3]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=2]) OR (([a:b=1] FOLLOWEDBY [a:b=2]) AND [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] FOLLOWEDBY [a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] AND [a:b=2]) OR (([a:b=1] AND [a:b=2]) FOLLOWEDBY [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] AND [a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_absorb_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"([a:b=1] AND [a:b=2]) OR ([a:b=2] AND [a:b=3] AND [a:b=4])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] AND [a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=2] FOLLOWEDBY [a:b=1]) OR ([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2] FOLLOWEDBY [a:b=1]",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_absorb_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] OR [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] AND [a:b=2]) OR ([a:b=1] AND [a:b=3])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY ([a:b=2] OR [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=2]) OR ([a:b=1] FOLLOWEDBY [a:b=3])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] AND ([a:b=3] OR [a:b=4]))",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] AND [a:b=2] AND [a:b=3]) OR ([a:b=1] AND [a:b=2] AND [a:b=4])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY ([a:b=3] OR [a:b=4]))",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=4])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] OR [a:b=2]) AND ([a:b=3] OR [a:b=4])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] AND [a:b=3]) OR ([a:b=1] AND [a:b=4]) OR ([a:b=2] AND [a:b=3]) OR ([a:b=2] AND [a:b=4])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"([a:b=1] OR [a:b=2]) FOLLOWEDBY ([a:b=3] OR [a:b=4])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=4]) OR ([a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=2] FOLLOWEDBY [a:b=4])",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_dnf_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1] AND [a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] OR [a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] AND ([a:b=2] OR [a:b=3])",
|
2020-08-13 23:44:42 +02:00
|
|
|
"([a:b=1] AND [a:b=2]) OR [a:b=3]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1] WITHIN 2 SECONDS",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1] REPEATS 2 TIMES",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_obs_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
# # # #
|
|
|
|
# # Comparison expression equivalence tests # #
|
|
|
|
# # # #
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1 AND a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND a:b=1 AND a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 OR a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 OR a:b=1 OR a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_dupe_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[(a:b=1)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(((((a:b=1)))))]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND (a:b=2 AND a:b=3)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 AND a:b=2) AND a:b=3]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 OR (a:b=2 OR a:b=3)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 OR a:b=2) OR a:b=3]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(((a:b=1 AND ((a:b=2) AND a:b=3) AND (a:b=4))))]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 AND a:b=2 AND a:b=3 AND a:b=4]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(((a:b=1 OR ((a:b=2) OR a:b=3) OR (a:b=4))))]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 OR a:b=2 OR a:b=3 OR a:b=4]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_flatten_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1 AND a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2 AND a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 OR a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2 OR a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 OR a:b=2) AND a:b=3]",
|
|
|
|
"[a:b=3 AND (a:b=2 OR a:b=1)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_order_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1 OR (a:b=1 AND a:b=2)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND (a:b=1 OR a:b=2)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 AND a:b=2) OR (a:b=3 AND a:b=2 AND a:b=1)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 AND a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 OR a:b=2) AND (a:b=3 OR a:b=2 OR a:b=1)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 OR a:b=2]",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_absorb_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1 OR (a:b=2 AND a:b=3)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 OR a:b=2) AND (a:b=1 OR a:b=3)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND (a:b=2 OR a:b=3)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 AND a:b=2) OR (a:b=1 AND a:b=3)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 AND a:b=2) OR (a:b=3 AND a:b=4)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 OR a:b=3) AND (a:b=1 OR a:b=4) AND (a:b=2 OR a:b=3) AND (a:b=2 OR a:b=4)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 OR a:b=2) AND (a:b=3 OR a:b=4)]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 AND a:b=3) OR (a:b=1 AND a:b=4) OR (a:b=2 AND a:b=3) OR (a:b=2 AND a:b=4)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND (a:b=2 AND (a:b=3 OR a:b=4))]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[(a:b=1 AND a:b=2 AND a:b=3) OR (a:b=1 AND a:b=2 AND a:b=4)]",
|
|
|
|
),
|
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_dnf_equivalent(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[a:b=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[a:b=1 AND a:b=2]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 OR a:b=2]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[(a:b=1 AND a:b=2) OR a:b=3]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b=1 AND (a:b=2 OR a:b=3)]",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4/32']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.4']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4/24']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.0/24']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.255.4/23']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.254.0/23']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.255.4/20']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.240.0/20']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.255.4/0']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='0.0.0.0/0']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='01.02.03.04']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.4']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4/-5']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.4/-5']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4/99']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.4/99']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='foo']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='foo']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_ipv4(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.5']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='1.2.3.4/1']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='1.2.3.4/2']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv4-addr:value='foo']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv4-addr:value='bar']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_ipv4_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/128']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/112']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:0/112']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:ffff:8/111']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:fffe:0/111']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:ffff:8/104']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:ff00:0/104']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/0']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='0:0:0:0:0:0:0:0/0']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='0001:0000:0000:0000:0000:0000:0000:0001']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1::1']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='0000:0000:0000:0000:0000:0000:0000:0000']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='::']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/-5']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/-5']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/99']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/99']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='foo']",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[ipv6-addr:value='foo']",
|
2020-08-13 22:46:25 +02:00
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_ipv6(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8']",
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:9']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/1']",
|
|
|
|
"[ipv6-addr:value='1:2:3:4:5:6:7:8/2']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[ipv6-addr:value='foo']",
|
|
|
|
"[ipv6-addr:value='bar']",
|
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_ipv6_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[windows-registry-key:key = 'aaa']",
|
|
|
|
"[windows-registry-key:key = 'AAA']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[windows-registry-key:values[0].name = 'aaa']",
|
|
|
|
"[windows-registry-key:values[0].name = 'AAA']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[windows-registry-key:values[*].name = 'aaa']",
|
|
|
|
"[windows-registry-key:values[*].name = 'AAA']",
|
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_win_reg_key(patt1, patt2):
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"patt1, patt2", [
|
|
|
|
(
|
|
|
|
"[windows-registry-key:key='foo']",
|
|
|
|
"[windows-registry-key:key='bar']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[windows-registry-key:values[0].name='foo']",
|
|
|
|
"[windows-registry-key:values[0].name='bar']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[windows-registry-key:values[*].name='foo']",
|
|
|
|
"[windows-registry-key:values[*].name='bar']",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"[windows-registry-key:values[*].data='foo']",
|
|
|
|
"[windows-registry-key:values[*].data='FOO']",
|
|
|
|
),
|
2020-08-13 23:44:42 +02:00
|
|
|
],
|
2020-08-13 22:46:25 +02:00
|
|
|
)
|
|
|
|
def test_comp_special_canonicalization_win_reg_key_not_equivalent(patt1, patt2):
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
2020-08-13 23:09:04 +02:00
|
|
|
|
|
|
|
|
2020-08-14 00:45:52 +02:00
|
|
|
def test_comp_other_constant_types():
|
|
|
|
constants = [
|
|
|
|
"1.23",
|
|
|
|
"1",
|
|
|
|
"true",
|
|
|
|
"false",
|
|
|
|
"h'4fa2'",
|
|
|
|
"b'ZmpoZWll'",
|
|
|
|
"t'1982-12-31T02:14:17.232Z'",
|
|
|
|
]
|
|
|
|
|
|
|
|
pattern_template = "[a:b={}]"
|
|
|
|
for i, const1 in enumerate(constants):
|
|
|
|
for j, const2 in enumerate(constants):
|
|
|
|
patt1 = pattern_template.format(const1)
|
|
|
|
patt2 = pattern_template.format(const2)
|
|
|
|
|
|
|
|
if i == j:
|
|
|
|
assert equivalent_patterns(patt1, patt2)
|
|
|
|
else:
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
# can't use an "=" pattern with lists...
|
|
|
|
for const in constants:
|
|
|
|
patt1 = "[a:b={}]".format(const)
|
|
|
|
patt2 = "[a:b IN (1,2,3)]"
|
|
|
|
assert not equivalent_patterns(patt1, patt2)
|
|
|
|
|
|
|
|
|
2020-08-13 23:09:04 +02:00
|
|
|
# # # #
|
|
|
|
# # find_equivalent_patterns() tests # #
|
|
|
|
# # # #
|
|
|
|
|
|
|
|
def test_find_equivalent_patterns():
|
|
|
|
search_pattern = "[a:b=1]"
|
|
|
|
other_patterns = [
|
|
|
|
"[a:b=2]",
|
|
|
|
"[a:b=1]",
|
|
|
|
"[a:b=1] WITHIN 1 SECONDS",
|
|
|
|
"[a:b=1] OR ([a:b=2] AND [a:b=1])",
|
|
|
|
"[(a:b=2 OR a:b=1) AND a:b=1]",
|
|
|
|
"[c:d=1]",
|
2020-08-13 23:44:42 +02:00
|
|
|
"[a:b>1]",
|
2020-08-13 23:09:04 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
result = list(
|
2020-08-13 23:44:42 +02:00
|
|
|
find_equivalent_patterns(search_pattern, other_patterns),
|
2020-08-13 23:09:04 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
assert result == [
|
|
|
|
"[a:b=1]",
|
|
|
|
"[a:b=1] OR ([a:b=2] AND [a:b=1])",
|
|
|
|
"[(a:b=2 OR a:b=1) AND a:b=1]",
|
|
|
|
]
|