Code refactor/clean-up, changed some exceptions. Update docstrings.

stix2.1
Emmanuelle Vargas-Gonzalez 2017-08-24 12:47:14 -04:00
parent 8d4c1d55b5
commit dd17e88ae0
7 changed files with 259 additions and 198 deletions

View File

@ -81,7 +81,7 @@ class _STIXBase(collections.Mapping):
def _check_object_constraints(self):
for m in self.get("granular_markings", []):
validate(self, m.get("selectors"), m.get("marking_ref"))
validate(self, m.get("selectors"))
def __init__(self, allow_custom=False, **kwargs):
cls = self.__class__

View File

@ -159,7 +159,7 @@ class ParseError(STIXError, ValueError):
super(ParseError, self).__init__(msg)
class InvalidSelectorError(STIXError, ValueError):
class InvalidSelectorError(STIXError, AssertionError):
"""Granular Marking selector violation. The selector must resolve into an existing STIX object property."""
def __init__(self, cls, key):
@ -168,31 +168,18 @@ class InvalidSelectorError(STIXError, ValueError):
self.key = key
def __str__(self):
msg = "Selector '{0}' in '{1}' is not valid!"
return msg.format(self.key, self.__class__.__name__)
msg = "Selector {0} in {1} is not valid!"
return msg.format(self.key, self.cls.__class__.__name__)
class InvalidMarkingError(STIXError, ValueError):
"""Marking violation. The marking reference must be a valid identifier."""
class MarkingNotFoundError(STIXError, AssertionError):
"""Marking violation. The marking reference must be present in SDO or SRO."""
def __init__(self, cls, key):
super(InvalidMarkingError, self).__init__()
super(MarkingNotFoundError, 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__)
class DuplicateMarkingError(STIXError, ValueError):
"""Marking violation. The marking reference is a duplicate."""
def __init__(self, cls, key):
super(DuplicateMarkingError, self).__init__()
self.cls = cls
self.key = key
def __str__(self):
msg = "Marking '{0}' in '{1}' is a duplicate marking reference."
return msg.format(self.key, self.__class__.__name__)
msg = "Marking {0} was not found in {1}!"
return msg.format(self.key, self.cls.__class__.__name__)

View File

@ -13,16 +13,16 @@ 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).
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 field(s).
inherited relative to the properties.
descendants: If True, include granular markings applied to any children
relative to the field(s).
relative to the properties.
Returns:
list: Marking IDs that matched the selectors expression.
list: Marking identifiers that matched the selectors expression.
Note:
If ``selectors`` is None, operation will be performed only on object
@ -51,11 +51,15 @@ def set_markings(obj, selectors, marking):
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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
Returns:
A new version of the given SDO or SRO with specified markings removed
and new ones added.
Note:
If ``selectors`` is None, operations will be performed on object level
@ -73,15 +77,19 @@ 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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
Raises:
AssertionError: If `selectors` or `marking` fail data validation. Also
if markings to remove are not found on the provided TLO.
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.
Note:
If ``selectors`` is None, operations will be performed on object level
@ -99,14 +107,17 @@ 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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
Raises:
AssertionError: If `selectors` or `marking` fail data validation.
InvalidSelectorError: If `selectors` fail validation.
Returns:
A new version of the given SDO or SRO with specified markings added.
Note:
If ``selectors`` is None, operations will be performed on object level
@ -124,9 +135,17 @@ 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).
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).
Raises:
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.
Note:
If ``selectors`` is None, operations will be performed on object level
@ -144,23 +163,23 @@ 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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
inherited: If True, include object level markings and granular markings
inherited to determine if the field(s) is/are marked.
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 field(s) is/are marked.
of the given selector to determine if the properties is/are marked.
Returns:
bool: True if ``selectors`` is found on internal TLO collection.
bool: True if ``selectors`` is found on internal SDO or SRO collection.
False otherwise.
Note:
When a list of marking IDs is provided, if ANY of the provided marking
IDs matches, True is returned.
When a list of marking identifiers is provided, if ANY of the provided
marking identifiers match, True is returned.
If ``selectors`` is None, operation will be performed only on object
level markings.

View File

@ -1,24 +1,29 @@
from stix2 import exceptions
from stix2.markings import utils
def get_markings(obj, selectors, inherited=False, descendants=False):
"""
Get all markings associated to the field(s).
Get all markings associated to with the properties.
Args:
obj: A TLO object.
selectors: string or list of selector strings relative to the TLO in
which the field(s) appear(s).
inherited: If True, include markings inherited relative to the field(s).
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
properties.
descendants: If True, include granular markings applied to any children
relative to the field(s).
relative to the properties.
Raises:
InvalidSelectorError: If `selectors` fail validation.
Returns:
list: Marking IDs that matched the selectors expression.
list: Marking identifiers that matched the selectors expression.
"""
selectors = utils.fix_value(selectors)
selectors = utils.convert_to_list(selectors)
utils.validate(obj, selectors)
granular_markings = obj.get("granular_markings", [])
@ -46,11 +51,15 @@ def set_markings(obj, selectors, marking):
marking. Refer to `clear_markings` and `add_markings` for details.
Args:
obj: A TLO object.
selectors: string or list of selector strings relative to the TLO in
which the field(s) appear(s).
obj: An SDO or SRO object.
selectors: string or list of selector strings relative to the SDO or
SRO in which the properties appear.
marking: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
Returns:
A new version of the given SDO or SRO with specified markings removed
and new ones added.
"""
obj = clear_markings(obj, selectors)
@ -62,19 +71,23 @@ 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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties selected by `selectors`.
Raises:
AssertionError: If `selectors` or `marking` fail data validation. Also
if markings to remove are not found on the provided TLO.
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.
"""
selectors = utils.fix_value(selectors)
utils.validate(obj, selectors, marking)
selectors = utils.convert_to_list(selectors)
utils.validate(obj, selectors)
granular_markings = obj.get("granular_markings")
@ -90,14 +103,10 @@ def remove_markings(obj, selectors, marking):
else:
to_remove = [{"marking_ref": marking, "selectors": selectors}]
to_remove = utils.expand_markings(to_remove)
tlo = utils.build_granular_marking(to_remove)
remove = tlo.get("granular_markings", [])
remove = utils.build_granular_marking(to_remove).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...")
raise exceptions.MarkingNotFoundError(obj, remove)
granular_markings = [
m for m in granular_markings if m not in remove
@ -105,10 +114,10 @@ def remove_markings(obj, selectors, marking):
granular_markings = utils.compress_markings(granular_markings)
if not granular_markings:
return obj.new_version(granular_markings=None)
else:
if granular_markings:
return obj.new_version(granular_markings=granular_markings)
else:
return obj.new_version(granular_markings=None)
def add_markings(obj, selectors, marking):
@ -116,18 +125,21 @@ def add_markings(obj, selectors, marking):
Appends a granular_marking to the granular_markings collection.
Args:
obj: A TLO object.
obj: An SDO or SRO object.
selectors: list of type string, selectors must be relative to the TLO
in which the properties appear.
marking: identifier that apply to the properties selected by
`selectors`.
marking: identifier or list of marking identifiers that apply to the
properties selected by `selectors`.
Raises:
AssertionError: If `selectors` or `marking` fail data validation.
InvalidSelectorError: If `selectors` fail validation.
Returns:
A new version of the given SDO or SRO with specified markings added.
"""
selectors = utils.fix_value(selectors)
utils.validate(obj, selectors, marking)
selectors = utils.convert_to_list(selectors)
utils.validate(obj, selectors)
if isinstance(marking, list):
granular_marking = []
@ -149,16 +161,20 @@ def clear_markings(obj, selectors):
Removes all granular_markings 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).
obj: An SDO or SRO object.
selectors: string or list of selectors strings relative to the SDO or
SRO in which the properties appear.
Raises:
AssertionError: If `selectors` or `marking` fail data validation. Also
if markings to remove are not found on the provided TLO.
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.
"""
selectors = utils.fix_value(selectors)
selectors = utils.convert_to_list(selectors)
utils.validate(obj, selectors)
granular_markings = obj.get("granular_markings")
@ -179,8 +195,7 @@ def clear_markings(obj, selectors):
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...")
raise exceptions.MarkingNotFoundError(obj, clear)
for granular_marking in granular_markings:
for s in selectors:
@ -192,10 +207,10 @@ def clear_markings(obj, selectors):
granular_markings = utils.compress_markings(granular_markings)
if not granular_markings:
return obj.new_version(granular_markings=None)
else:
if granular_markings:
return obj.new_version(granular_markings=granular_markings)
else:
return obj.new_version(granular_markings=None)
def is_marked(obj, selectors, marking=None, inherited=False, descendants=False):
@ -203,27 +218,30 @@ 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).
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: identifier or list of marking identifiers that apply to the
field(s) selected by `selectors`.
properties 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.
Raises:
InvalidSelectorError: If `selectors` fail validation.
Returns:
bool: True if ``selectors`` is found on internal TLO collection.
bool: True if ``selectors`` is found on internal SDO or SRO collection.
False otherwise.
Note:
When a list of marking IDs is provided, if ANY of the provided marking
IDs matches, True is returned.
When a list of marking identifiers is provided, if ANY of the provided
marking identifiers match, True is returned.
"""
selectors = utils.fix_value(selectors)
marking = utils.fix_value(marking)
utils.validate(obj, selectors, marking)
selectors = utils.convert_to_list(selectors)
marking = utils.convert_to_list(marking)
utils.validate(obj, selectors)
granular_markings = obj.get("granular_markings", [])

View File

@ -1,4 +1,5 @@
from stix2 import exceptions
from stix2.markings import utils
@ -10,7 +11,7 @@ def get_markings(obj):
obj: A SDO or SRO object.
Returns:
list: Marking IDs contained in the SDO or SRO. Empty list if no
list: Marking identifiers contained in the SDO or SRO. Empty list if no
markings are present in `object_marking_refs`.
"""
@ -25,13 +26,12 @@ def add_markings(obj, marking):
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.
Returns:
A new version of the given SDO or SRO with specified markings added.
"""
marking = utils.convert_to_list(marking)
# TODO: Remove set for comparison and raise DuplicateMarkingException.
object_markings = set(obj.get("object_marking_refs", []) + marking)
return obj.new_version(object_marking_refs=list(object_markings))
@ -47,12 +47,14 @@ def remove_markings(obj, marking):
SDO or SRO object.
Raises:
AssertionError: If markings to remove are not found on the provided
SDO or SRO.
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.
"""
marking = utils.convert_to_list(marking)
utils.validate(obj, marking=marking)
object_markings = obj.get("object_marking_refs", [])
@ -60,8 +62,7 @@ def remove_markings(obj, marking):
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...")
raise exceptions.MarkingNotFoundError(obj, marking)
new_markings = [x for x in object_markings if x not in marking]
if new_markings:
@ -80,6 +81,10 @@ def set_markings(obj, marking):
marking: identifier or list of identifiers to apply in the
SDO or SRO object.
Returns:
A new version of the given SDO or SRO with specified markings removed
and new ones added.
"""
return add_markings(clear_markings(obj), marking)
@ -91,6 +96,9 @@ def clear_markings(obj):
Args:
obj: A SDO or SRO object.
Returns:
A new version of the given SDO or SRO with object_marking_refs cleared.
"""
return obj.new_version(object_marking_refs=None)

View File

@ -3,9 +3,23 @@ import collections
import six
from stix2 import exceptions
def evaluate_expression(obj, selector):
def _evaluate_expression(obj, selector):
"""
Walks an SDO or SRO generating selectors to match against ``selector``. If
a match is found and the the value of this property is present in the
objects. Matching value of the property will be returned.
Args:
obj: An SDO or SRO object.
selector: A string following the selector syntax.
Returns:
list: Values contained in matching property. Otherwise empty list.
"""
for items, value in iterpath(obj):
path = ".".join(items)
@ -15,45 +29,26 @@ def evaluate_expression(obj, selector):
return []
def validate_selector(obj, selector):
results = list(evaluate_expression(obj, selector))
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
def validate(obj, selectors):
"""Given an SDO or SRO, check that each selector is valid."""
if selectors:
for s in selectors:
assert validate_selector(obj, s)
if not _validate_selector(obj, s):
raise exceptions.InvalidSelectorError(obj, s)
return
if marking is not None:
assert validate_markings(marking)
raise exceptions.InvalidSelectorError(obj, selectors)
def convert_to_list(data):
"""Convert input into a list for further processing."""
if data is not None:
if isinstance(data, list):
return data
@ -61,14 +56,47 @@ def convert_to_list(data):
return [data]
def fix_value(data):
data = convert_to_list(data)
return data
def compress_markings(granular_markings):
"""
Compress granular markings list. If there is more than one marking
identifier matches. It will collapse into a single granular marking.
Examples:
Input:
[
{
"selectors": [
"description"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
},
{
"selectors": [
"name"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
}
]
Output:
[
{
"selectors": [
"description",
"name"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
}
]
Args:
granular_markings: The granular markings list property present in a
SDO or SRO.
Returns:
list: A list with all markings collapsed.
"""
if not granular_markings:
return
@ -88,10 +116,46 @@ def compress_markings(granular_markings):
def expand_markings(granular_markings):
"""
Expands granular markings list. If there is more than one selector per
granular marking. It will be expanded using the same marking_ref.
if not granular_markings:
return
Examples:
Input:
[
{
"selectors": [
"description",
"name"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
}
]
Output:
[
{
"selectors": [
"description"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
},
{
"selectors": [
"name"
],
"marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
}
]
Args:
granular_markings: The granular markings list property present in a
SDO or SRO.
Returns:
list: A list with all markings expanded.
"""
expanded = []
for marking in granular_markings:
@ -109,11 +173,9 @@ def expand_markings(granular_markings):
def build_granular_marking(granular_marking):
tlo = {"granular_markings": granular_marking}
expand_markings(tlo["granular_markings"])
return tlo
"""Returns a dictionary with the required structure for a granular
marking"""
return {"granular_markings": expand_markings(granular_marking)}
def iterpath(obj, path=None):
@ -122,7 +184,7 @@ def iterpath(obj, path=None):
tuple containing a list of ancestors and the property value.
Args:
obj: A TLO object.
obj: An SDO or SRO object.
path: None, used recursively to store ancestors.
Example:
@ -164,36 +226,3 @@ def iterpath(obj, path=None):
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(obj, obj["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

View File

@ -102,7 +102,7 @@ def test_marking_def_invalid_type():
stix2.MarkingDefinition(
id="marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9",
created="2017-01-20T00:00:00.000Z",
definition_type="my-definiition-type",
definition_type="my-definition-type",
definition=stix2.StatementMarking("Copyright 2016, Example Corp")
)