commit
9cc74e88b6
|
@ -56,7 +56,7 @@ class STIXJSONIncludeOptionalDefaultsEncoder(json.JSONEncoder):
|
||||||
elif isinstance(obj, _STIXBase):
|
elif isinstance(obj, _STIXBase):
|
||||||
return dict(obj)
|
return dict(obj)
|
||||||
else:
|
else:
|
||||||
return super(STIXJSONEncoder, self).default(obj)
|
return super(STIXJSONIncludeOptionalDefaultsEncoder, self).default(obj)
|
||||||
|
|
||||||
|
|
||||||
def get_required_properties(properties):
|
def get_required_properties(properties):
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pkgutil
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
|
|
||||||
from . import exceptions
|
from .exceptions import ParseError
|
||||||
from .utils import _get_dict
|
from .utils import _get_dict
|
||||||
|
|
||||||
STIX2_OBJ_MAPS = {}
|
STIX2_OBJ_MAPS = {}
|
||||||
|
@ -74,7 +74,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if 'type' not in stix_dict:
|
if 'type' not in stix_dict:
|
||||||
raise exceptions.ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict))
|
raise ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict))
|
||||||
|
|
||||||
if "spec_version" in stix_dict:
|
if "spec_version" in stix_dict:
|
||||||
# For STIX 2.0, applies to bundles only.
|
# For STIX 2.0, applies to bundles only.
|
||||||
|
@ -87,7 +87,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
|
||||||
else:
|
else:
|
||||||
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||||
else:
|
else:
|
||||||
v = 'v20'
|
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||||
|
|
||||||
OBJ_MAP = STIX2_OBJ_MAPS[v]
|
OBJ_MAP = STIX2_OBJ_MAPS[v]
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
|
||||||
# flag allows for unknown custom objects too, but will not
|
# flag allows for unknown custom objects too, but will not
|
||||||
# be parsed into STIX object, returned as is
|
# be parsed into STIX object, returned as is
|
||||||
return stix_dict
|
return stix_dict
|
||||||
raise exceptions.ParseError("Can't parse unknown object type '%s'! For custom types, use the CustomObject decorator." % stix_dict['type'])
|
raise ParseError("Can't parse unknown object type '%s'! For custom types, use the CustomObject decorator." % stix_dict['type'])
|
||||||
|
|
||||||
return obj_class(allow_custom=allow_custom, **stix_dict)
|
return obj_class(allow_custom=allow_custom, **stix_dict)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ import json
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
import stix2.v20.sdo
|
|
||||||
import stix2.v21.bundle
|
import stix2.v21.bundle
|
||||||
|
import stix2.v21.sdo
|
||||||
|
|
||||||
EXPECTED_BUNDLE = """{
|
EXPECTED_BUNDLE = """{
|
||||||
"type": "bundle",
|
"type": "bundle",
|
||||||
|
@ -179,8 +179,7 @@ def test_parse_bundle(version):
|
||||||
|
|
||||||
assert bundle.type == "bundle"
|
assert bundle.type == "bundle"
|
||||||
assert bundle.id.startswith("bundle--")
|
assert bundle.id.startswith("bundle--")
|
||||||
# TODO: update this to a STIX 2.1 indicator
|
assert type(bundle.objects[0]) is stix2.v21.sdo.Indicator
|
||||||
assert type(bundle.objects[0]) is stix2.v20.sdo.Indicator
|
|
||||||
assert bundle.objects[0].type == 'indicator'
|
assert bundle.objects[0].type == 'indicator'
|
||||||
assert bundle.objects[1].type == 'malware'
|
assert bundle.objects[1].type == 'malware'
|
||||||
assert bundle.objects[2].type == 'relationship'
|
assert bundle.objects[2].type == 'relationship'
|
||||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
import stix2.base
|
import stix2.base
|
||||||
import stix2.v20.sdo
|
import stix2.v21.sdo
|
||||||
|
|
||||||
from .constants import FAKE_TIME, MARKING_DEFINITION_ID
|
from .constants import FAKE_TIME, MARKING_DEFINITION_ID
|
||||||
|
|
||||||
|
@ -95,8 +95,7 @@ def test_identity_custom_property_allowed():
|
||||||
def test_parse_identity_custom_property(data):
|
def test_parse_identity_custom_property(data):
|
||||||
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
|
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
|
||||||
identity = stix2.parse(data)
|
identity = stix2.parse(data)
|
||||||
# TODO: update to create and check a STIX 2.1 Identity object
|
assert excinfo.value.cls == stix2.v21.sdo.Identity
|
||||||
assert excinfo.value.cls == stix2.v20.sdo.Identity
|
|
||||||
assert excinfo.value.properties == ['foo']
|
assert excinfo.value.properties == ['foo']
|
||||||
assert "Unexpected properties for" in str(excinfo.value)
|
assert "Unexpected properties for" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
|
||||||
import pytest
|
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
|
@ -21,7 +20,7 @@ TEST_CAMPAIGN = """{
|
||||||
"description": "More information about bank attack"
|
"description": "More information about bank attack"
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
TEST_LANGUAGE_CONTENT = """{
|
TEST_LANGUAGE_CONTENT = u"""{
|
||||||
"type": "language-content",
|
"type": "language-content",
|
||||||
"id": "language-content--b86bd89f-98bb-4fa9-8cb2-9ad421da981d",
|
"id": "language-content--b86bd89f-98bb-4fa9-8cb2-9ad421da981d",
|
||||||
"created": "2017-02-08T21:31:22.007Z",
|
"created": "2017-02-08T21:31:22.007Z",
|
||||||
|
@ -30,18 +29,17 @@ TEST_LANGUAGE_CONTENT = """{
|
||||||
"object_modified": "2017-02-08T21:31:22.007Z",
|
"object_modified": "2017-02-08T21:31:22.007Z",
|
||||||
"contents": {
|
"contents": {
|
||||||
"de": {
|
"de": {
|
||||||
"name": "Bank Angriff 1",
|
"description": "Weitere Informationen über Banküberfall",
|
||||||
"description": "Weitere Informationen über Banküberfall"
|
"name": "Bank Angriff 1"
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
"name": "Attaque Bank 1",
|
"description": "Plus d'informations sur la crise bancaire",
|
||||||
"description": "Plus d'informations sur la crise bancaire"
|
"name": "Attaque Bank 1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="Dictionary keys are too short")
|
|
||||||
def test_language_content_campaign():
|
def test_language_content_campaign():
|
||||||
now = dt.datetime(2017, 2, 8, 21, 31, 22, microsecond=7000, tzinfo=pytz.utc)
|
now = dt.datetime(2017, 2, 8, 21, 31, 22, microsecond=7000, tzinfo=pytz.utc)
|
||||||
|
|
||||||
|
@ -66,5 +64,8 @@ def test_language_content_campaign():
|
||||||
|
|
||||||
camp = stix2.parse(TEST_CAMPAIGN)
|
camp = stix2.parse(TEST_CAMPAIGN)
|
||||||
|
|
||||||
assert str(lc) in TEST_LANGUAGE_CONTENT
|
# In order to provide the same representation, we need to disable escaping
|
||||||
|
# in json.dumps(). https://docs.python.org/3/library/json.html#json.dumps
|
||||||
|
# or https://docs.python.org/2/library/json.html#json.dumps
|
||||||
|
assert lc.serialize(pretty=True, ensure_ascii=False) == TEST_LANGUAGE_CONTENT
|
||||||
assert lc.modified == camp.modified
|
assert lc.modified == camp.modified
|
||||||
|
|
|
@ -2,12 +2,13 @@ import pytest
|
||||||
|
|
||||||
from stix2 import CustomObject, EmailMIMEComponent, ExtensionsProperty, TCPExt
|
from stix2 import CustomObject, EmailMIMEComponent, ExtensionsProperty, TCPExt
|
||||||
from stix2.exceptions import AtLeastOnePropertyError, DictionaryKeyError
|
from stix2.exceptions import AtLeastOnePropertyError, DictionaryKeyError
|
||||||
from stix2.properties import (BinaryProperty, BooleanProperty,
|
from stix2.v20.properties import (BinaryProperty, BooleanProperty,
|
||||||
DictionaryProperty, EmbeddedObjectProperty,
|
DictionaryProperty, EmbeddedObjectProperty,
|
||||||
EnumProperty, FloatProperty, HashesProperty,
|
EnumProperty, FloatProperty, HashesProperty,
|
||||||
HexProperty, IDProperty, IntegerProperty,
|
HexProperty, IDProperty, IntegerProperty,
|
||||||
ListProperty, Property, ReferenceProperty,
|
ListProperty, Property, ReferenceProperty,
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
StringProperty, TimestampProperty,
|
||||||
|
TypeProperty)
|
||||||
|
|
||||||
from .constants import FAKE_TIME
|
from .constants import FAKE_TIME
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from stix2 import parse
|
from ..base import _STIXBase
|
||||||
from stix2.base import _STIXBase
|
from ..core import parse
|
||||||
from stix2.properties import (IDProperty, ListProperty, Property,
|
from ..utils import _get_dict, get_class_hierarchy_names
|
||||||
StringProperty, TypeProperty)
|
from .properties import (IDProperty, ListProperty, Property, StringProperty,
|
||||||
from stix2.utils import _get_dict, get_class_hierarchy_names
|
TypeProperty)
|
||||||
|
|
||||||
|
|
||||||
class STIXObjectProperty(Property):
|
class STIXObjectProperty(Property):
|
||||||
|
|
|
@ -4,10 +4,10 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (HashesProperty, IDProperty, ListProperty, Property,
|
from ..utils import NOW, _get_dict
|
||||||
|
from .properties import (HashesProperty, IDProperty, ListProperty, Property,
|
||||||
ReferenceProperty, SelectorProperty, StringProperty,
|
ReferenceProperty, SelectorProperty, StringProperty,
|
||||||
TimestampProperty, TypeProperty)
|
TimestampProperty, TypeProperty)
|
||||||
from ..utils import NOW, _get_dict
|
|
||||||
|
|
||||||
|
|
||||||
class ExternalReference(_STIXBase):
|
class ExternalReference(_STIXBase):
|
||||||
|
|
|
@ -12,12 +12,12 @@ import re
|
||||||
from ..base import _Extension, _Observable, _STIXBase
|
from ..base import _Extension, _Observable, _STIXBase
|
||||||
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
|
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
|
||||||
DependentPropertiesError, ParseError)
|
DependentPropertiesError, ParseError)
|
||||||
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
from ..utils import TYPE_REGEX, _get_dict
|
||||||
|
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
||||||
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
||||||
HashesProperty, HexProperty, IntegerProperty,
|
HashesProperty, HexProperty, IntegerProperty,
|
||||||
ListProperty, ObjectReferenceProperty, Property,
|
ListProperty, ObjectReferenceProperty, Property,
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
StringProperty, TimestampProperty, TypeProperty)
|
||||||
from ..utils import TYPE_REGEX, _get_dict
|
|
||||||
|
|
||||||
|
|
||||||
class ObservableProperty(Property):
|
class ObservableProperty(Property):
|
||||||
|
|
|
@ -10,9 +10,9 @@ import uuid
|
||||||
from six import string_types, text_type
|
from six import string_types, text_type
|
||||||
from stix2patterns.validator import run_validator
|
from stix2patterns.validator import run_validator
|
||||||
|
|
||||||
from .base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from .exceptions import DictionaryKeyError
|
from ..exceptions import DictionaryKeyError
|
||||||
from .utils import _get_dict, parse_into_datetime
|
from ..utils import _get_dict, parse_into_datetime
|
||||||
|
|
||||||
|
|
||||||
class Property(object):
|
class Property(object):
|
|
@ -4,16 +4,15 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import stix2
|
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
|
from ..core import _register_type
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
|
|
||||||
ListProperty, PatternProperty, ReferenceProperty,
|
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
|
||||||
from ..utils import NOW, TYPE_REGEX
|
from ..utils import NOW, TYPE_REGEX
|
||||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||||
from .observables import ObservableProperty
|
from .observables import ObservableProperty
|
||||||
|
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
|
||||||
|
ListProperty, PatternProperty, ReferenceProperty,
|
||||||
|
StringProperty, TimestampProperty, TypeProperty)
|
||||||
|
|
||||||
|
|
||||||
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
||||||
|
@ -409,7 +408,7 @@ def CustomObject(type='x-custom-type', properties=None):
|
||||||
return
|
return
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
stix2._register_type(_Custom, version="2.0")
|
_register_type(_Custom, version="2.0")
|
||||||
return _Custom
|
return _Custom
|
||||||
|
|
||||||
return custom_builder
|
return custom_builder
|
||||||
|
|
|
@ -4,11 +4,11 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
|
|
||||||
ListProperty, ReferenceProperty, StringProperty,
|
|
||||||
TimestampProperty, TypeProperty)
|
|
||||||
from ..utils import NOW
|
from ..utils import NOW
|
||||||
from .common import ExternalReference, GranularMarking
|
from .common import ExternalReference, GranularMarking
|
||||||
|
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
|
||||||
|
ListProperty, ReferenceProperty, StringProperty,
|
||||||
|
TimestampProperty, TypeProperty)
|
||||||
|
|
||||||
|
|
||||||
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from stix2 import parse
|
from ..base import _STIXBase
|
||||||
from stix2.base import _STIXBase
|
from ..core import parse
|
||||||
from stix2.properties import IDProperty, ListProperty, Property, TypeProperty
|
from ..utils import _get_dict, get_class_hierarchy_names
|
||||||
from stix2.utils import _get_dict, get_class_hierarchy_names
|
from .properties import IDProperty, ListProperty, Property, TypeProperty
|
||||||
|
|
||||||
|
|
||||||
class STIXObjectProperty(Property):
|
class STIXObjectProperty(Property):
|
||||||
|
|
|
@ -4,11 +4,11 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (BooleanProperty, DictionaryProperty, HashesProperty,
|
|
||||||
IDProperty, ListProperty, Property,
|
|
||||||
ReferenceProperty, SelectorProperty, StringProperty,
|
|
||||||
TimestampProperty, TypeProperty)
|
|
||||||
from ..utils import NOW, _get_dict
|
from ..utils import NOW, _get_dict
|
||||||
|
from .properties import (BooleanProperty, DictionaryProperty, HashesProperty,
|
||||||
|
IDProperty, ListProperty, Property, ReferenceProperty,
|
||||||
|
SelectorProperty, StringProperty, TimestampProperty,
|
||||||
|
TypeProperty)
|
||||||
|
|
||||||
|
|
||||||
class ExternalReference(_STIXBase):
|
class ExternalReference(_STIXBase):
|
||||||
|
|
|
@ -12,12 +12,12 @@ import re
|
||||||
from ..base import _Extension, _Observable, _STIXBase
|
from ..base import _Extension, _Observable, _STIXBase
|
||||||
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
|
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
|
||||||
DependentPropertiesError, ParseError)
|
DependentPropertiesError, ParseError)
|
||||||
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
from ..utils import TYPE_REGEX, _get_dict
|
||||||
|
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
||||||
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
||||||
HashesProperty, HexProperty, IntegerProperty,
|
HashesProperty, HexProperty, IntegerProperty,
|
||||||
ListProperty, ObjectReferenceProperty, Property,
|
ListProperty, ObjectReferenceProperty, Property,
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
StringProperty, TimestampProperty, TypeProperty)
|
||||||
from ..utils import TYPE_REGEX, _get_dict
|
|
||||||
|
|
||||||
|
|
||||||
class ObservableProperty(Property):
|
class ObservableProperty(Property):
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
"""Classes for representing properties of STIX Objects and Cyber Observables.
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import collections
|
||||||
|
import inspect
|
||||||
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from six import string_types, text_type
|
||||||
|
from stix2patterns.validator import run_validator
|
||||||
|
|
||||||
|
from ..base import _STIXBase
|
||||||
|
from ..exceptions import DictionaryKeyError
|
||||||
|
from ..utils import _get_dict, parse_into_datetime
|
||||||
|
|
||||||
|
|
||||||
|
class Property(object):
|
||||||
|
"""Represent a property of STIX data type.
|
||||||
|
|
||||||
|
Subclasses can define the following attributes as keyword arguments to
|
||||||
|
``__init__()``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
required (bool): If ``True``, the property must be provided when creating an
|
||||||
|
object with that property. No default value exists for these properties.
|
||||||
|
(Default: ``False``)
|
||||||
|
fixed: This provides a constant default value. Users are free to
|
||||||
|
provide this value explicity when constructing an object (which allows
|
||||||
|
you to copy **all** values from an existing object to a new object), but
|
||||||
|
if the user provides a value other than the ``fixed`` value, it will raise
|
||||||
|
an error. This is semantically equivalent to defining both:
|
||||||
|
|
||||||
|
- a ``clean()`` function that checks if the value matches the fixed
|
||||||
|
value, and
|
||||||
|
- a ``default()`` function that returns the fixed value.
|
||||||
|
|
||||||
|
Subclasses can also define the following functions:
|
||||||
|
|
||||||
|
- ``def clean(self, value) -> any:``
|
||||||
|
- Return a value that is valid for this property. If ``value`` is not
|
||||||
|
valid for this property, this will attempt to transform it first. If
|
||||||
|
``value`` is not valid and no such transformation is possible, it should
|
||||||
|
raise a ValueError.
|
||||||
|
- ``def default(self):``
|
||||||
|
- provide a default value for this property.
|
||||||
|
- ``default()`` can return the special value ``NOW`` to use the current
|
||||||
|
time. This is useful when several timestamps in the same object need
|
||||||
|
to use the same default value, so calling now() for each property--
|
||||||
|
likely several microseconds apart-- does not work.
|
||||||
|
|
||||||
|
Subclasses can instead provide a lambda function for ``default`` as a keyword
|
||||||
|
argument. ``clean`` should not be provided as a lambda since lambdas cannot
|
||||||
|
raise their own exceptions.
|
||||||
|
|
||||||
|
When instantiating Properties, ``required`` and ``default`` should not be used
|
||||||
|
together. ``default`` implies that the property 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):
|
||||||
|
if value != self._fixed_value:
|
||||||
|
raise ValueError("must equal '{0}'.".format(self._fixed_value))
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, required=False, fixed=None, default=None, type=None):
|
||||||
|
self.required = required
|
||||||
|
self.type = type
|
||||||
|
if fixed:
|
||||||
|
self._fixed_value = fixed
|
||||||
|
self.clean = self._default_clean
|
||||||
|
self.default = lambda: fixed
|
||||||
|
if default:
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __call__(self, value=None):
|
||||||
|
"""Used by ListProperty to handle lists that have been defined with
|
||||||
|
either a class or an instance.
|
||||||
|
"""
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ListProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, contained, **kwargs):
|
||||||
|
"""
|
||||||
|
``contained`` should be a function which returns an object from the value.
|
||||||
|
"""
|
||||||
|
if inspect.isclass(contained) and issubclass(contained, Property):
|
||||||
|
# If it's a class and not an instance, instantiate it so that
|
||||||
|
# clean() can be called on it, and ListProperty.clean() will
|
||||||
|
# use __call__ when it appends the item.
|
||||||
|
self.contained = contained()
|
||||||
|
else:
|
||||||
|
self.contained = contained
|
||||||
|
super(ListProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
try:
|
||||||
|
iter(value)
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("must be an iterable.")
|
||||||
|
|
||||||
|
if isinstance(value, (_STIXBase, string_types)):
|
||||||
|
value = [value]
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for item in value:
|
||||||
|
try:
|
||||||
|
valid = self.contained.clean(item)
|
||||||
|
except ValueError:
|
||||||
|
raise
|
||||||
|
except AttributeError:
|
||||||
|
# type of list has no clean() function (eg. built in Python types)
|
||||||
|
# TODO Should we raise an error here?
|
||||||
|
valid = item
|
||||||
|
|
||||||
|
if type(self.contained) is EmbeddedObjectProperty:
|
||||||
|
obj_type = self.contained.type
|
||||||
|
elif type(self.contained).__name__ is 'STIXObjectProperty':
|
||||||
|
# ^ this way of checking doesn't require a circular import
|
||||||
|
# valid is already an instance of a python-stix2 class; no need
|
||||||
|
# to turn it into a dictionary and then pass it to the class
|
||||||
|
# constructor again
|
||||||
|
result.append(valid)
|
||||||
|
continue
|
||||||
|
elif type(self.contained) is DictionaryProperty:
|
||||||
|
obj_type = dict
|
||||||
|
else:
|
||||||
|
obj_type = self.contained
|
||||||
|
|
||||||
|
if isinstance(valid, collections.Mapping):
|
||||||
|
result.append(obj_type(**valid))
|
||||||
|
else:
|
||||||
|
result.append(obj_type(valid))
|
||||||
|
|
||||||
|
# STIX spec forbids empty lists
|
||||||
|
if len(result) < 1:
|
||||||
|
raise ValueError("must not be empty.")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class StringProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.string_type = text_type
|
||||||
|
super(StringProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
return self.string_type(value)
|
||||||
|
|
||||||
|
|
||||||
|
class TypeProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, type):
|
||||||
|
super(TypeProperty, self).__init__(fixed=type)
|
||||||
|
|
||||||
|
|
||||||
|
class IDProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, type):
|
||||||
|
self.required_prefix = type + "--"
|
||||||
|
super(IDProperty, self).__init__()
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if not value.startswith(self.required_prefix):
|
||||||
|
raise ValueError("must start with '{0}'.".format(self.required_prefix))
|
||||||
|
try:
|
||||||
|
uuid.UUID(value.split('--', 1)[1])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError("must have a valid UUID after the prefix.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
def default(self):
|
||||||
|
return self.required_prefix + str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
class IntegerProperty(Property):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except Exception:
|
||||||
|
raise ValueError("must be an integer.")
|
||||||
|
|
||||||
|
|
||||||
|
class FloatProperty(Property):
|
||||||
|
def clean(self, value):
|
||||||
|
try:
|
||||||
|
return float(value)
|
||||||
|
except Exception:
|
||||||
|
raise ValueError("must be a float.")
|
||||||
|
|
||||||
|
|
||||||
|
class BooleanProperty(Property):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
|
||||||
|
trues = ['true', 't']
|
||||||
|
falses = ['false', 'f']
|
||||||
|
try:
|
||||||
|
if value.lower() in trues:
|
||||||
|
return True
|
||||||
|
if value.lower() in falses:
|
||||||
|
return False
|
||||||
|
except AttributeError:
|
||||||
|
if value == 1:
|
||||||
|
return True
|
||||||
|
if value == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
raise ValueError("must be a boolean value.")
|
||||||
|
|
||||||
|
|
||||||
|
class TimestampProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, precision=None, **kwargs):
|
||||||
|
self.precision = precision
|
||||||
|
super(TimestampProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
return parse_into_datetime(value, self.precision)
|
||||||
|
|
||||||
|
|
||||||
|
class DictionaryProperty(Property):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
try:
|
||||||
|
dictified = _get_dict(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("The dictionary property must contain a dictionary")
|
||||||
|
if dictified == {}:
|
||||||
|
raise ValueError("The dictionary property must contain a non-empty dictionary")
|
||||||
|
|
||||||
|
for k in dictified.keys():
|
||||||
|
if len(k) > 250:
|
||||||
|
raise DictionaryKeyError(k, "longer than 250 characters")
|
||||||
|
if not re.match('^[a-zA-Z0-9_-]+$', k):
|
||||||
|
raise DictionaryKeyError(k, "contains characters other than"
|
||||||
|
"lowercase a-z, uppercase A-Z, "
|
||||||
|
"numerals 0-9, hyphen (-), or "
|
||||||
|
"underscore (_)")
|
||||||
|
return dictified
|
||||||
|
|
||||||
|
|
||||||
|
HASHES_REGEX = {
|
||||||
|
"MD5": ("^[a-fA-F0-9]{32}$", "MD5"),
|
||||||
|
"MD6": ("^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"),
|
||||||
|
"RIPEMD160": ("^[a-fA-F0-9]{40}$", "RIPEMD-160"),
|
||||||
|
"SHA1": ("^[a-fA-F0-9]{40}$", "SHA-1"),
|
||||||
|
"SHA224": ("^[a-fA-F0-9]{56}$", "SHA-224"),
|
||||||
|
"SHA256": ("^[a-fA-F0-9]{64}$", "SHA-256"),
|
||||||
|
"SHA384": ("^[a-fA-F0-9]{96}$", "SHA-384"),
|
||||||
|
"SHA512": ("^[a-fA-F0-9]{128}$", "SHA-512"),
|
||||||
|
"SHA3224": ("^[a-fA-F0-9]{56}$", "SHA3-224"),
|
||||||
|
"SHA3256": ("^[a-fA-F0-9]{64}$", "SHA3-256"),
|
||||||
|
"SHA3384": ("^[a-fA-F0-9]{96}$", "SHA3-384"),
|
||||||
|
"SHA3512": ("^[a-fA-F0-9]{128}$", "SHA3-512"),
|
||||||
|
"SSDEEP": ("^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"),
|
||||||
|
"WHIRLPOOL": ("^[a-fA-F0-9]{128}$", "WHIRLPOOL"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HashesProperty(DictionaryProperty):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
clean_dict = super(HashesProperty, self).clean(value)
|
||||||
|
for k, v in clean_dict.items():
|
||||||
|
key = k.upper().replace('-', '')
|
||||||
|
if key in HASHES_REGEX:
|
||||||
|
vocab_key = HASHES_REGEX[key][1]
|
||||||
|
if not re.match(HASHES_REGEX[key][0], v):
|
||||||
|
raise ValueError("'%s' is not a valid %s hash" % (v, vocab_key))
|
||||||
|
if k != vocab_key:
|
||||||
|
clean_dict[vocab_key] = clean_dict[k]
|
||||||
|
del clean_dict[k]
|
||||||
|
return clean_dict
|
||||||
|
|
||||||
|
|
||||||
|
class BinaryProperty(Property):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
try:
|
||||||
|
base64.b64decode(value)
|
||||||
|
except (binascii.Error, TypeError):
|
||||||
|
raise ValueError("must contain a base64 encoded string")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class HexProperty(Property):
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if not re.match('^([a-fA-F0-9]{2})+$', value):
|
||||||
|
raise ValueError("must contain an even number of hexadecimal characters")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
REF_REGEX = re.compile("^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}"
|
||||||
|
"-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
||||||
|
|
||||||
|
|
||||||
|
class ReferenceProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, required=False, type=None):
|
||||||
|
"""
|
||||||
|
references sometimes must be to a specific object type
|
||||||
|
"""
|
||||||
|
self.type = type
|
||||||
|
super(ReferenceProperty, self).__init__(required, type=type)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if isinstance(value, _STIXBase):
|
||||||
|
value = value.id
|
||||||
|
value = str(value)
|
||||||
|
if self.type:
|
||||||
|
if not value.startswith(self.type):
|
||||||
|
raise ValueError("must start with '{0}'.".format(self.type))
|
||||||
|
if not REF_REGEX.match(value):
|
||||||
|
raise ValueError("must match <object-type>--<guid>.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
SELECTOR_REGEX = re.compile("^[a-z0-9_-]{3,250}(\\.(\\[\\d+\\]|[a-z0-9_-]{1,250}))*$")
|
||||||
|
|
||||||
|
|
||||||
|
class SelectorProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, type=None):
|
||||||
|
# ignore type
|
||||||
|
super(SelectorProperty, self).__init__()
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if not SELECTOR_REGEX.match(value):
|
||||||
|
raise ValueError("must adhere to selector syntax.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectReferenceProperty(StringProperty):
|
||||||
|
|
||||||
|
def __init__(self, valid_types=None, **kwargs):
|
||||||
|
if valid_types and type(valid_types) is not list:
|
||||||
|
valid_types = [valid_types]
|
||||||
|
self.valid_types = valid_types
|
||||||
|
super(ObjectReferenceProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EmbeddedObjectProperty(Property):
|
||||||
|
|
||||||
|
def __init__(self, type, required=False):
|
||||||
|
self.type = type
|
||||||
|
super(EmbeddedObjectProperty, self).__init__(required, type=type)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if type(value) is dict:
|
||||||
|
value = self.type(**value)
|
||||||
|
elif not isinstance(value, self.type):
|
||||||
|
raise ValueError("must be of type %s." % self.type.__name__)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class EnumProperty(StringProperty):
|
||||||
|
|
||||||
|
def __init__(self, allowed, **kwargs):
|
||||||
|
if type(allowed) is not list:
|
||||||
|
allowed = list(allowed)
|
||||||
|
self.allowed = allowed
|
||||||
|
super(EnumProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
value = super(EnumProperty, self).clean(value)
|
||||||
|
if value not in self.allowed:
|
||||||
|
raise ValueError("value '%s' is not valid for this enumeration." % value)
|
||||||
|
return self.string_type(value)
|
||||||
|
|
||||||
|
|
||||||
|
class PatternProperty(StringProperty):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(PatternProperty, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
str_value = super(PatternProperty, self).clean(value)
|
||||||
|
errors = run_validator(str_value)
|
||||||
|
if errors:
|
||||||
|
raise ValueError(str(errors[0]))
|
||||||
|
|
||||||
|
return self.string_type(value)
|
|
@ -3,18 +3,17 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import stix2
|
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
|
from ..core import _register_type
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (BooleanProperty, DictionaryProperty,
|
from ..utils import NOW, TYPE_REGEX
|
||||||
|
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||||
|
from .observables import ObservableProperty
|
||||||
|
from .properties import (BooleanProperty, DictionaryProperty,
|
||||||
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
||||||
IDProperty, IntegerProperty, ListProperty,
|
IDProperty, IntegerProperty, ListProperty,
|
||||||
PatternProperty, ReferenceProperty, StringProperty,
|
PatternProperty, ReferenceProperty, StringProperty,
|
||||||
TimestampProperty, TypeProperty)
|
TimestampProperty, TypeProperty)
|
||||||
from ..utils import NOW, TYPE_REGEX
|
|
||||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
|
||||||
from .observables import ObservableProperty
|
|
||||||
|
|
||||||
|
|
||||||
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
||||||
|
@ -585,7 +584,7 @@ def CustomObject(type='x-custom-type', properties=None):
|
||||||
return
|
return
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
stix2._register_type(_Custom, version="2.1")
|
_register_type(_Custom, version="2.1")
|
||||||
return _Custom
|
return _Custom
|
||||||
|
|
||||||
return custom_builder
|
return custom_builder
|
||||||
|
|
|
@ -4,11 +4,11 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from ..base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from ..markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
|
|
||||||
ListProperty, ReferenceProperty, StringProperty,
|
|
||||||
TimestampProperty, TypeProperty)
|
|
||||||
from ..utils import NOW
|
from ..utils import NOW
|
||||||
from .common import ExternalReference, GranularMarking
|
from .common import ExternalReference, GranularMarking
|
||||||
|
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
|
||||||
|
ListProperty, ReferenceProperty, StringProperty,
|
||||||
|
TimestampProperty, TypeProperty)
|
||||||
|
|
||||||
|
|
||||||
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
||||||
|
|
Loading…
Reference in New Issue