2018-11-28 22:51:00 +01:00
|
|
|
"""Functions for working with STIX2 granular markings."""
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-24 18:47:14 +02:00
|
|
|
from stix2 import exceptions
|
2017-06-09 20:21:42 +02:00
|
|
|
from stix2.markings import utils
|
2020-05-28 22:48:51 +02:00
|
|
|
from stix2.utils import is_marking
|
|
|
|
from stix2.versioning import new_version
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
def get_markings(obj, selectors, inherited=False, descendants=False, marking_ref=True, lang=True):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2017-11-09 16:10:19 +01:00
|
|
|
Get all granular markings associated to with the properties.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
|
|
|
selectors: string or list of selector strings relative to the SDO or
|
|
|
|
SRO in which the properties appear.
|
2019-04-22 21:25:46 +02:00
|
|
|
inherited (bool): If True, include markings inherited relative to the
|
2017-08-24 18:47:14 +02:00
|
|
|
properties.
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-08-24 18:47:14 +02:00
|
|
|
|
|
|
|
Raises:
|
|
|
|
InvalidSelectorError: If `selectors` fail validation.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Returns:
|
2017-08-24 18:47:14 +02:00
|
|
|
list: Marking identifiers that matched the selectors expression.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2017-08-24 18:47:14 +02:00
|
|
|
selectors = utils.convert_to_list(selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
utils.validate(obj, selectors)
|
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
granular_markings = obj.get('granular_markings', [])
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
if not granular_markings:
|
|
|
|
return []
|
|
|
|
|
|
|
|
results = set()
|
|
|
|
|
|
|
|
for marking in granular_markings:
|
|
|
|
for user_selector in selectors:
|
2018-06-30 00:38:04 +02:00
|
|
|
for marking_selector in marking.get('selectors', []):
|
2018-07-13 17:10:05 +02:00
|
|
|
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
|
2019-04-22 21:25:46 +02:00
|
|
|
ref = marking.get('marking_ref')
|
|
|
|
lng = marking.get('lang')
|
|
|
|
|
|
|
|
if ref and marking_ref:
|
|
|
|
results.add(ref)
|
|
|
|
if lng and lang:
|
|
|
|
results.add(lng)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
return list(results)
|
|
|
|
|
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
def set_markings(obj, marking, selectors, marking_ref=True, lang=True):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2017-11-09 16:10:19 +01:00
|
|
|
Remove all granular markings associated with selectors and append a new
|
|
|
|
granular marking. Refer to `clear_markings` and `add_markings` for details.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
|
|
|
selectors: string or list of selector strings relative to the SDO or
|
|
|
|
SRO in which the properties appear.
|
2017-06-09 20:21:42 +02:00
|
|
|
marking: identifier or list of marking identifiers that apply to the
|
2017-08-24 18:47:14 +02:00
|
|
|
properties selected by `selectors`.
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-08-24 18:47:14 +02:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new version of the given SDO or SRO with specified markings removed
|
|
|
|
and new ones added.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2019-04-22 21:25:46 +02:00
|
|
|
obj = clear_markings(obj, selectors, marking_ref, lang)
|
2017-09-01 16:50:01 +02:00
|
|
|
return add_markings(obj, marking, selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
|
2017-09-01 16:50:01 +02:00
|
|
|
def remove_markings(obj, marking, selectors):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
2017-06-09 20:21:42 +02:00
|
|
|
marking: identifier or list of marking identifiers that apply to the
|
2017-08-24 18:47:14 +02:00
|
|
|
properties selected by `selectors`.
|
2017-11-09 16:10:19 +01:00
|
|
|
selectors: string or list of selectors strings relative to the SDO or
|
|
|
|
SRO in which the properties appear.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Raises:
|
2017-08-24 18:47:14 +02:00
|
|
|
InvalidSelectorError: If `selectors` fail validation.
|
|
|
|
MarkingNotFoundError: If markings to remove are not found on
|
|
|
|
the provided SDO or SRO.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new version of the given SDO or SRO with specified markings removed.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2017-08-24 18:47:14 +02:00
|
|
|
selectors = utils.convert_to_list(selectors)
|
2017-10-02 18:28:47 +02:00
|
|
|
marking = utils.convert_to_marking_list(marking)
|
2017-08-24 18:47:14 +02:00
|
|
|
utils.validate(obj, selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
granular_markings = obj.get('granular_markings')
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
if not granular_markings:
|
2017-08-23 19:06:51 +02:00
|
|
|
return obj
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-23 19:06:51 +02:00
|
|
|
granular_markings = utils.expand_markings(granular_markings)
|
|
|
|
|
2017-10-02 18:28:47 +02:00
|
|
|
to_remove = []
|
|
|
|
for m in marking:
|
2019-04-22 21:25:46 +02:00
|
|
|
if is_marking(m):
|
|
|
|
to_remove.append({'marking_ref': m, 'selectors': selectors})
|
|
|
|
else:
|
|
|
|
to_remove.append({'lang': m, 'selectors': selectors})
|
2017-08-23 19:06:51 +02:00
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
remove = utils.build_granular_marking(to_remove).get('granular_markings')
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
if not any(marking in granular_markings for marking in remove):
|
2017-08-24 18:47:14 +02:00
|
|
|
raise exceptions.MarkingNotFoundError(obj, remove)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-23 19:06:51 +02:00
|
|
|
granular_markings = [
|
2017-06-09 20:21:42 +02:00
|
|
|
m for m in granular_markings if m not in remove
|
|
|
|
]
|
|
|
|
|
2017-08-23 19:06:51 +02:00
|
|
|
granular_markings = utils.compress_markings(granular_markings)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-24 18:47:14 +02:00
|
|
|
if granular_markings:
|
2018-03-02 17:32:07 +01:00
|
|
|
return new_version(obj, granular_markings=granular_markings, allow_custom=True)
|
2017-08-24 18:47:14 +02:00
|
|
|
else:
|
2018-03-02 17:32:07 +01:00
|
|
|
return new_version(obj, granular_markings=None, allow_custom=True)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
|
2017-09-01 16:50:01 +02:00
|
|
|
def add_markings(obj, marking, selectors):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
|
|
|
marking: identifier or list of marking identifiers that apply to the
|
|
|
|
properties selected by `selectors`.
|
2017-11-09 16:10:19 +01:00
|
|
|
selectors: list of type string, selectors must be relative to the TLO
|
|
|
|
in which the properties appear.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Raises:
|
2017-08-24 18:47:14 +02:00
|
|
|
InvalidSelectorError: If `selectors` fail validation.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new version of the given SDO or SRO with specified markings added.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2017-08-24 18:47:14 +02:00
|
|
|
selectors = utils.convert_to_list(selectors)
|
2017-10-02 18:28:47 +02:00
|
|
|
marking = utils.convert_to_marking_list(marking)
|
2017-08-24 18:47:14 +02:00
|
|
|
utils.validate(obj, selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-10-02 18:28:47 +02:00
|
|
|
granular_marking = []
|
|
|
|
for m in marking:
|
2019-04-22 21:25:46 +02:00
|
|
|
if is_marking(m):
|
|
|
|
granular_marking.append({'marking_ref': m, 'selectors': sorted(selectors)})
|
|
|
|
else:
|
|
|
|
granular_marking.append({'lang': m, 'selectors': sorted(selectors)})
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
if obj.get('granular_markings'):
|
|
|
|
granular_marking.extend(obj.get('granular_markings'))
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-23 19:06:51 +02:00
|
|
|
granular_marking = utils.expand_markings(granular_marking)
|
|
|
|
granular_marking = utils.compress_markings(granular_marking)
|
2018-03-02 17:32:07 +01:00
|
|
|
return new_version(obj, granular_markings=granular_marking, allow_custom=True)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
def clear_markings(obj, selectors, marking_ref=True, lang=True):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2017-11-09 16:10:19 +01:00
|
|
|
Remove all granular markings associated with the selectors.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
|
|
|
selectors: string or list of selectors strings relative to the SDO or
|
|
|
|
SRO in which the properties appear.
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Raises:
|
2017-08-24 18:47:14 +02:00
|
|
|
InvalidSelectorError: If `selectors` fail validation.
|
|
|
|
MarkingNotFoundError: If markings to remove are not found on
|
|
|
|
the provided SDO or SRO.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new version of the given SDO or SRO with specified markings cleared.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2017-08-24 18:47:14 +02:00
|
|
|
selectors = utils.convert_to_list(selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
utils.validate(obj, selectors)
|
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
granular_markings = obj.get('granular_markings')
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
if not granular_markings:
|
2017-08-23 19:06:51 +02:00
|
|
|
return obj
|
|
|
|
|
|
|
|
granular_markings = utils.expand_markings(granular_markings)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
granular_dict = utils.build_granular_marking([
|
|
|
|
{'selectors': selectors, 'marking_ref': 'N/A'},
|
2019-04-23 13:43:56 +02:00
|
|
|
{'selectors': selectors, 'lang': 'N/A'},
|
2019-04-22 21:25:46 +02:00
|
|
|
])
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
clear = granular_dict.get('granular_markings', [])
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2018-07-13 17:10:05 +02:00
|
|
|
if not any(
|
|
|
|
clear_selector in sdo_selectors.get('selectors', [])
|
|
|
|
for sdo_selectors in granular_markings
|
|
|
|
for clear_marking in clear
|
|
|
|
for clear_selector in clear_marking.get('selectors', [])
|
|
|
|
):
|
2017-08-24 18:47:14 +02:00
|
|
|
raise exceptions.MarkingNotFoundError(obj, clear)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
for granular_marking in granular_markings:
|
|
|
|
for s in selectors:
|
2018-06-30 00:38:04 +02:00
|
|
|
if s in granular_marking.get('selectors', []):
|
2019-04-22 21:25:46 +02:00
|
|
|
ref = granular_marking.get('marking_ref')
|
|
|
|
lng = granular_marking.get('lang')
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2019-04-22 21:25:46 +02:00
|
|
|
if ref and marking_ref:
|
2018-06-30 00:38:04 +02:00
|
|
|
granular_marking['marking_ref'] = ''
|
2019-04-22 21:25:46 +02:00
|
|
|
if lng and lang:
|
|
|
|
granular_marking['lang'] = ''
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-23 19:06:51 +02:00
|
|
|
granular_markings = utils.compress_markings(granular_markings)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-24 18:47:14 +02:00
|
|
|
if granular_markings:
|
2018-03-02 17:32:07 +01:00
|
|
|
return new_version(obj, granular_markings=granular_markings, allow_custom=True)
|
2017-08-24 18:47:14 +02:00
|
|
|
else:
|
2018-03-02 17:32:07 +01:00
|
|
|
return new_version(obj, granular_markings=None, allow_custom=True)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
|
2017-09-01 16:50:01 +02:00
|
|
|
def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=False):
|
2017-06-09 20:21:42 +02:00
|
|
|
"""
|
2017-11-09 16:10:19 +01:00
|
|
|
Check if field is marked by any marking or by specific marking(s).
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
Args:
|
2017-08-24 18:47:14 +02:00
|
|
|
obj: An SDO or SRO object.
|
2017-06-09 20:21:42 +02:00
|
|
|
marking: identifier or list of marking identifiers that apply to the
|
2017-08-24 18:47:14 +02:00
|
|
|
properties selected by `selectors`.
|
2019-04-22 21:25:46 +02:00
|
|
|
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.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2017-08-24 18:47:14 +02:00
|
|
|
Raises:
|
|
|
|
InvalidSelectorError: If `selectors` fail validation.
|
|
|
|
|
2017-06-09 20:21:42 +02:00
|
|
|
Returns:
|
2017-08-24 18:47:14 +02:00
|
|
|
bool: True if ``selectors`` is found on internal SDO or SRO collection.
|
2017-06-09 20:21:42 +02:00
|
|
|
False otherwise.
|
|
|
|
|
|
|
|
Note:
|
2017-08-24 18:47:14 +02:00
|
|
|
When a list of marking identifiers is provided, if ANY of the provided
|
|
|
|
marking identifiers match, True is returned.
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
"""
|
2017-09-01 16:50:01 +02:00
|
|
|
if selectors is None:
|
|
|
|
raise TypeError("Required argument 'selectors' must be provided")
|
|
|
|
|
2017-08-24 18:47:14 +02:00
|
|
|
selectors = utils.convert_to_list(selectors)
|
2017-10-02 18:28:47 +02:00
|
|
|
marking = utils.convert_to_marking_list(marking)
|
2017-08-24 18:47:14 +02:00
|
|
|
utils.validate(obj, selectors)
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2018-06-30 00:38:04 +02:00
|
|
|
granular_markings = obj.get('granular_markings', [])
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
marked = False
|
|
|
|
markings = set()
|
|
|
|
|
|
|
|
for granular_marking in granular_markings:
|
|
|
|
for user_selector in selectors:
|
2018-06-30 00:38:04 +02:00
|
|
|
for marking_selector in granular_marking.get('selectors', []):
|
2017-06-09 20:21:42 +02:00
|
|
|
|
2018-07-13 17:10:05 +02:00
|
|
|
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
|
2018-06-30 00:38:04 +02:00
|
|
|
marking_ref = granular_marking.get('marking_ref', '')
|
2019-04-22 21:25:46 +02:00
|
|
|
lang = granular_marking.get('lang', '')
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
if marking and any(x == marking_ref for x in marking):
|
|
|
|
markings.update([marking_ref])
|
2019-04-22 21:25:46 +02:00
|
|
|
if marking and any(x == lang for x in marking):
|
|
|
|
markings.update([lang])
|
2017-06-09 20:21:42 +02:00
|
|
|
|
|
|
|
marked = True
|
|
|
|
|
|
|
|
if marking:
|
|
|
|
# All user-provided markings must be found.
|
|
|
|
return markings.issuperset(set(marking))
|
|
|
|
|
|
|
|
return marked
|