Fix typos, add to Property class documentation, small performance

boosts, and let strings and booleans in a ListProperty be handled by
__call__().
stix2.1
clenk 2017-04-24 16:33:59 -04:00
parent 76acd8c0c2
commit fe4c4d78fc
3 changed files with 34 additions and 29 deletions

View File

@ -13,35 +13,36 @@ from .utils import get_dict
from . import exceptions from . import exceptions
OBJ_MAP = {
'attack-pattern': AttackPattern,
'campaign': Campaign,
'course-of-action': CourseOfAction,
'identity': Identity,
'indicator': Indicator,
'intrusion-set': IntrusionSet,
'malware': Malware,
'marking-definition': MarkingDefinition,
'observed-data': ObservedData,
'report': Report,
'relationship': Relationship,
'threat-actor': ThreatActor,
'tool': Tool,
'sighting': Sighting,
'vulnerability': Vulnerability,
}
def parse(data): def parse(data):
"""Deserialize a string or file-like object into a STIX object""" """Deserialize a string or file-like object into a STIX object"""
obj = get_dict(data) obj = get_dict(data)
obj_map = {
'attack-pattern': AttackPattern,
'campaign': Campaign,
'course-of-action': CourseOfAction,
'identity': Identity,
'indicator': Indicator,
'intrusion-set': IntrusionSet,
'malware': Malware,
'marking-definition': MarkingDefinition,
'observed-data': ObservedData,
'report': Report,
'relationship': Relationship,
'threat-actor': ThreatActor,
'tool': Tool,
'sighting': Sighting,
'vulnerability': Vulnerability,
}
if 'type' not in obj: if 'type' not in obj:
# TODO parse external references, kill chain phases, and granular markings # TODO parse external references, kill chain phases, and granular markings
pass pass
else: else:
try: try:
obj_class = obj_map[obj['type']] obj_class = OBJ_MAP[obj['type']]
return obj_class(**obj) return obj_class(**obj)
except KeyError: except KeyError:
# TODO handle custom objects # TODO handle custom objects

View File

@ -39,7 +39,7 @@ class TLPMarking(_STIXBase):
class StatementMarking(_STIXBase): class StatementMarking(_STIXBase):
_properties = { _properties = {
'statement': Property(required=True) 'statement': StringProperty(required=True)
} }
def __init__(self, statement=None, **kwargs): def __init__(self, statement=None, **kwargs):

View File

@ -42,9 +42,15 @@ class Property(object):
to use the same default value, so calling now() for each field-- to use the same default value, so calling now() for each field--
likely several microseconds apart-- does not work. likely several microseconds apart-- does not work.
Subclasses can instead provide a lambda function for `default as a keyword Subclasses can instead provide a lambda function for `default` as a keyword
argument. `clean` should not be provided as a lambda since lambdas cannot argument. `clean` should not be provided as a lambda since lambdas cannot
raise their own exceptions. raise their own exceptions.
When instantiating Properties, `required` and `default` should not be used
together. `default` implies that the field is required in the specification
so this function will be used to supply a value if none is provided.
`required` means that the user must provide this; it is required in the
specification and we can't or don't want to create a default value.
""" """
def _default_clean(self, value): def _default_clean(self, value):
@ -66,8 +72,10 @@ class Property(object):
return value return value
def __call__(self, value=None): def __call__(self, value=None):
if value is not None: """Used by ListProperty to handle lists that have been defined with
return value either a class or an instance.
"""
return value
class ListProperty(Property): class ListProperty(Property):
@ -76,11 +84,7 @@ class ListProperty(Property):
""" """
Contained should be a function which returns an object from the value. Contained should be a function which returns an object from the value.
""" """
if contained == StringProperty: if inspect.isclass(contained) and issubclass(contained, Property):
self.contained = StringProperty().string_type
elif contained == BooleanProperty:
self.contained = bool
elif inspect.isclass(contained) and issubclass(contained, Property):
# If it's a class and not an instance, instantiate it so that # If it's a class and not an instance, instantiate it so that
# clean() can be called on it, and ListProperty.clean() will # clean() can be called on it, and ListProperty.clean() will
# use __call__ when it appends the item. # use __call__ when it appends the item.
@ -191,7 +195,7 @@ class TimestampProperty(Property):
return value return value
else: else:
# Add a time component # Add a time component
return dt.datetime.combine(value, dt.time(), tzinfo=pytz.timezone('US/Eastern')) return dt.datetime.combine(value, dt.time(), tzinfo=pytz.utc)
# value isn't a date or datetime object so assume it's a string # value isn't a date or datetime object so assume it's a string
try: try: