From c1ce2bd3f1a2586ec0f0c42b610a82a5b4149b5d Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Thu, 8 Jun 2017 10:44:23 -0400 Subject: [PATCH 1/9] Add markings package structure. --- stix2/markings/__init__.py | 0 stix2/markings/granular_markings.py | 0 stix2/markings/object_markings.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 stix2/markings/__init__.py create mode 100644 stix2/markings/granular_markings.py create mode 100644 stix2/markings/object_markings.py diff --git a/stix2/markings/__init__.py b/stix2/markings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stix2/markings/granular_markings.py b/stix2/markings/granular_markings.py new file mode 100644 index 0000000..e69de29 diff --git a/stix2/markings/object_markings.py b/stix2/markings/object_markings.py new file mode 100644 index 0000000..e69de29 From 069c82abf1c6f1ee4bd2a5773b5b3eedf40c5a87 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Fri, 9 Jun 2017 14:20:16 -0400 Subject: [PATCH 2/9] Add markings.utils.py --- stix2/markings/utils.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 stix2/markings/utils.py diff --git a/stix2/markings/utils.py b/stix2/markings/utils.py new file mode 100644 index 0000000..e69de29 From 1b7695c4f6623c6fced4a6790653c54181f623b3 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Fri, 9 Jun 2017 14:21:42 -0400 Subject: [PATCH 3/9] Initial marking code. --- stix2/markings/__init__.py | 197 +++++++++++++++++++++++ stix2/markings/granular_markings.py | 237 ++++++++++++++++++++++++++++ stix2/markings/object_markings.py | 140 ++++++++++++++++ stix2/markings/utils.py | 228 ++++++++++++++++++++++++++ 4 files changed, 802 insertions(+) diff --git a/stix2/markings/__init__.py b/stix2/markings/__init__.py index e69de29..8301131 100644 --- a/stix2/markings/__init__.py +++ b/stix2/markings/__init__.py @@ -0,0 +1,197 @@ +""" +Python STIX 2.0 Data Markings API. + +These high level functions will operate on both object level markings and +granular markings unless otherwise noted in each of the functions. +""" + +from stix2.markings import utils +from stix2.markings import granular_markings +from stix2.markings import object_markings + + +def get_markings(obj, selectors, inherited=False, descendants=False): + """ + Get all markings associated to the field(s). + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + inherited: If True, include object level markings and granular markings + inherited relative to the field(s). + descendants: If True, include granular markings applied to any children + relative to the field(s). + + Returns: + list: Marking IDs that matched the selectors expression. + + Note: + If ``selectors`` is None, operation will be performed only on object + level markings. + + """ + if selectors is None: + return object_markings.get_markings(obj) + + results = granular_markings.get_markings( + obj, + selectors, + inherited, + descendants + ) + + if inherited: + results.extend(object_markings.get_markings(obj)) + + return list(set(results)) + + +def set_markings(obj, selectors, marking): + """ + Removes all markings associated with selectors and appends a new granular + marking. Refer to `clear_markings` and `add_markings` for details. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + Note: + If ``selectors`` is None, operations will be performed on object level + markings. Otherwise on granular markings. + + """ + if selectors is None: + object_markings.set_markings(obj, marking) + else: + granular_markings.set_markings(obj, selectors, marking) + + +def remove_markings(obj, selectors, marking): + """ + Removes granular_marking from the granular_markings collection. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + Raises: + AssertionError: If `selectors` or `marking` fail data validation. Also + if markings to remove are not found on the provided TLO. + + Note: + If ``selectors`` is None, operations will be performed on object level + markings. Otherwise on granular markings. + + """ + if selectors is None: + object_markings.remove_markings(obj, marking) + else: + granular_markings.remove_markings(obj, selectors, marking) + + +def add_markings(obj, selectors, marking): + """ + Appends a granular_marking to the granular_markings collection. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + Raises: + AssertionError: If `selectors` or `marking` fail data validation. + + Note: + If ``selectors`` is None, operations will be performed on object level + markings. Otherwise on granular markings. + + """ + if selectors is None: + object_markings.add_markings(obj, marking) + else: + granular_markings.add_markings(obj, selectors, marking) + + +def clear_markings(obj, selectors): + """ + Removes all granular_marking associated with the selectors. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + + Note: + If ``selectors`` is None, operations will be performed on object level + markings. Otherwise on granular markings. + + """ + if selectors is None: + object_markings.clear_markings(obj) + else: + granular_markings.clear_markings(obj, selectors) + + +def is_marked(obj, selectors, marking=None, inherited=False, descendants=False): + """ + Checks if field(s) is marked by any marking or by specific marking(s). + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + inherited: If True, include object level markings and granular markings + inherited to determine if the field(s) is/are marked. + descendants: If True, include granular markings applied to any children + of the given selector to determine if the field(s) is/are marked. + + Returns: + bool: True if ``selectors`` is found on internal TLO collection. + False otherwise. + + Note: + When a list of marking IDs is provided, if ANY of the provided marking + IDs matches, True is returned. + + If ``selectors`` is None, operation will be performed only on object + level markings. + + """ + if selectors is None: + return object_markings.is_marked(obj, marking) + + result = granular_markings.is_marked( + obj, + selectors, + marking, + inherited, + descendants + ) + + if inherited: + granular_marks = granular_markings.get_markings(obj, selectors) + object_marks = object_markings.get_markings(obj) + + if granular_marks: + result = granular_markings.is_marked( + obj, + selectors, + granular_marks, + inherited, + descendants + ) + + result = result or object_markings.is_marked(obj, object_marks) + + return result diff --git a/stix2/markings/granular_markings.py b/stix2/markings/granular_markings.py index e69de29..6d69f16 100644 --- a/stix2/markings/granular_markings.py +++ b/stix2/markings/granular_markings.py @@ -0,0 +1,237 @@ + +from stix2.markings import utils + + +def get_markings(obj, selectors, inherited=False, descendants=False): + """ + Get all markings associated to the field(s). + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + inherited: If True, include markings inherited relative to the field(s). + descendants: If True, include granular markings applied to any children + relative to the field(s). + + Returns: + list: Marking IDs that matched the selectors expression. + + """ + selectors = utils.fix_value(selectors) + utils.validate(obj, selectors) + + granular_markings = obj.get("granular_markings", []) + + if not granular_markings: + return [] + + results = set() + + for marking in granular_markings: + for user_selector in selectors: + for marking_selector in marking.get("selectors", []): + if any([(user_selector == marking_selector), # Catch explicit selectors. + (user_selector.startswith(marking_selector) and inherited), # Catch inherited selectors. + (marking_selector.startswith(user_selector) and descendants)]): # Catch descendants selectors + refs = marking.get("marking_ref", []) + results.update([refs]) + + return list(results) + + +def set_markings(obj, selectors, marking): + """ + Removes all markings associated with selectors and appends a new granular + marking. Refer to `clear_markings` and `add_markings` for details. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + """ + clear_markings(obj, selectors) + add_markings(obj, selectors, marking) + + +def remove_markings(obj, selectors, marking): + """ + Removes granular_marking from the granular_markings collection. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + Raises: + AssertionError: If `selectors` or `marking` fail data validation. Also + if markings to remove are not found on the provided TLO. + + """ + selectors = utils.fix_value(selectors) + utils.validate(obj, selectors, marking) + + utils.expand_markings(obj) + + granular_markings = obj.get("granular_markings") + + if not granular_markings: + return + + tlo = utils.build_granular_marking( + {"selectors": selectors, "marking_ref": marking} + ) + + remove = tlo.get("granular_markings", []) + + if not any(marking in granular_markings for marking in remove): + raise AssertionError("Unable to remove Granular Marking(s) from" + " internal collection. Marking(s) not found...") + + obj["granular_markings"] = [ + m for m in granular_markings if m not in remove + ] + + utils.compress_markings(obj) + + if not obj.get("granular_markings"): + obj.pop("granular_markings") + + +def add_markings(obj, selectors, marking): + """ + Appends a granular_marking to the granular_markings collection. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + + Raises: + AssertionError: If `selectors` or `marking` fail data validation. + + """ + selectors = utils.fix_value(selectors) + utils.validate(obj, selectors, marking) + + granular_marking = {"selectors": sorted(selectors), "marking_ref": marking} + + if not obj.get("granular_markings"): + obj["granular_markings"] = list() + + obj["granular_markings"].append(granular_marking) + + utils.expand_markings(obj) + utils.compress_markings(obj) + + +def clear_markings(obj, selectors): + """ + Removes all granular_marking associated with the selectors. + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + + Raises: + AssertionError: If `selectors` or `marking` fail data validation. Also + if markings to remove are not found on the provided TLO. + + """ + selectors = utils.fix_value(selectors) + utils.validate(obj, selectors) + + utils.expand_markings(obj) + + granular_markings = obj.get("granular_markings") + + if not granular_markings: + return + + tlo = utils.build_granular_marking( + {"selectors": selectors, "marking_ref": ["N/A"]} + ) + + clear = tlo.get("granular_markings", []) + + if not any(clear_selector in tlo_selectors.get("selectors", []) + for tlo_selectors in granular_markings + for clear_marking in clear + for clear_selector in clear_marking.get("selectors", []) + ): + raise AssertionError("Unable to clear Granular Marking(s) from" + " internal collection. Selector(s) not found...") + + for granular_marking in granular_markings: + for s in selectors: + if s in granular_marking.get("selectors", []): + marking_refs = granular_marking.get("marking_ref") + + if marking_refs: + granular_marking["marking_ref"] = list() + + utils.compress_markings(obj) + + if not obj.get("granular_markings"): + obj.pop("granular_markings") + + +def is_marked(obj, selectors, marking=None, inherited=False, descendants=False): + """ + Checks if field is marked by any marking or by specific marking(s). + + Args: + obj: A TLO object. + selectors: string or list of selectors strings relative to the TLO in + which the field(s) appear(s). + marking: identifier or list of marking identifiers that apply to the + field(s) selected by `selectors`. + inherited: If True, return markings inherited from the given selector. + descendants: If True, return granular markings applied to any children + of the given selector. + + Returns: + bool: True if ``selectors`` is found on internal TLO collection. + False otherwise. + + Note: + When a list of marking IDs is provided, if ANY of the provided marking + IDs matches, True is returned. + + """ + selectors = utils.fix_value(selectors) + marking = utils.fix_value(marking) + utils.validate(obj, selectors, marking) + + granular_markings = obj.get("granular_markings", []) + + marked = False + markings = set() + + for granular_marking in granular_markings: + for user_selector in selectors: + for marking_selector in granular_marking.get("selectors", []): + + if any([(user_selector == marking_selector), # Catch explicit selectors. + (user_selector.startswith(marking_selector) and inherited), # Catch inherited selectors. + (marking_selector.startswith(user_selector) and descendants)]): # Catch descendants selectors + marking_ref = granular_marking.get("marking_ref", "") + + if marking and any(x == marking_ref for x in marking): + markings.update([marking_ref]) + + marked = True + + if marking: + # All user-provided markings must be found. + return markings.issuperset(set(marking)) + + return marked diff --git a/stix2/markings/object_markings.py b/stix2/markings/object_markings.py index e69de29..33a8d2a 100644 --- a/stix2/markings/object_markings.py +++ b/stix2/markings/object_markings.py @@ -0,0 +1,140 @@ + +import six + +from stix2.markings import utils + + +def get_markings(obj): + """ + Get all object level markings from the given TLO object. + + Args: + obj: A TLO object. + + Returns: + list: Marking IDs contained in the TLO. + + """ + object_markings = obj.get("object_marking_refs", []) + + if not object_markings: + return [] + elif isinstance(object_markings, six.string_types): + return [object_markings] + else: + return object_markings + + +def add_markings(obj, marking): + """ + Appends an object level marking to the object_marking_refs collection. + + Args: + obj: A TLO object. + marking: identifier or list of marking identifiers that apply to the + TLO object. + + Raises: + AssertionError: If `marking` fail data validation. + + """ + marking = utils.convert_to_list(marking) + utils.validate(obj, marking=marking) + + if not obj.get("object_marking_refs"): + obj["object_marking_refs"] = list() + + object_markings = set(obj.get("object_marking_refs") + marking) + + obj["object_marking_refs"] = list(object_markings) + + +def remove_markings(obj, marking): + """ + Removes object level marking from the object_marking_refs collection. + + Args: + obj: A TLO object. + marking: identifier or list of marking identifiers that apply to the + TLO object. + + Raises: + AssertionError: If `marking` fail data validation. Also + if markings to remove are not found on the provided TLO. + + """ + marking = utils.convert_to_list(marking) + utils.validate(obj, marking=marking) + + object_markings = obj.get("object_marking_refs", []) + + if not object_markings: + return [] + + if any(x not in obj["object_marking_refs"] for x in marking): + raise AssertionError("Unable to remove Object Level Marking(s) from " + "internal collection. Marking(s) not found...") + + obj["object_marking_refs"] = [x for x in object_markings + if x not in marking] + + if not obj.get("object_marking_refs"): + obj.pop("object_marking_refs") + + +def set_markings(obj, marking): + """ + Removes all object level markings and appends new object level markings to + the collection. Refer to `clear_markings` and `add_markings` for details. + + Args: + obj: A TLO object. + marking: identifier or list of marking identifiers that apply to the + TLO object. + + """ + utils.validate(obj, marking=marking) + + clear_markings(obj) + add_markings(obj, marking) + + +def clear_markings(obj): + """ + Removes all object level markings from the object_marking_refs collection. + + Args: + obj: A TLO object. + + """ + try: + del obj["object_marking_refs"] + except KeyError: + raise AssertionError("Unable to clear Object Marking(s) from internal" + " collection. No Markings in object...") + + +def is_marked(obj, marking=None): + """ + Checks if TLO is marked by any marking or by specific marking(s). + + Args: + obj: A TLO object. + marking: identifier or list of marking identifiers that apply to the + TLO object. + + Returns: + bool: True if TLO has object level markings. False otherwise. + + Note: + When a list of marking IDs is provided, if ANY of the provided marking + IDs matches, True is returned. + + """ + marking = utils.convert_to_list(marking) + object_markings = obj.get("object_marking_refs", []) + + if marking: + return any(x in object_markings for x in marking) + else: + return bool(object_markings) diff --git a/stix2/markings/utils.py b/stix2/markings/utils.py index e69de29..9286695 100644 --- a/stix2/markings/utils.py +++ b/stix2/markings/utils.py @@ -0,0 +1,228 @@ + +import collections + +import six + + +def evaluate_expression(obj, selector): + + for items, value in iterpath(obj): + path = ".".join(items) + + if path == selector and value: + return [value] + + return [] + + +def validate_selector(obj, selector): + results = list(evaluate_expression(obj, selector)) + + if len(results) >= 1: + return True + + +def validate_markings(marking): + if isinstance(marking, six.string_types): + if not marking: + return False + else: + return True + + elif isinstance(marking, list) and len(marking) >= 1: + for m in marking: + if not m: + return False + elif not isinstance(m, six.string_types): + return False + + return True + else: + return False + + +def validate(obj, selectors=None, marking=None): + + if selectors is not None: + assert selectors + + for s in selectors: + assert validate_selector(obj, s) + + if marking is not None: + assert validate_markings(marking) + + +def convert_to_list(data): + if data is not None: + if isinstance(data, list): + return data + else: + return [data] + + +def fix_value(data): + data = convert_to_list(data) + + return data + + +def _fix_markings(markings): + + for granular_marking in markings: + refs = granular_marking.get("marking_ref", []) + selectors = granular_marking.get("selectors", []) + + if not isinstance(refs, list): + granular_marking["marking_ref"] = [refs] + + if not isinstance(selectors, list): + granular_marking["selectors"] = [selectors] + + +def _group_by(markings): + + key = "marking_ref" + retrieve = "selectors" + + map_ = collections.defaultdict(set) + + for granular_marking in markings: + for data in granular_marking.get(key, []): + map_[data].update(granular_marking.get(retrieve)) + + granular_markings = \ + [ + {"selectors": sorted(selectors), "marking_ref": ref} + for ref, selectors in six.iteritems(map_) + ] + + return granular_markings + + +def compress_markings(tlo): + + if not tlo.get("granular_markings"): + return + + granular_markings = tlo.get("granular_markings") + + _fix_markings(granular_markings) + + tlo["granular_markings"] = _group_by(granular_markings) + + +def expand_markings(tlo): + + if not tlo.get("granular_markings"): + return + + granular_markings = tlo.get("granular_markings") + + _fix_markings(granular_markings) + + expanded = list() + + for marking in granular_markings: + selectors = marking.get("selectors", []) + marking_ref = marking.get("marking_ref", []) + + expanded.extend( + [ + {"selectors": [sel], "marking_ref": ref} + for sel in selectors + for ref in marking_ref + ] + ) + + tlo["granular_markings"] = expanded + + +def build_granular_marking(granular_marking): + tlo = {"granular_markings": [granular_marking]} + + expand_markings(tlo) + + return tlo + + +def iterpath(obj, path=None): + """ + Generator which walks the input ``obj`` model. Each iteration yields a + tuple containing a list of ancestors and the property value. + + Args: + obj: A TLO object. + path: None, used recursively to store ancestors. + + Example: + >>> for item in iterpath(tlo): + >>> print(item) + (['type'], 'campaign') + ... + (['cybox', 'objects', '[0]', 'hashes', 'sha1'], 'cac35ec206d868b7d7cb0b55f31d9425b075082b') + + Returns: + tuple: Containing two items: a list of ancestors and the property value. + + """ + if path is None: + path = [] + + for varname, varobj in iter(sorted(six.iteritems(obj))): + path.append(varname) + yield (path, varobj) + + if isinstance(varobj, dict): + + for item in iterpath(varobj, path): + yield item + + elif isinstance(varobj, list): + + for item in varobj: + index = "[{0}]".format(varobj.index(item)) + path.append(index) + + yield (path, item) + + if isinstance(item, dict): + for descendant in iterpath(item, path): + yield descendant + + path.pop() + + path.pop() + + +def get_selector(obj, prop): + """ + Function that creates a selector based on ``prop``. + + Args: + obj: A TLO object. + prop: A property of the TLO object. + + Note: + Must supply the actual value inside the structure. Since some + limitations exist with Python interning methods, checking for object + location is for now the option to assert the data. + + Example: + >>> selector = get_selector(tlo, tlo["cybox"]["objects"][0]["file_name"]) + >>> print(selector) + ["cybox.objects.[0].file_name"] + + Returns: + list: A list with one selector that asserts the supplied property. + Empty list if it was unable to find the property. + + """ + selector = [] + + for ancestors, value in iterpath(obj): + if value is prop: + path = ".".join(ancestors) + selector.append(path) + + return selector From 1f258551e1bd1aeaeb51edd0a7fe96b76d73811a Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Fri, 9 Jun 2017 14:22:56 -0400 Subject: [PATCH 4/9] Add and update tests for markings API. --- stix2/test/test_granular_markings.py | 968 +++++++++++++++++++++++++++ stix2/test/test_object_markings.py | 487 ++++++++++++++ 2 files changed, 1455 insertions(+) create mode 100644 stix2/test/test_granular_markings.py create mode 100644 stix2/test/test_object_markings.py diff --git a/stix2/test/test_granular_markings.py b/stix2/test/test_granular_markings.py new file mode 100644 index 0000000..da53ce1 --- /dev/null +++ b/stix2/test/test_granular_markings.py @@ -0,0 +1,968 @@ + +from stix2 import markings + +import pytest + +"""Tests for the Data Markings API.""" + + +def test_add_marking_mark_one_selector_multiple_refs(): + before = { + "description": "test description", + "title": "foo", + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.add_markings(before, ["description"], ["marking-definition--1", "marking-definition--2"]) + + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +def test_add_marking_mark_multiple_selector_one_refs(): + before = { + "description": "test description", + "title": "foo", + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + ] + } + markings.add_markings(before, ["description", "title"], ["marking-definition--1"]) + assert before == after + + +def test_add_marking_mark_multiple_selector_multiple_refs(): + before = { + "description": "test description", + "title": "foo", + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.add_markings(before, ["description", "title"], ["marking-definition--1", "marking-definition--2"]) + + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +def test_add_marking_mark_another_property_same_marking(): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + ] + } + markings.add_markings(before, ["title"], ["marking-definition--1"]) + assert before == after + + +def test_add_marking_mark_same_property_same_marking(): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + markings.add_markings(before, ["description"], ["marking-definition--1"]) + assert before == after + + +@pytest.mark.parametrize("data,marking", [ + ({"description": "test description"}, + [["title"], ["marking-definition--1", "marking-definition--2"], + "", ["marking-definition--1", "marking-definition--2"], + [], ["marking-definition--1", "marking-definition--2"], + [""], ["marking-definition--1", "marking-definition--2"], + ["description"], [""], + ["description"], [], + ["description"], ["marking-definition--1", 456] + ]) +]) +def test_add_marking_bad_selector(data, marking): + with pytest.raises(AssertionError): + markings.add_markings(data, marking[0], marking[1]) + + +GET_MARKINGS_TEST_DATA = \ +{ + "a": 333, + "b": "value", + "c": [ + 17, + "list value", + { + "g": "nested", + "h": 45 + } + ], + "x": { + "y": [ + "hello", + 88 + ], + "z": { + "foo1": "bar", + "foo2": 65 + } + }, + "granular_markings": [ + { + "marking_ref": "1", + "selectors": ["a"] + }, + { + "marking_ref": "2", + "selectors": ["c"] + }, + { + "marking_ref": "3", + "selectors": ["c.[1]"] + }, + { + "marking_ref": "4", + "selectors": ["c.[2]"] + }, + { + "marking_ref": "5", + "selectors": ["c.[2].g"] + }, + { + "marking_ref": "6", + "selectors": ["x"] + }, + { + "marking_ref": "7", + "selectors": ["x.y"] + }, + { + "marking_ref": "8", + "selectors": ["x.y.[1]"] + }, + { + "marking_ref": "9", + "selectors": ["x.z"] + }, + { + "marking_ref": "10", + "selectors": ["x.z.foo2"] + }, + ] +} + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_smoke(data): + """Test get_markings does not fail.""" + assert len(markings.get_markings(data, "a")) >= 1 + assert markings.get_markings(data, "a") == ["1"] + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_not_marked(data): + """Test selector that is not marked returns empty list.""" + results = markings.get_markings(data, "b") + assert len(results) == 0 + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_multiple_selectors(data): + """Test multiple selectors return combination of markings.""" + total = markings.get_markings(data, ["x.y", "x.z"]) + xy_markings = markings.get_markings(data, ["x.y"]) + xz_markings = markings.get_markings(data, ["x.z"]) + + assert set(xy_markings).issubset(total) + assert set(xz_markings).issubset(total) + assert set(xy_markings).union(xz_markings).issuperset(total) + + +@pytest.mark.parametrize("data,selector", [ + (GET_MARKINGS_TEST_DATA, "foo"), + (GET_MARKINGS_TEST_DATA, ""), + (GET_MARKINGS_TEST_DATA, []), + (GET_MARKINGS_TEST_DATA, [""]), + (GET_MARKINGS_TEST_DATA, "x.z.[-2]"), + (GET_MARKINGS_TEST_DATA, "c.f"), + (GET_MARKINGS_TEST_DATA, "c.[2].i"), + (GET_MARKINGS_TEST_DATA, "c.[3]"), + (GET_MARKINGS_TEST_DATA, "d"), + (GET_MARKINGS_TEST_DATA, "x.[0]"), + (GET_MARKINGS_TEST_DATA, "z.y.w"), + (GET_MARKINGS_TEST_DATA, "x.z.[1]"), + (GET_MARKINGS_TEST_DATA, "x.z.foo3") +]) +def test_get_markings_bad_selector(data, selector): + """Test bad selectors raise exception""" + with pytest.raises(AssertionError): + markings.get_markings(data, selector) + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_positional_arguments_combinations(data): + """Test multiple combinations for inherited and descendant markings.""" + assert set(markings.get_markings(data, "a", False, False)) == set(["1"]) + assert set(markings.get_markings(data, "a", True, False)) == set(["1"]) + assert set(markings.get_markings(data, "a", True, True)) == set(["1"]) + assert set(markings.get_markings(data, "a", False, True)) == set(["1"]) + + assert set(markings.get_markings(data, "b", False, False)) == set([]) + assert set(markings.get_markings(data, "b", True, False)) == set([]) + assert set(markings.get_markings(data, "b", True, True)) == set([]) + assert set(markings.get_markings(data, "b", False, True)) == set([]) + + assert set(markings.get_markings(data, "c", False, False)) == set(["2"]) + assert set(markings.get_markings(data, "c", True, False)) == set(["2"]) + assert set(markings.get_markings(data, "c", True, True)) == set(["2", "3", "4", "5"]) + assert set(markings.get_markings(data, "c", False, True)) == set(["2", "3", "4", "5"]) + + assert set(markings.get_markings(data, "c.[0]", False, False)) == set([]) + assert set(markings.get_markings(data, "c.[0]", True, False)) == set(["2"]) + assert set(markings.get_markings(data, "c.[0]", True, True)) == set(["2"]) + assert set(markings.get_markings(data, "c.[0]", False, True)) == set([]) + + assert set(markings.get_markings(data, "c.[1]", False, False)) == set(["3"]) + assert set(markings.get_markings(data, "c.[1]", True, False)) == set(["2", "3"]) + assert set(markings.get_markings(data, "c.[1]", True, True)) == set(["2", "3"]) + assert set(markings.get_markings(data, "c.[1]", False, True)) == set(["3"]) + + assert set(markings.get_markings(data, "c.[2]", False, False)) == set(["4"]) + assert set(markings.get_markings(data, "c.[2]", True, False)) == set(["2", "4"]) + assert set(markings.get_markings(data, "c.[2]", True, True)) == set(["2", "4", "5"]) + assert set(markings.get_markings(data, "c.[2]", False, True)) == set(["4", "5"]) + + assert set(markings.get_markings(data, "c.[2].g", False, False)) == set(["5"]) + assert set(markings.get_markings(data, "c.[2].g", True, False)) == set(["2", "4", "5"]) + assert set(markings.get_markings(data, "c.[2].g", True, True)) == set(["2", "4", "5"]) + assert set(markings.get_markings(data, "c.[2].g", False, True)) == set(["5"]) + + assert set(markings.get_markings(data, "x", False, False)) == set(["6"]) + assert set(markings.get_markings(data, "x", True, False)) == set(["6"]) + assert set(markings.get_markings(data, "x", True, True)) == set(["6", "7", "8", "9", "10"]) + assert set(markings.get_markings(data, "x", False, True)) == set(["6", "7", "8", "9", "10"]) + + assert set(markings.get_markings(data, "x.y", False, False)) == set(["7"]) + assert set(markings.get_markings(data, "x.y", True, False)) == set(["6", "7"]) + assert set(markings.get_markings(data, "x.y", True, True)) == set(["6", "7", "8"]) + assert set(markings.get_markings(data, "x.y", False, True)) == set(["7", "8"]) + + assert set(markings.get_markings(data, "x.y.[0]", False, False)) == set([]) + assert set(markings.get_markings(data, "x.y.[0]", True, False)) == set(["6", "7"]) + assert set(markings.get_markings(data, "x.y.[0]", True, True)) == set(["6", "7"]) + assert set(markings.get_markings(data, "x.y.[0]", False, True)) == set([]) + + assert set(markings.get_markings(data, "x.y.[1]", False, False)) == set(["8"]) + assert set(markings.get_markings(data, "x.y.[1]", True, False)) == set(["6", "7", "8"]) + assert set(markings.get_markings(data, "x.y.[1]", True, True)) == set(["6", "7", "8"]) + assert set(markings.get_markings(data, "x.y.[1]", False, True)) == set(["8"]) + + assert set(markings.get_markings(data, "x.z", False, False)) == set(["9"]) + assert set(markings.get_markings(data, "x.z", True, False)) == set(["6", "9"]) + assert set(markings.get_markings(data, "x.z", True, True)) == set(["6", "9", "10"]) + assert set(markings.get_markings(data, "x.z", False, True)) == set(["9", "10"]) + + assert set(markings.get_markings(data, "x.z.foo1", False, False)) == set([]) + assert set(markings.get_markings(data, "x.z.foo1", True, False)) == set(["6", "9"]) + assert set(markings.get_markings(data, "x.z.foo1", True, True)) == set(["6", "9"]) + assert set(markings.get_markings(data, "x.z.foo1", False, True)) == set([]) + + assert set(markings.get_markings(data, "x.z.foo2", False, False)) == set(["10"]) + assert set(markings.get_markings(data, "x.z.foo2", True, False)) == set(["6", "9", "10"]) + assert set(markings.get_markings(data, "x.z.foo2", True, True)) == set(["6", "9", "10"]) + assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"]) + + +def test_remove_marking_remove_one_selector_with_multiple_refs(): + after = { + "description": "test description", + "title": "foo", + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.remove_markings(before, ["description"], ["marking-definition--1", "marking-definition--2"]) + assert before == after + + +def test_remove_marking_remove_multiple_selector_one_ref(): + after = { + "description": "test description", + "title": "foo", + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + ] + } + markings.remove_markings(before, ["description", "title"], ["marking-definition--1"]) + assert before == after + + +def test_remove_marking_mark_one_selector_from_multiple_ones(): + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + ] + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + ] + } + markings.remove_markings(before, ["title"], ["marking-definition--1"]) + assert before == after + + +def test_remove_marking_mark_one_selector_markings_from_multiple_ones(): + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--2" + }, + ] + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.remove_markings(before, ["title"], ["marking-definition--1"]) + + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +def test_remove_marking_mark_mutilple_selector_multiple_refs(): + after = { + "description": "test description", + "title": "foo", + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.remove_markings(before, ["description", "title"], ["marking-definition--1", "marking-definition--2"]) + assert before == after + + +def test_remove_marking_mark_another_property_same_marking(): + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + ] + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["title"], + "marking_ref": "marking-definition--1" + } + ] + } + markings.remove_markings(before, ["title"], ["marking-definition--1"]) + assert before == after + + +def test_remove_marking_mark_same_property_same_marking(): + after = { + "description": "test description", + "title": "foo", + } + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + markings.remove_markings(before, ["description"], ["marking-definition--1"]) + assert before == after + + +def test_remove_marking_bad_selector(): + before = { + "description": "test description", + } + with pytest.raises(AssertionError): + markings.remove_markings(before, ["title"], ["marking-definition--1", "marking-definition--2"]) + + +IS_MARKED_TEST_DATA = \ +{ + "title": "test title", + "description": "test description", + "revision": 2, + "type": "test", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["revision", "description"], + "marking_ref": "marking-definition--2" + }, + { + "selectors": ["revision", "description"], + "marking_ref": "marking-definition--3" + }, + ] +} + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_smoke(data): + """Smoke test is_marked call does not fail.""" + assert markings.is_marked(data, ["description"]) + assert markings.is_marked(data, ["title"]) is False + + +@pytest.mark.parametrize("data,selector", [ + (IS_MARKED_TEST_DATA, "foo"), + (IS_MARKED_TEST_DATA, ""), + (IS_MARKED_TEST_DATA, []), + (IS_MARKED_TEST_DATA, [""]), + (IS_MARKED_TEST_DATA, "x.z.[-2]"), + (IS_MARKED_TEST_DATA, "c.f"), + (IS_MARKED_TEST_DATA, "c.[2].i"), + (IS_MARKED_TEST_DATA, "c.[3]"), + (IS_MARKED_TEST_DATA, "d"), + (IS_MARKED_TEST_DATA, "x.[0]"), + (IS_MARKED_TEST_DATA, "z.y.w"), + (IS_MARKED_TEST_DATA, "x.z.[1]"), + (IS_MARKED_TEST_DATA, "x.z.foo3") +]) +def test_is_marked_invalid_selector(data, selector): + """Test invalid selector raises an error.""" + with pytest.raises(AssertionError): + markings.is_marked(data, selector) + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_mix_selector(data): + """Test valid selector, one marked and one not marked returns True.""" + assert markings.is_marked(data, ["description", "revision"]) + assert markings.is_marked(data, ["description"]) + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_valid_selector_no_refs(data): + """Test that a valid selector return True when it has marking refs and False when not.""" + assert markings.is_marked(data, ["description"]) + assert markings.is_marked(data, ["description"], ["marking-definition--2", "marking-definition--3"]) + assert markings.is_marked(data, ["description"], ["marking-definition--2"]) + assert markings.is_marked(data, ["description"], ["marking-definition--2", "marking-definition--8"]) is False + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_valid_selector_and_refs(data): + """Test that a valid selector returns True when marking_refs match.""" + assert markings.is_marked(data, ["description"], ["marking-definition--1"]) + assert markings.is_marked(data, ["title"], ["marking-definition--1"]) is False + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_valid_selector_multiple_refs(data): + """Test that a valid selector returns True if aall marking_refs match. + Otherwise False.""" + assert markings.is_marked(data, ["revision"], ["marking-definition--2", "marking-definition--3"]) + assert markings.is_marked(data, ["revision"], ["marking-definition--2", "marking-definition--1"]) is False + assert markings.is_marked(data, ["revision"], "marking-definition--2") + assert markings.is_marked(data, ["revision"], ["marking-definition--1234"]) is False + + +@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +def test_is_marked_no_marking_refs(data): + """Test that a valid content selector with no marking_refs returns True + if there is a granular_marking that asserts that field, False + otherwise.""" + assert markings.is_marked(data, ["type"]) is False + assert markings.is_marked(data, ["revision"]) + + +def test_is_marked_positional_arguments_combinations(): + """Test multiple combinations for inherited and descendant markings.""" + test_tlo = \ + { + "a": 333, + "b": "value", + "c": [ + 17, + "list value", + { + "g": "nested", + "h": 45 + } + ], + "x": { + "y": [ + "hello", + 88 + ], + "z": { + "foo1": "bar", + "foo2": 65 + } + }, + "granular_markings": [ + { + "marking_ref": "1", + "selectors": ["a"] + }, + { + "marking_ref": "2", + "selectors": ["c"] + }, + { + "marking_ref": "3", + "selectors": ["c.[1]"] + }, + { + "marking_ref": "4", + "selectors": ["c.[2]"] + }, + { + "marking_ref": "5", + "selectors": ["c.[2].g"] + }, + { + "marking_ref": "6", + "selectors": ["x"] + }, + { + "marking_ref": "7", + "selectors": ["x.y"] + }, + { + "marking_ref": "8", + "selectors": ["x.y.[1]"] + }, + { + "marking_ref": "9", + "selectors": ["x.z"] + }, + { + "marking_ref": "10", + "selectors": ["x.z.foo2"] + }, + ] + } + + assert markings.is_marked(test_tlo, "a", ["1"], False, False) + assert markings.is_marked(test_tlo, "a", ["1"], True, False) + assert markings.is_marked(test_tlo, "a", ["1"], True, True) + assert markings.is_marked(test_tlo, "a", ["1"], False, True) + + assert markings.is_marked(test_tlo, "b", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "b", inherited=True, descendants=False) is False + assert markings.is_marked(test_tlo, "b", inherited=True, descendants=True) is False + assert markings.is_marked(test_tlo, "b", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "c", ["2"], False, False) + assert markings.is_marked(test_tlo, "c", ["2"], True, False) + assert markings.is_marked(test_tlo, "c", ["2", "3", "4", "5"], True, True) + assert markings.is_marked(test_tlo, "c", ["2", "3", "4", "5"], False, True) + + assert markings.is_marked(test_tlo, "c.[0]", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "c.[0]", ["2"], True, False) + assert markings.is_marked(test_tlo, "c.[0]", ["2"], True, True) + assert markings.is_marked(test_tlo, "c.[0]", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "c.[1]", ["3"], False, False) + assert markings.is_marked(test_tlo, "c.[1]", ["2", "3"], True, False) + assert markings.is_marked(test_tlo, "c.[1]", ["2", "3"], True, True) + assert markings.is_marked(test_tlo, "c.[1]", ["3"], False, True) + + assert markings.is_marked(test_tlo, "c.[2]", ["4"], False, False) + assert markings.is_marked(test_tlo, "c.[2]", ["2", "4"], True, False) + assert markings.is_marked(test_tlo, "c.[2]", ["2", "4", "5"], True, True) + assert markings.is_marked(test_tlo, "c.[2]", ["4", "5"], False, True) + + assert markings.is_marked(test_tlo, "c.[2].g", ["5"], False, False) + assert markings.is_marked(test_tlo, "c.[2].g", ["2", "4", "5"], True, False) + assert markings.is_marked(test_tlo, "c.[2].g", ["2", "4", "5"], True, True) + assert markings.is_marked(test_tlo, "c.[2].g", ["5"], False, True) + + assert markings.is_marked(test_tlo, "x", ["6"], False, False) + assert markings.is_marked(test_tlo, "x", ["6"], True, False) + assert markings.is_marked(test_tlo, "x", ["6", "7", "8", "9", "10"], True, True) + assert markings.is_marked(test_tlo, "x", ["6", "7", "8", "9", "10"], False, True) + + assert markings.is_marked(test_tlo, "x.y", ["7"], False, False) + assert markings.is_marked(test_tlo, "x.y", ["6", "7"], True, False) + assert markings.is_marked(test_tlo, "x.y", ["6", "7", "8"], True, True) + assert markings.is_marked(test_tlo, "x.y", ["7", "8"], False, True) + + assert markings.is_marked(test_tlo, "x.y.[0]", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "x.y.[0]", ["6", "7"], True, False) + assert markings.is_marked(test_tlo, "x.y.[0]", ["6", "7"], True, True) + assert markings.is_marked(test_tlo, "x.y.[0]", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "x.y.[1]", ["8"], False, False) + assert markings.is_marked(test_tlo, "x.y.[1]", ["6", "7", "8"], True, False) + assert markings.is_marked(test_tlo, "x.y.[1]", ["6", "7", "8"], True, True) + assert markings.is_marked(test_tlo, "x.y.[1]", ["8"], False, True) + + assert markings.is_marked(test_tlo, "x.z", ["9"], False, False) + assert markings.is_marked(test_tlo, "x.z", ["6", "9"], True, False) + assert markings.is_marked(test_tlo, "x.z", ["6", "9", "10"], True, True) + assert markings.is_marked(test_tlo, "x.z", ["9", "10"], False, True) + + assert markings.is_marked(test_tlo, "x.z.foo1", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "x.z.foo1", ["6", "9"], True, False) + assert markings.is_marked(test_tlo, "x.z.foo1", ["6", "9"], True, True) + assert markings.is_marked(test_tlo, "x.z.foo1", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "x.z.foo2", ["10"], False, False) + assert markings.is_marked(test_tlo, "x.z.foo2", ["6", "9", "10"], True, False) + assert markings.is_marked(test_tlo, "x.z.foo2", ["6", "9", "10"], True, True) + assert markings.is_marked(test_tlo, "x.z.foo2", ["10"], False, True) + + +def test_set_marking_mark_one_selector_multiple_refs(): + before = { + "description": "test description", + "title": "foo", + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.set_markings(before, ["description"], ["marking-definition--1", "marking-definition--2"]) + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +def test_set_marking_mark_multiple_selector_one_refs(): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--3" + }, + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + ] + } + markings.set_markings(before, ["description", "title"], ["marking-definition--1"]) + assert before == after + + +def test_set_marking_mark_multiple_selector_multiple_refs_from_none(): + before = { + "description": "test description", + "title": "foo", + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["description", "title"], + "marking_ref": "marking-definition--2" + }, + ] + } + markings.set_markings(before, ["description", "title"], ["marking-definition--1", "marking-definition--2"]) + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +def test_set_marking_mark_another_property_same_marking(): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--7" + }, + { + "selectors": ["description"], + "marking_ref": "marking-definition--8" + }, + ] + } + markings.set_markings(before, ["description"], ["marking-definition--7", "marking-definition--8"]) + + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + +@pytest.mark.parametrize("marking", [ + (["foo"], ["marking-definition--7", "marking-definition--8"]), + ("", ["marking-definition--7", "marking-definition--8"]), + ([], ["marking-definition--7", "marking-definition--8"]), + ([""], ["marking-definition--7", "marking-definition--8"]) +]) +def test_set_marking_bad_selector(marking): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + ] + } + + with pytest.raises(AssertionError): + markings.set_markings(before, marking[0], marking[1]) + + assert before == after + + +def test_set_marking_mark_same_property_same_marking(): + before = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + after = { + "description": "test description", + "title": "foo", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + } + ] + } + markings.set_markings(before, ["description"], ["marking-definition--1"]) + assert before == after + + +CLEAR_MARKINGS_TEST_DATA = \ +{ + "title": "test title", + "description": "test description", + "revision": 2, + "type": "test", + "granular_markings": [ + { + "selectors": ["description"], + "marking_ref": "marking-definition--1" + }, + { + "selectors": ["revision", "description"], + "marking_ref": "marking-definition--2" + }, + { + "selectors": ["revision", "description", "type"], + "marking_ref": "marking-definition--3" + }, + ] +} + + +@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +def test_clear_marking_smoke(data): + """Test clear_marking call does not fail.""" + markings.clear_markings(data, "revision") + assert markings.is_marked(data, "revision") is False + + +@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +def test_clear_marking_multiple_selectors(data): + """Test clearing markings for multiple selectors effectively removes associated markings.""" + markings.clear_markings(data, ["type", "description"]) + assert markings.is_marked(data, ["type", "description"]) is False + + +@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +def test_clear_marking_one_selector(data): + """Test markings associated with one selector were removed.""" + markings.clear_markings(data, "description") + assert markings.is_marked(data, "description") is False + + +@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +def test_clear_marking_all_selectors(data): + markings.clear_markings(data, ["description", "type", "revision"]) + assert markings.is_marked(data, "description") is False + assert "granular_markings" not in data + + +@pytest.mark.parametrize("data,selector", [ + (CLEAR_MARKINGS_TEST_DATA, "foo"), + (CLEAR_MARKINGS_TEST_DATA, ""), + (CLEAR_MARKINGS_TEST_DATA, []), + (CLEAR_MARKINGS_TEST_DATA, [""]), +]) +def test_clear_marking_bad_selector(data, selector): + """Test bad selector raises exception.""" + with pytest.raises(AssertionError): + markings.clear_markings(data, selector) diff --git a/stix2/test/test_object_markings.py b/stix2/test/test_object_markings.py new file mode 100644 index 0000000..5205ad7 --- /dev/null +++ b/stix2/test/test_object_markings.py @@ -0,0 +1,487 @@ + +from stix2 import markings + +import pytest + +"""Tests for the Data Markings API.""" + + +def test_add_markings_one_marking(): + before = { + "title": "test title", + "description": "test description" + } + + after = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1"] + } + + markings.add_markings(before, None, "marking-definition--1") + + assert before == after + + +def test_add_markings_multiple_marking(): + before = { + "title": "test title", + "description": "test description" + } + + after = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2"] + } + + markings.add_markings(before, None, ["marking-definition--1", "marking-definition--2"]) + + for m in before["object_marking_refs"]: + assert m in after["object_marking_refs"] + + +def test_add_markings_combination(): + before = { + "title": "test title", + "description": "test description" + } + + after = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2"], + "granular_markings": [ + { + "selectors": ["title"], + "marking_ref": "marking-definition--3" + }, + { + "selectors": ["description"], + "marking_ref": "marking-definition--4" + }, + ] + } + + markings.add_markings(before, None, "marking-definition--1") + markings.add_markings(before, None, "marking-definition--2") + markings.add_markings(before, "title", "marking-definition--3") + markings.add_markings(before, "description", "marking-definition--4") + + for m in before["granular_markings"]: + assert m in after["granular_markings"] + + for m in before["object_marking_refs"]: + assert m in after["object_marking_refs"] + + +@pytest.mark.parametrize("data", [ + ([""]), + (""), + ([]), + (["marking-definition--1", 456]) +]) +def test_add_markings_bad_markings(data): + before = { + "title": "test title", + "description": "test description" + } + with pytest.raises(AssertionError): + markings.add_markings(before, None, data) + + assert "object_marking_refs" not in before + + +GET_MARKINGS_TEST_DATA = \ + { + "a": 333, + "b": "value", + "c": [ + 17, + "list value", + { + "g": "nested", + "h": 45 + } + ], + "x": { + "y": [ + "hello", + 88 + ], + "z": { + "foo1": "bar", + "foo2": 65 + } + }, + "object_marking_refs": ["11"], + "granular_markings": [ + { + "marking_ref": "1", + "selectors": ["a"] + }, + { + "marking_ref": "2", + "selectors": ["c"] + }, + { + "marking_ref": "3", + "selectors": ["c.[1]"] + }, + { + "marking_ref": "4", + "selectors": ["c.[2]"] + }, + { + "marking_ref": "5", + "selectors": ["c.[2].g"] + }, + { + "marking_ref": "6", + "selectors": ["x"] + }, + { + "marking_ref": "7", + "selectors": ["x.y"] + }, + { + "marking_ref": "8", + "selectors": ["x.y.[1]"] + }, + { + "marking_ref": "9", + "selectors": ["x.z"] + }, + { + "marking_ref": "10", + "selectors": ["x.z.foo2"] + }, + ] + } + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_object_marking(data): + assert set(markings.get_markings(data, None)) == set(["11"]) + + +@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA]) +def test_get_markings_object_and_granular_combinations(data): + """Test multiple combinations for inherited and descendant markings.""" + assert set(markings.get_markings(data, "a", False, False)) == set(["1"]) + assert set(markings.get_markings(data, "a", True, False)) == set(["1", "11"]) + assert set(markings.get_markings(data, "a", True, True)) == set(["1", "11"]) + assert set(markings.get_markings(data, "a", False, True)) == set(["1"]) + + assert set(markings.get_markings(data, "b", False, False)) == set([]) + assert set(markings.get_markings(data, "b", True, False)) == set(["11"]) + assert set(markings.get_markings(data, "b", True, True)) == set(["11"]) + assert set(markings.get_markings(data, "b", False, True)) == set([]) + + assert set(markings.get_markings(data, "c", False, False)) == set(["2"]) + assert set(markings.get_markings(data, "c", True, False)) == set(["2", "11"]) + assert set(markings.get_markings(data, "c", True, True)) == set(["2", "3", "4", "5", "11"]) + assert set(markings.get_markings(data, "c", False, True)) == set(["2", "3", "4", "5"]) + + assert set(markings.get_markings(data, "c.[0]", False, False)) == set([]) + assert set(markings.get_markings(data, "c.[0]", True, False)) == set(["2", "11"]) + assert set(markings.get_markings(data, "c.[0]", True, True)) == set(["2", "11"]) + assert set(markings.get_markings(data, "c.[0]", False, True)) == set([]) + + assert set(markings.get_markings(data, "c.[1]", False, False)) == set(["3"]) + assert set(markings.get_markings(data, "c.[1]", True, False)) == set(["2", "3", "11"]) + assert set(markings.get_markings(data, "c.[1]", True, True)) == set(["2", "3", "11"]) + assert set(markings.get_markings(data, "c.[1]", False, True)) == set(["3"]) + + assert set(markings.get_markings(data, "c.[2]", False, False)) == set(["4"]) + assert set(markings.get_markings(data, "c.[2]", True, False)) == set(["2", "4", "11"]) + assert set(markings.get_markings(data, "c.[2]", True, True)) == set(["2", "4", "5", "11"]) + assert set(markings.get_markings(data, "c.[2]", False, True)) == set(["4", "5"]) + + assert set(markings.get_markings(data, "c.[2].g", False, False)) == set(["5"]) + assert set(markings.get_markings(data, "c.[2].g", True, False)) == set(["2", "4", "5", "11"]) + assert set(markings.get_markings(data, "c.[2].g", True, True)) == set(["2", "4", "5", "11"]) + assert set(markings.get_markings(data, "c.[2].g", False, True)) == set(["5"]) + + assert set(markings.get_markings(data, "x", False, False)) == set(["6"]) + assert set(markings.get_markings(data, "x", True, False)) == set(["6", "11"]) + assert set(markings.get_markings(data, "x", True, True)) == set(["6", "7", "8", "9", "10", "11"]) + assert set(markings.get_markings(data, "x", False, True)) == set(["6", "7", "8", "9", "10"]) + + assert set(markings.get_markings(data, "x.y", False, False)) == set(["7"]) + assert set(markings.get_markings(data, "x.y", True, False)) == set(["6", "7", "11"]) + assert set(markings.get_markings(data, "x.y", True, True)) == set(["6", "7", "8", "11"]) + assert set(markings.get_markings(data, "x.y", False, True)) == set(["7", "8"]) + + assert set(markings.get_markings(data, "x.y.[0]", False, False)) == set([]) + assert set(markings.get_markings(data, "x.y.[0]", True, False)) == set(["6", "7", "11"]) + assert set(markings.get_markings(data, "x.y.[0]", True, True)) == set(["6", "7", "11"]) + assert set(markings.get_markings(data, "x.y.[0]", False, True)) == set([]) + + assert set(markings.get_markings(data, "x.y.[1]", False, False)) == set(["8"]) + assert set(markings.get_markings(data, "x.y.[1]", True, False)) == set(["6", "7", "8", "11"]) + assert set(markings.get_markings(data, "x.y.[1]", True, True)) == set(["6", "7", "8", "11"]) + assert set(markings.get_markings(data, "x.y.[1]", False, True)) == set(["8"]) + + assert set(markings.get_markings(data, "x.z", False, False)) == set(["9"]) + assert set(markings.get_markings(data, "x.z", True, False)) == set(["6", "9", "11"]) + assert set(markings.get_markings(data, "x.z", True, True)) == set(["6", "9", "10", "11"]) + assert set(markings.get_markings(data, "x.z", False, True)) == set(["9", "10"]) + + assert set(markings.get_markings(data, "x.z.foo1", False, False)) == set([]) + assert set(markings.get_markings(data, "x.z.foo1", True, False)) == set(["6", "9", "11"]) + assert set(markings.get_markings(data, "x.z.foo1", True, True)) == set(["6", "9", "11"]) + assert set(markings.get_markings(data, "x.z.foo1", False, True)) == set([]) + + assert set(markings.get_markings(data, "x.z.foo2", False, False)) == set(["10"]) + assert set(markings.get_markings(data, "x.z.foo2", True, False)) == set(["6", "9", "10", "11"]) + assert set(markings.get_markings(data, "x.z.foo2", True, True)) == set(["6", "9", "10", "11"]) + assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"]) + + +def test_remove_markings_object_level(): + after = { + "title": "test title", + "description": "test description" + } + + before = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1"] + } + + markings.remove_markings(before, None, "marking-definition--1") + + assert before == after + + +def test_remove_markings_multiple(): + after = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--2"] + } + + before = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2", "marking-definition--3"] + } + + markings.remove_markings(before, None, ["marking-definition--1", "marking-definition--3"]) + + assert before == after + + +def test_remove_markings_bad_markings(): + before = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2", "marking-definition--3"] + } + with pytest.raises(AssertionError): + markings.remove_markings(before, None, ["marking-definition--5"]) + + +def test_clear_markings(): + after = { + "title": "test title", + "description": "test description" + } + + before = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2", "marking-definition--3"] + } + + markings.clear_markings(before, None) + + assert before == after + + +def test_is_marked_object_and_granular_combinations(): + """Test multiple combinations for inherited and descendant markings.""" + test_tlo = \ + { + "a": 333, + "b": "value", + "c": [ + 17, + "list value", + { + "g": "nested", + "h": 45 + } + ], + "x": { + "y": [ + "hello", + 88 + ], + "z": { + "foo1": "bar", + "foo2": 65 + } + }, + "object_marking_refs": "11", + "granular_markings": [ + { + "marking_ref": "1", + "selectors": ["a"] + }, + { + "marking_ref": "2", + "selectors": ["c"] + }, + { + "marking_ref": "3", + "selectors": ["c.[1]"] + }, + { + "marking_ref": "4", + "selectors": ["c.[2]"] + }, + { + "marking_ref": "5", + "selectors": ["c.[2].g"] + }, + { + "marking_ref": "6", + "selectors": ["x"] + }, + { + "marking_ref": "7", + "selectors": ["x.y"] + }, + { + "marking_ref": "8", + "selectors": ["x.y.[1]"] + }, + { + "marking_ref": "9", + "selectors": ["x.z"] + }, + { + "marking_ref": "10", + "selectors": ["x.z.foo2"] + }, + ] + } + + assert markings.is_marked(test_tlo, "a", ["1"], False, False) + assert markings.is_marked(test_tlo, "a", ["1", "11"], True, False) + assert markings.is_marked(test_tlo, "a", ["1", "11"], True, True) + assert markings.is_marked(test_tlo, "a", ["1"], False, True) + + assert markings.is_marked(test_tlo, "b", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "b", ["11"], True, False) + assert markings.is_marked(test_tlo, "b", ["11"], True, True) + assert markings.is_marked(test_tlo, "b", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "c", ["2"], False, False) + assert markings.is_marked(test_tlo, "c", ["2", "11"], True, False) + assert markings.is_marked(test_tlo, "c", ["2", "3", "4", "5", "11"], True, True) + assert markings.is_marked(test_tlo, "c", ["2", "3", "4", "5"], False, True) + + assert markings.is_marked(test_tlo, "c.[0]", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "c.[0]", ["2", "11"], True, False) + assert markings.is_marked(test_tlo, "c.[0]", ["2", "11"], True, True) + assert markings.is_marked(test_tlo, "c.[0]", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "c.[1]", ["3"], False, False) + assert markings.is_marked(test_tlo, "c.[1]", ["2", "3", "11"], True, False) + assert markings.is_marked(test_tlo, "c.[1]", ["2", "3", "11"], True, True) + assert markings.is_marked(test_tlo, "c.[1]", ["3"], False, True) + + assert markings.is_marked(test_tlo, "c.[2]", ["4"], False, False) + assert markings.is_marked(test_tlo, "c.[2]", ["2", "4", "11"], True, False) + assert markings.is_marked(test_tlo, "c.[2]", ["2", "4", "5", "11"], True, True) + assert markings.is_marked(test_tlo, "c.[2]", ["4", "5"], False, True) + + assert markings.is_marked(test_tlo, "c.[2].g", ["5"], False, False) + assert markings.is_marked(test_tlo, "c.[2].g", ["2", "4", "5", "11"], True, False) + assert markings.is_marked(test_tlo, "c.[2].g", ["2", "4", "5", "11"], True, True) + assert markings.is_marked(test_tlo, "c.[2].g", ["5"], False, True) + + assert markings.is_marked(test_tlo, "x", ["6"], False, False) + assert markings.is_marked(test_tlo, "x", ["6", "11"], True, False) + assert markings.is_marked(test_tlo, "x", ["6", "7", "8", "9", "10", "11"], True, True) + assert markings.is_marked(test_tlo, "x", ["6", "7", "8", "9", "10"], False, True) + + assert markings.is_marked(test_tlo, "x.y", ["7"], False, False) + assert markings.is_marked(test_tlo, "x.y", ["6", "7", "11"], True, False) + assert markings.is_marked(test_tlo, "x.y", ["6", "7", "8", "11"], True, True) + assert markings.is_marked(test_tlo, "x.y", ["7", "8"], False, True) + + assert markings.is_marked(test_tlo, "x.y.[0]", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "x.y.[0]", ["6", "7", "11"], True, False) + assert markings.is_marked(test_tlo, "x.y.[0]", ["6", "7", "11"], True, True) + assert markings.is_marked(test_tlo, "x.y.[0]", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "x.y.[1]", ["8"], False, False) + assert markings.is_marked(test_tlo, "x.y.[1]", ["6", "7", "8", "11"], True, False) + assert markings.is_marked(test_tlo, "x.y.[1]", ["6", "7", "8", "11"], True, True) + assert markings.is_marked(test_tlo, "x.y.[1]", ["8"], False, True) + + assert markings.is_marked(test_tlo, "x.z", ["9"], False, False) + assert markings.is_marked(test_tlo, "x.z", ["6", "9", "11"], True, False) + assert markings.is_marked(test_tlo, "x.z", ["6", "9", "10", "11"], True, True) + assert markings.is_marked(test_tlo, "x.z", ["9", "10"], False, True) + + assert markings.is_marked(test_tlo, "x.z.foo1", inherited=False, descendants=False) is False + assert markings.is_marked(test_tlo, "x.z.foo1", ["6", "9", "11"], True, False) + assert markings.is_marked(test_tlo, "x.z.foo1", ["6", "9", "11"], True, True) + assert markings.is_marked(test_tlo, "x.z.foo1", inherited=False, descendants=True) is False + + assert markings.is_marked(test_tlo, "x.z.foo2", ["10"], False, False) + assert markings.is_marked(test_tlo, "x.z.foo2", ["6", "9", "10", "11"], True, False) + assert markings.is_marked(test_tlo, "x.z.foo2", ["6", "9", "10", "11"], True, True) + assert markings.is_marked(test_tlo, "x.z.foo2", ["10"], False, True) + + +def test_set_marking(): + before = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--1", "marking-definition--2", "marking-definition--3"] + } + after = { + "title": "test title", + "description": "test description", + "object_marking_refs": ["marking-definition--7", "marking-definition--9"] + } + + markings.set_markings(before, None, ["marking-definition--7", "marking-definition--9"]) + + for m in before["object_marking_refs"]: + assert m in ["marking-definition--7", "marking-definition--9"] + + assert ["marking-definition--1", "marking-definition--2", "marking-definition--3"] not in before["object_marking_refs"] + + for x in before["object_marking_refs"]: + assert x in after["object_marking_refs"] + + +@pytest.mark.parametrize("data", [ + ([]), + ([""]), + (""), + (["marking-definition--7", 687]) +]) +def test_set_marking_bad_input(data): + before = { + "description": "test description", + "title": "foo", + "object_marking_refs": ["marking-definition--1"] + } + after = { + "description": "test description", + "title": "foo", + "object_marking_refs": ["marking-definition--1"] + } + with pytest.raises(AssertionError): + markings.set_markings(before, None, data) + + assert before == after From ee4618f6c831d1a53420726e08925e1cd1661269 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Mon, 12 Jun 2017 08:06:13 -0400 Subject: [PATCH 5/9] Add new marking errors. --- stix2/exceptions.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/stix2/exceptions.py b/stix2/exceptions.py index 3043047..7fa76c4 100644 --- a/stix2/exceptions.py +++ b/stix2/exceptions.py @@ -150,3 +150,29 @@ class RevokeError(STIXError, ValueError): return "Cannot revoke an already revoked object." else: return "Cannot create a new version of a revoked object." + + +class InvalidSelectorError(STIXError, ValueError): + """Granular Marking selector violation. The selector must resolve into an existing STIX object property.""" + + def __init__(self, cls, key): + super(InvalidSelectorError, self).__init__() + self.cls = cls + self.key = key + + def __str__(self): + msg = "Selector '{0}' in '{1}' is not valid!" + return msg.format(self.key, self.__class__.__name__) + + +class InvalidMarkingError(STIXError, ValueError): + """Marking violation. The marking reference must be a valid identifier.""" + + def __init__(self, cls, key): + super(InvalidMarkingError, self).__init__() + self.cls = cls + self.key = key + + def __str__(self): + msg = "Marking '{0}' in '{1}' is not a valid marking reference." + return msg.format(self.key, self.__class__.__name__) From bf740b21eb1b91bd268515f52d7d779e007e1db2 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Mon, 12 Jun 2017 08:06:37 -0400 Subject: [PATCH 6/9] Initial selector and marking_ref validation. --- stix2/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stix2/base.py b/stix2/base.py index c40c9e6..d78c801 100644 --- a/stix2/base.py +++ b/stix2/base.py @@ -11,6 +11,7 @@ from .exceptions import (AtLeastOnePropertyError, DependentPropertiesError, MissingPropertiesError, MutuallyExclusivePropertiesError, RevokeError, UnmodifiablePropertyError) +from .markings.utils import validate from .utils import NOW, format_datetime, get_timestamp, parse_into_datetime __all__ = ['STIXJSONEncoder', '_STIXBase'] @@ -80,8 +81,7 @@ class _STIXBase(collections.Mapping): def _check_object_constraints(self): for m in self.get("granular_markings", []): - # TODO: check selectors - pass + validate(self, m.get("selectors"), m.get("marking_ref")) def __init__(self, **kwargs): cls = self.__class__ From 7abcce7635c99e7d581fd05f51b1a9961ef19851 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Thu, 13 Jul 2017 07:55:52 -0400 Subject: [PATCH 7/9] Add new duplicate marking exception. --- stix2/exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stix2/exceptions.py b/stix2/exceptions.py index 7fa76c4..7e56117 100644 --- a/stix2/exceptions.py +++ b/stix2/exceptions.py @@ -165,11 +165,11 @@ class InvalidSelectorError(STIXError, ValueError): return msg.format(self.key, self.__class__.__name__) -class InvalidMarkingError(STIXError, ValueError): +class DuplicateMarkingError(STIXError, ValueError): """Marking violation. The marking reference must be a valid identifier.""" def __init__(self, cls, key): - super(InvalidMarkingError, self).__init__() + super(DuplicateMarkingError, self).__init__() self.cls = cls self.key = key From 747f0307a799885f9384aefcb8fab4a0a6d6107b Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Thu, 13 Jul 2017 07:57:01 -0400 Subject: [PATCH 8/9] [WIP] Changes to align python-stix2 with marking-prototype project. --- stix2/markings/__init__.py | 4 +- stix2/markings/object_markings.py | 82 +++++++++++-------------------- 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/stix2/markings/__init__.py b/stix2/markings/__init__.py index 8301131..14d21bb 100644 --- a/stix2/markings/__init__.py +++ b/stix2/markings/__init__.py @@ -116,9 +116,9 @@ def add_markings(obj, selectors, marking): """ if selectors is None: - object_markings.add_markings(obj, marking) + return object_markings.add_markings(obj, marking) else: - granular_markings.add_markings(obj, selectors, marking) + return granular_markings.add_markings(obj, selectors, marking) def clear_markings(obj, selectors): diff --git a/stix2/markings/object_markings.py b/stix2/markings/object_markings.py index 33a8d2a..9d2b2bc 100644 --- a/stix2/markings/object_markings.py +++ b/stix2/markings/object_markings.py @@ -1,28 +1,20 @@ -import six - from stix2.markings import utils def get_markings(obj): """ - Get all object level markings from the given TLO object. + Get all object level markings from the given SDO or SRO object. Args: - obj: A TLO object. + obj: A SDO or SRO object. Returns: - list: Marking IDs contained in the TLO. + list: Marking IDs contained in the SDO or SRO. Empty list if no + markings are present in `object_marking_refs`. """ - object_markings = obj.get("object_marking_refs", []) - - if not object_markings: - return [] - elif isinstance(object_markings, six.string_types): - return [object_markings] - else: - return object_markings + return obj.get("object_marking_refs", []) def add_markings(obj, marking): @@ -30,23 +22,19 @@ def add_markings(obj, marking): Appends an object level marking to the object_marking_refs collection. Args: - obj: A TLO object. - marking: identifier or list of marking identifiers that apply to the - TLO object. + obj: A SDO or SRO object. + marking: identifier or list of identifiers to apply SDO or SRO object. Raises: AssertionError: If `marking` fail data validation. """ marking = utils.convert_to_list(marking) - utils.validate(obj, marking=marking) - if not obj.get("object_marking_refs"): - obj["object_marking_refs"] = list() + # TODO: Remove set for comparison and raise DuplicateMarkingException. + object_markings = set(obj.get("object_marking_refs", []) + marking) - object_markings = set(obj.get("object_marking_refs") + marking) - - obj["object_marking_refs"] = list(object_markings) + return obj.new_version(object_marking_refs=list(object_markings)) def remove_markings(obj, marking): @@ -54,13 +42,13 @@ def remove_markings(obj, marking): Removes object level marking from the object_marking_refs collection. Args: - obj: A TLO object. - marking: identifier or list of marking identifiers that apply to the - TLO object. + obj: A SDO or SRO object. + marking: identifier or list of identifiers that apply to the + SDO or SRO object. Raises: - AssertionError: If `marking` fail data validation. Also - if markings to remove are not found on the provided TLO. + AssertionError: If markings to remove are not found on the provided + SDO or SRO. """ marking = utils.convert_to_list(marking) @@ -69,17 +57,14 @@ def remove_markings(obj, marking): object_markings = obj.get("object_marking_refs", []) if not object_markings: - return [] + return obj if any(x not in obj["object_marking_refs"] for x in marking): raise AssertionError("Unable to remove Object Level Marking(s) from " "internal collection. Marking(s) not found...") - obj["object_marking_refs"] = [x for x in object_markings - if x not in marking] - - if not obj.get("object_marking_refs"): - obj.pop("object_marking_refs") + return obj.new_version(object_marking_refs=[x for x in object_markings + if x not in marking]) def set_markings(obj, marking): @@ -88,15 +73,12 @@ def set_markings(obj, marking): the collection. Refer to `clear_markings` and `add_markings` for details. Args: - obj: A TLO object. - marking: identifier or list of marking identifiers that apply to the - TLO object. + obj: A SDO or SRO object. + marking: identifier or list of identifiers to apply in the + SDO or SRO object. """ - utils.validate(obj, marking=marking) - - clear_markings(obj) - add_markings(obj, marking) + return add_markings(clear_markings(obj), marking) def clear_markings(obj): @@ -104,31 +86,27 @@ def clear_markings(obj): Removes all object level markings from the object_marking_refs collection. Args: - obj: A TLO object. + obj: A SDO or SRO object. """ - try: - del obj["object_marking_refs"] - except KeyError: - raise AssertionError("Unable to clear Object Marking(s) from internal" - " collection. No Markings in object...") + return obj.new_version(object_marking_refs=None) def is_marked(obj, marking=None): """ - Checks if TLO is marked by any marking or by specific marking(s). + Checks if SDO or SRO is marked by any marking or by specific marking(s). Args: - obj: A TLO object. + obj: A SDO or SRO object. marking: identifier or list of marking identifiers that apply to the - TLO object. + SDO or SRO object. Returns: - bool: True if TLO has object level markings. False otherwise. + bool: True if SDO or SRO has object level markings. False otherwise. Note: - When a list of marking IDs is provided, if ANY of the provided marking - IDs matches, True is returned. + When an identifier or list of identifiers is provided, if ANY of the + provided marking refs match, True is returned. """ marking = utils.convert_to_list(marking) From 6d2cfcdedf81741be64505227c8b7965a481e429 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Thu, 13 Jul 2017 07:57:33 -0400 Subject: [PATCH 9/9] [WIP] Update tests. --- stix2/test/test_markings.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stix2/test/test_markings.py b/stix2/test/test_markings.py index f1f07db..58a0864 100644 --- a/stix2/test/test_markings.py +++ b/stix2/test/test_markings.py @@ -5,10 +5,10 @@ import pytz import stix2 from stix2.other import TLP_WHITE +from stix2 import markings from .constants import MARKING_DEFINITION_ID - EXPECTED_TLP_MARKING_DEFINITION = """{ "created": "2017-01-20T00:00:00Z", "definition": { @@ -119,7 +119,8 @@ def test_campaign_with_granular_markings_example(): marking_ref="marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", selectors=["description"]) ]) - print(str(campaign)) + print (markings.get_markings(campaign, None)) + print (markings.add_markings(campaign, None, "marking-definition--00000000-0000-0000-0000-000000000000")) assert str(campaign) == EXPECTED_CAMPAIGN_WITH_GRANULAR_MARKINGS