diff --git a/geneve/constraints.py b/geneve/constraints.py index 7db90647..d88e8651 100644 --- a/geneve/constraints.py +++ b/geneve/constraints.py @@ -128,7 +128,11 @@ def _solver(cls, field, value, constraints): if len(v) > 1: raise ValueError(f"Too many arguments for cardinality of '{field}': {v}") v = v[0] - cardinality = int(v) + v = int(v) + if not cardinality or cardinality == v: + cardinality = v + else: + raise ConflictError(f"is already {cardinality}, cannot set to {v}", field, k) history = cls.fields_history.setdefault(field, []) if max_attempts is None: max_attempts = _max_attempts diff --git a/geneve/events_emitter_eql.py b/geneve/events_emitter_eql.py index 6bbace36..9eef6483 100644 --- a/geneve/events_emitter_eql.py +++ b/geneve/events_emitter_eql.py @@ -171,8 +171,10 @@ def cc_join_branch(seq: List[Tuple[Constraints, List[str]]]) -> Branch: for join_col in zip(*join_rows): field0 = None for field, c in join_col: - field0 = field0 or field - constraints[0].append_constraint(field0, "join_value", (field, c)) + if field0 is None: + field0 = field + else: + constraints[0].append_constraint(field0, "join_value", (field, c)) return Branch(constraints) diff --git a/tests/reports/documents_from_queries.ipynb b/tests/reports/documents_from_queries.ipynb index c28da049..97a207d7 100644 --- a/tests/reports/documents_from_queries.ipynb +++ b/tests/reports/documents_from_queries.ipynb @@ -2092,16 +2092,50 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "3c7a3d01", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[[{'event': {'category': ['network']}, 'destination': {'ip': '1::f09b', 'port': 265}},\n", + " {'event': {'category': ['network']}, 'source': {'ip': '1::f09b', 'port': 265}, 'destination': {'ip': '1::1cf0', 'port': 22623}}],\n", + " [{'event': {'category': ['network']}, 'destination': {'ip': '1::f09b', 'port': 691}},\n", + " {'event': {'category': ['network']}, 'source': {'ip': '1::f09b', 'port': 691}, 'destination': {'ip': '1::1cf0', 'port': 57576}}],\n", + " [{'event': {'category': ['network']}, 'destination': {'ip': '1::f09b', 'port': 173}},\n", + " {'event': {'category': ['network']}, 'source': {'ip': '1::f09b', 'port': 173}, 'destination': {'ip': '1::1cf0', 'port': 3897}}],\n", + " [{'event': {'category': ['network']}, 'destination': {'ip': '1::f09b', 'port': 685}},\n", + " {'event': {'category': ['network']}, 'source': {'ip': '1::f09b', 'port': 685}, 'destination': {'ip': '1::f09b', 'port': 58442}}],\n", + " [{'event': {'category': ['network']}, 'destination': {'ip': '1::f09b', 'port': 929}},\n", + " {'event': {'category': ['network']}, 'source': {'ip': '1::f09b', 'port': 929}, 'destination': {'ip': '1::1cf0', 'port': 54423}}]]" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "emit('''\n", + " sequence\n", + " [network where _cardinality(destination.ip, 1) and destination.port < 1024] by destination.ip, destination.port\n", + " [network where _cardinality(destination.ip, 2) and destination.port > 1024] by source.ip, source.port\n", + "''', count=5)" + ] + }, + { + "cell_type": "markdown", + "id": "5090731a", + "metadata": {}, "source": [ "## Any oddities?" ] }, { "cell_type": "markdown", - "id": "5090731a", + "id": "c3fb7b7a", "metadata": {}, "source": [ "Did you find anything odd reviewing the report or playing with the documents emitter?\n", diff --git a/tests/test_constraints.py b/tests/test_constraints.py index bab6ad37..098acdba 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -950,6 +950,13 @@ ], """Too many arguments for cardinality of 'test_var': (0, 1)""", ), + ( + [ + ("cardinality", 1), + ("cardinality", 0), + ], + "Unsolvable constraints cardinality: test_var (is already 1, cannot set to 0)", + ), ] diff --git a/tests/test_emitter_queries.py b/tests/test_emitter_queries.py index 8fe046d0..6fdc46b4 100644 --- a/tests/test_emitter_queries.py +++ b/tests/test_emitter_queries.py @@ -557,6 +557,55 @@ [{"event": {"category": ["network"]}, "destination": {"ip": "1::f14", "port": 443}}], ], ), + ( + """sequence + [network where _cardinality(destination.ip, 1) and destination.port < 1024] by destination.ip, destination.port + [network where _cardinality(destination.ip, 2) and destination.port > 1024] by source.ip, source.port + """, + 1, + [ + [ + {"event": {"category": ["network"]}, "destination": {"ip": "1::f09b", "port": 265}}, + { + "event": {"category": ["network"]}, + "source": {"ip": "1::f09b", "port": 265}, + "destination": {"ip": "1::1cf0", "port": 22623}, + }, + ], + [ + {"event": {"category": ["network"]}, "destination": {"ip": "1::f09b", "port": 691}}, + { + "event": {"category": ["network"]}, + "source": {"ip": "1::f09b", "port": 691}, + "destination": {"ip": "1::1cf0", "port": 57576}, + }, + ], + [ + {"event": {"category": ["network"]}, "destination": {"ip": "1::f09b", "port": 173}}, + { + "event": {"category": ["network"]}, + "source": {"ip": "1::f09b", "port": 173}, + "destination": {"ip": "1::1cf0", "port": 3897}, + }, + ], + [ + {"event": {"category": ["network"]}, "destination": {"ip": "1::f09b", "port": 685}}, + { + "event": {"category": ["network"]}, + "source": {"ip": "1::f09b", "port": 685}, + "destination": {"ip": "1::f09b", "port": 58442}, + }, + ], + [ + {"event": {"category": ["network"]}, "destination": {"ip": "1::f09b", "port": 929}}, + { + "event": {"category": ["network"]}, + "source": {"ip": "1::f09b", "port": 929}, + "destination": {"ip": "1::1cf0", "port": 54423}, + }, + ], + ], + ), ]