diff --git a/stix2/markings/__init__.py b/stix2/markings/__init__.py index 79d1012..b0dba6b 100644 --- a/stix2/markings/__init__.py +++ b/stix2/markings/__init__.py @@ -22,7 +22,7 @@ Note: from stix2.markings import granular_markings, object_markings -def get_markings(obj, selectors=None, inherited=False, descendants=False): +def get_markings(obj, selectors=None, inherited=False, descendants=False, marking_ref=True, lang=True): """ Get all markings associated to the field(s) specified by selectors. @@ -30,10 +30,13 @@ def get_markings(obj, selectors=None, inherited=False, descendants=False): obj: An SDO or SRO object. selectors: string or list of selectors strings relative to the SDO or SRO in which the properties appear. - inherited: If True, include object level markings and granular markings - inherited relative to the properties. - descendants: If True, include granular markings applied to any children - relative to the properties. + inherited (bool): If True, include object level markings and granular + markings inherited relative to the properties. + descendants (bool): If True, include granular markings applied to any + children relative to the properties. + marking_ref (bool): If False, excludes markings that use + ``marking_ref`` property. + lang (bool): If False, excludes markings that use ``lang`` property. Returns: list: Marking identifiers that matched the selectors expression. @@ -51,6 +54,8 @@ def get_markings(obj, selectors=None, inherited=False, descendants=False): selectors, inherited, descendants, + marking_ref, + lang ) if inherited: @@ -59,7 +64,7 @@ def get_markings(obj, selectors=None, inherited=False, descendants=False): return list(set(results)) -def set_markings(obj, marking, selectors=None): +def set_markings(obj, marking, selectors=None, marking_ref=True, lang=True): """ Remove all markings associated with selectors and appends a new granular marking. Refer to `clear_markings` and `add_markings` for details. @@ -70,6 +75,10 @@ def set_markings(obj, marking, selectors=None): properties selected by `selectors`. selectors: string or list of selectors strings relative to the SDO or SRO in which the properties appear. + marking_ref (bool): If False, markings that use the ``marking_ref`` + property will not be removed. + lang (bool): If False, markings that use the ``lang`` property + will not be removed. Returns: A new version of the given SDO or SRO with specified markings removed @@ -83,7 +92,7 @@ def set_markings(obj, marking, selectors=None): if selectors is None: return object_markings.set_markings(obj, marking) else: - return granular_markings.set_markings(obj, marking, selectors) + return granular_markings.set_markings(obj, marking, selectors, marking_ref, lang) def remove_markings(obj, marking, selectors=None): @@ -144,7 +153,7 @@ def add_markings(obj, marking, selectors=None): return granular_markings.add_markings(obj, marking, selectors) -def clear_markings(obj, selectors=None): +def clear_markings(obj, selectors=None, marking_ref=True, lang=True): """ Remove all markings associated with the selectors. @@ -152,6 +161,10 @@ def clear_markings(obj, selectors=None): obj: An SDO or SRO object. selectors: string or list of selectors strings relative to the SDO or SRO in which the field(s) appear(s). + marking_ref (bool): If False, markings that use the ``marking_ref`` + property will not be removed. + lang (bool): If False, markings that use the ``lang`` property + will not be removed. Raises: InvalidSelectorError: If `selectors` fail validation. @@ -169,7 +182,7 @@ def clear_markings(obj, selectors=None): if selectors is None: return object_markings.clear_markings(obj) else: - return granular_markings.clear_markings(obj, selectors) + return granular_markings.clear_markings(obj, selectors, marking_ref, lang) def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=False): @@ -182,10 +195,11 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa properties selected by `selectors`. selectors: string or list of selectors strings relative to the SDO or SRO in which the field(s) appear(s). - inherited: If True, include object level markings and granular markings - inherited to determine if the properties is/are marked. - descendants: If True, include granular markings applied to any children - of the given selector to determine if the properties is/are marked. + inherited (bool): If True, include object level markings and granular + markings inherited to determine if the properties is/are marked. + descendants (bool): If True, include granular markings applied to any + children of the given selector to determine if the properties + is/are marked. Returns: bool: True if ``selectors`` is found on internal SDO or SRO collection. @@ -228,7 +242,7 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa return result -class _MarkingsMixin(): +class _MarkingsMixin(object): pass diff --git a/stix2/markings/granular_markings.py b/stix2/markings/granular_markings.py index 09c3d37..a1c8479 100644 --- a/stix2/markings/granular_markings.py +++ b/stix2/markings/granular_markings.py @@ -2,10 +2,10 @@ from stix2 import exceptions from stix2.markings import utils -from stix2.utils import new_version +from stix2.utils import new_version, is_marking -def get_markings(obj, selectors, inherited=False, descendants=False): +def get_markings(obj, selectors, inherited=False, descendants=False, marking_ref=True, lang=True): """ Get all granular markings associated to with the properties. @@ -13,10 +13,13 @@ def get_markings(obj, selectors, inherited=False, descendants=False): obj: An SDO or SRO object. selectors: string or list of selector strings relative to the SDO or SRO in which the properties appear. - inherited: If True, include markings inherited relative to the + inherited (bool): If True, include markings inherited relative to the properties. - descendants: If True, include granular markings applied to any children - relative to the properties. + descendants (bool): If True, include granular markings applied to any + children relative to the properties. + marking_ref (bool): If False, excludes markings that use + ``marking_ref`` property. + lang (bool): If False, excludes markings that use ``lang`` property. Raises: InvalidSelectorError: If `selectors` fail validation. @@ -43,13 +46,18 @@ def get_markings(obj, selectors, inherited=False, descendants=False): (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]) + ref = marking.get('marking_ref') + lng = marking.get('lang') + + if ref and marking_ref: + results.add(ref) + if lng and lang: + results.add(lng) return list(results) -def set_markings(obj, marking, selectors): +def set_markings(obj, marking, selectors, marking_ref=True, lang=True): """ Remove all granular markings associated with selectors and append a new granular marking. Refer to `clear_markings` and `add_markings` for details. @@ -60,19 +68,25 @@ def set_markings(obj, marking, selectors): SRO in which the properties appear. marking: identifier or list of marking identifiers that apply to the properties selected by `selectors`. + marking_ref (bool): If False, markings that use the ``marking_ref`` + property will not be removed. + lang (bool): If False, markings that use the ``lang`` property + will not be removed. Returns: A new version of the given SDO or SRO with specified markings removed and new ones added. """ - obj = clear_markings(obj, selectors) + obj = clear_markings(obj, selectors, marking_ref, lang) return add_markings(obj, marking, selectors) def remove_markings(obj, marking, selectors): """ - Remove a granular marking from the granular_markings collection. + Remove a granular marking from the granular_markings collection. The method + makes a best-effort attempt to distinguish between a marking-definition + or language granular marking. Args: obj: An SDO or SRO object. @@ -103,7 +117,10 @@ def remove_markings(obj, marking, selectors): to_remove = [] for m in marking: - to_remove.append({'marking_ref': m, 'selectors': selectors}) + if is_marking(m): + to_remove.append({'marking_ref': m, 'selectors': selectors}) + else: + to_remove.append({'lang': m, 'selectors': selectors}) remove = utils.build_granular_marking(to_remove).get('granular_markings') @@ -124,7 +141,9 @@ def remove_markings(obj, marking, selectors): def add_markings(obj, marking, selectors): """ - Append a granular marking to the granular_markings collection. + Append a granular marking to the granular_markings collection. The method + makes a best-effort attempt to distinguish between a marking-definition + or language granular marking. Args: obj: An SDO or SRO object. @@ -146,7 +165,10 @@ def add_markings(obj, marking, selectors): granular_marking = [] for m in marking: - granular_marking.append({'marking_ref': m, 'selectors': sorted(selectors)}) + if is_marking(m): + granular_marking.append({'marking_ref': m, 'selectors': sorted(selectors)}) + else: + granular_marking.append({'lang': m, 'selectors': sorted(selectors)}) if obj.get('granular_markings'): granular_marking.extend(obj.get('granular_markings')) @@ -156,7 +178,7 @@ def add_markings(obj, marking, selectors): return new_version(obj, granular_markings=granular_marking, allow_custom=True) -def clear_markings(obj, selectors): +def clear_markings(obj, selectors, marking_ref=True, lang=True): """ Remove all granular markings associated with the selectors. @@ -164,6 +186,10 @@ def clear_markings(obj, selectors): obj: An SDO or SRO object. selectors: string or list of selectors strings relative to the SDO or SRO in which the properties appear. + marking_ref (bool): If False, markings that use the ``marking_ref`` + property will not be removed. + lang (bool): If False, markings that use the ``lang`` property + will not be removed. Raises: InvalidSelectorError: If `selectors` fail validation. @@ -184,11 +210,12 @@ def clear_markings(obj, selectors): granular_markings = utils.expand_markings(granular_markings) - sdo = utils.build_granular_marking( - [{'selectors': selectors, 'marking_ref': 'N/A'}], - ) + granular_dict = utils.build_granular_marking([ + {'selectors': selectors, 'marking_ref': 'N/A'}, + {'selectors': selectors, 'lang': 'N/A'} + ]) - clear = sdo.get('granular_markings', []) + clear = granular_dict.get('granular_markings', []) if not any( clear_selector in sdo_selectors.get('selectors', []) @@ -201,10 +228,13 @@ def clear_markings(obj, selectors): for granular_marking in granular_markings: for s in selectors: if s in granular_marking.get('selectors', []): - marking_refs = granular_marking.get('marking_ref') + ref = granular_marking.get('marking_ref') + lng = granular_marking.get('lang') - if marking_refs: + if ref and marking_ref: granular_marking['marking_ref'] = '' + if lng and lang: + granular_marking['lang'] = '' granular_markings = utils.compress_markings(granular_markings) @@ -222,11 +252,12 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa obj: An SDO or SRO object. marking: identifier or list of marking identifiers that apply to the properties selected by `selectors`. - selectors: string or list of selectors strings relative to the SDO or - SRO in which the properties appear. - inherited: If True, return markings inherited from the given selector. - descendants: If True, return granular markings applied to any children - of the given selector. + selectors (bool): string or list of selectors strings relative to the + SDO or SRO in which the properties appear. + inherited (bool): If True, return markings inherited from the given + selector. + descendants (bool): If True, return granular markings applied to any + children of the given selector. Raises: InvalidSelectorError: If `selectors` fail validation. @@ -262,9 +293,12 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa (marking_selector.startswith(user_selector) and descendants), ]): # Catch descendants selectors marking_ref = granular_marking.get('marking_ref', '') + lang = granular_marking.get('lang', '') if marking and any(x == marking_ref for x in marking): markings.update([marking_ref]) + if marking and any(x == lang for x in marking): + markings.update([lang]) marked = True diff --git a/stix2/markings/utils.py b/stix2/markings/utils.py index d8bbf1d..7a6aae1 100644 --- a/stix2/markings/utils.py +++ b/stix2/markings/utils.py @@ -5,6 +5,7 @@ import collections import six from stix2 import exceptions +from stix2.utils import is_marking def _evaluate_expression(obj, selector): @@ -121,10 +122,15 @@ def compress_markings(granular_markings): if granular_marking.get('marking_ref'): map_[granular_marking.get('marking_ref')].update(granular_marking.get('selectors')) + if granular_marking.get('lang'): + map_[granular_marking.get('lang')].update(granular_marking.get('selectors')) + compressed = \ [ - {'marking_ref': marking_ref, 'selectors': sorted(selectors)} - for marking_ref, selectors in six.iteritems(map_) + {'marking_ref': item, 'selectors': sorted(selectors)} + if is_marking(item) else + {'lang': item, 'selectors': sorted(selectors)} + for item, selectors in six.iteritems(map_) ] return compressed @@ -174,13 +180,22 @@ def expand_markings(granular_markings): for marking in granular_markings: selectors = marking.get('selectors') marking_ref = marking.get('marking_ref') + lang = marking.get('lang') - expanded.extend( - [ - {'marking_ref': marking_ref, 'selectors': [selector]} - for selector in selectors - ], - ) + if marking_ref: + expanded.extend( + [ + {'marking_ref': marking_ref, 'selectors': [selector]} + for selector in selectors + ], + ) + if lang: + expanded.extend( + [ + {'lang': lang, 'selectors': [selector]} + for selector in selectors + ], + ) return expanded