From f47809eaafacac4dff11c749d10f7c437dd6efa6 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Mon, 2 Jul 2018 18:19:54 -0400 Subject: [PATCH] Add "contains" filter This allows filtering on, for example, observed-data objects that contain a specific cyber observable. --- docs/guide/datastore.ipynb | 1 + stix2/datastore/filters.py | 7 ++++++- stix2/test/test_datastore_filters.py | 26 +++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/guide/datastore.ipynb b/docs/guide/datastore.ipynb index 2fabf88..1c88cf1 100644 --- a/docs/guide/datastore.ipynb +++ b/docs/guide/datastore.ipynb @@ -369,6 +369,7 @@ "* < \n", "* ```>=```\n", "* <=\n", + "* contains\n", "\n", "Value types of the property values must be one of these (Python) types:\n", "\n", diff --git a/stix2/datastore/filters.py b/stix2/datastore/filters.py index a32b14a..03585ad 100644 --- a/stix2/datastore/filters.py +++ b/stix2/datastore/filters.py @@ -9,7 +9,7 @@ from datetime import datetime from stix2.utils import format_datetime """Supported filter operations""" -FILTER_OPS = ['=', '!=', 'in', '>', '<', '>=', '<='] +FILTER_OPS = ['=', '!=', 'in', '>', '<', '>=', '<=', 'contains'] """Supported filter value types""" FILTER_VALUE_TYPES = [bool, dict, float, int, list, str, tuple] @@ -100,6 +100,11 @@ class Filter(collections.namedtuple("Filter", ['property', 'op', 'value'])): return stix_obj_property != self.value elif self.op == "in": return stix_obj_property in self.value + elif self.op == "contains": + if isinstance(self.value, dict): + return self.value in stix_obj_property.values() + else: + return self.value in stix_obj_property elif self.op == ">": return stix_obj_property > self.value elif self.op == "<": diff --git a/stix2/test/test_datastore_filters.py b/stix2/test/test_datastore_filters.py index 5ffd051..4ec6e7e 100644 --- a/stix2/test/test_datastore_filters.py +++ b/stix2/test/test_datastore_filters.py @@ -100,7 +100,9 @@ filters = [ Filter("object_marking_refs", "=", "marking-definition--613f2e26-0000-0000-0000-b8e91df99dc9"), Filter("granular_markings.selectors", "in", "description"), Filter("external_references.source_name", "=", "CVE"), - Filter("objects", "=", {"0": {"type": "file", "name": "HAL 9000.exe"}}) + Filter("objects", "=", {"0": {"type": "file", "name": "HAL 9000.exe"}}), + Filter("objects", "contains", {"type": "file", "name": "HAL 9000.exe"}), + Filter("labels", "contains", "heartbleed"), ] # same as above objects but converted to real Python STIX2 objects @@ -302,6 +304,28 @@ def test_apply_common_filters13(): assert len(resp) == 1 +def test_apply_common_filters14(): + # Return any object that contains a specific File Cyber Observable Object + resp = list(apply_common_filters(stix_objs, [filters[15]])) + assert resp[0]['id'] == stix_objs[4]['id'] + assert len(resp) == 1 + + resp = list(apply_common_filters(real_stix_objs, [filters[15]])) + assert resp[0].id == real_stix_objs[4].id + assert len(resp) == 1 + + +def test_apply_common_filters15(): + # Return any object that contains 'heartbleed' in "labels" + resp = list(apply_common_filters(stix_objs, [filters[16]])) + assert resp[0]['id'] == stix_objs[3]['id'] + assert len(resp) == 1 + + resp = list(apply_common_filters(real_stix_objs, [filters[16]])) + assert resp[0].id == real_stix_objs[3].id + assert len(resp) == 1 + + def test_datetime_filter_behavior(): """if a filter is initialized with its value being a datetime object OR the STIX object property being filtered on is a datetime object, all