Merge branch 'parse-cyber-observables' into cyber-observables

stix2.1
Rich Piazza 2017-05-11 15:29:15 -04:00 committed by GitHub
commit ae5fb51564
9 changed files with 106 additions and 57 deletions

7
.isort.cfg Normal file
View File

@ -0,0 +1,7 @@
[settings]
check=1
diff=1
known_third_party=dateutil,pytest,pytz,six
known_first_party=stix2
not_skip=__init__.py
force_sort_within_sections=1

View File

@ -179,7 +179,7 @@ class _STIXBase(collections.Mapping):
return self.new_version(revoked=True) return self.new_version(revoked=True)
class Observable(_STIXBase): class _Observable(_STIXBase):
def __init__(self, **kwargs): def __init__(self, **kwargs):
# the constructor might be called independently of an observed data object # the constructor might be called independently of an observed data object
@ -187,10 +187,10 @@ class Observable(_STIXBase):
self._STIXBase__valid_refs = kwargs.pop('_valid_refs') self._STIXBase__valid_refs = kwargs.pop('_valid_refs')
else: else:
self._STIXBase__valid_refs = [] self._STIXBase__valid_refs = []
super(Observable, self).__init__(**kwargs) super(_Observable, self).__init__(**kwargs)
def _check_property(self, prop_name, prop, kwargs): def _check_property(self, prop_name, prop, kwargs):
super(Observable, self)._check_property(prop_name, prop, kwargs) super(_Observable, self)._check_property(prop_name, prop, kwargs)
if prop_name.endswith('_ref') and prop_name in kwargs: if prop_name.endswith('_ref') and prop_name in kwargs:
ref = kwargs[prop_name] ref = kwargs[prop_name]
if ref not in self._STIXBase__valid_refs: if ref not in self._STIXBase__valid_refs:

View File

@ -5,15 +5,15 @@ embedded in Email Message objects, inherit from _STIXBase instead of Observable
and do not have a '_type' attribute. and do not have a '_type' attribute.
""" """
from .base import Observable, _STIXBase from .base import _Observable, _STIXBase
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty, from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, HashesProperty, HexProperty, EmbeddedObjectProperty, EnumProperty, HashesProperty,
IntegerProperty, ListProperty, HexProperty, IntegerProperty, ListProperty,
ObjectReferenceProperty, Property, StringProperty, ObjectReferenceProperty, Property, StringProperty,
TimestampProperty, TypeProperty) TimestampProperty, TypeProperty)
class Artifact(Observable): class Artifact(_Observable):
_type = 'artifact' _type = 'artifact'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -29,7 +29,7 @@ class Artifact(Observable):
self._check_properties_dependency(["hashes"], ["url"]) self._check_properties_dependency(["hashes"], ["url"])
class AutonomousSystem(Observable): class AutonomousSystem(_Observable):
_type = 'autonomous-system' _type = 'autonomous-system'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -39,7 +39,7 @@ class AutonomousSystem(Observable):
} }
class Directory(Observable): class Directory(_Observable):
_type = 'directory' _type = 'directory'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -53,7 +53,7 @@ class Directory(Observable):
} }
class DomainName(Observable): class DomainName(_Observable):
_type = 'domain-name' _type = 'domain-name'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -62,7 +62,7 @@ class DomainName(Observable):
} }
class EmailAddress(Observable): class EmailAddress(_Observable):
_type = 'email-address' _type = 'email-address'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -85,7 +85,7 @@ class EmailMIMEComponent(_STIXBase):
self._check_at_least_one_property(["body", "body_raw_ref"]) self._check_at_least_one_property(["body", "body_raw_ref"])
class EmailMessage(Observable): class EmailMessage(_Observable):
_type = 'email-message' _type = 'email-message'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -111,7 +111,7 @@ class EmailMessage(Observable):
# self._dependency(["is_multipart"], ["body"], [False]) # self._dependency(["is_multipart"], ["body"], [False])
class File(Observable): class File(_Observable):
_type = 'file' _type = 'file'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -140,7 +140,7 @@ class File(Observable):
self._check_at_least_one_property(["hashes", "name"]) self._check_at_least_one_property(["hashes", "name"])
class IPv4Address(Observable): class IPv4Address(_Observable):
_type = 'ipv4-addr' _type = 'ipv4-addr'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -150,7 +150,7 @@ class IPv4Address(Observable):
} }
class IPv6Address(Observable): class IPv6Address(_Observable):
_type = 'ipv6-addr' _type = 'ipv6-addr'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -160,7 +160,7 @@ class IPv6Address(Observable):
} }
class MACAddress(Observable): class MACAddress(_Observable):
_type = 'mac-addr' _type = 'mac-addr'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -168,7 +168,7 @@ class MACAddress(Observable):
} }
class Mutex(Observable): class Mutex(_Observable):
_type = 'mutex' _type = 'mutex'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -176,7 +176,7 @@ class Mutex(Observable):
} }
class NetworkTraffic(Observable): class NetworkTraffic(_Observable):
_type = 'network-traffic' _type = 'network-traffic'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -205,7 +205,7 @@ class NetworkTraffic(Observable):
self._check_at_least_one_property(["src_ref", "dst_ref"]) self._check_at_least_one_property(["src_ref", "dst_ref"])
class Process(Observable): class Process(_Observable):
_type = 'process' _type = 'process'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -227,7 +227,7 @@ class Process(Observable):
} }
class Software(Observable): class Software(_Observable):
_type = 'software' _type = 'software'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -239,7 +239,7 @@ class Software(Observable):
} }
class URL(Observable): class URL(_Observable):
_type = 'url' _type = 'url'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -247,7 +247,7 @@ class URL(Observable):
} }
class UserAccount(Observable): class UserAccount(_Observable):
_type = 'user-account' _type = 'user-account'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -273,11 +273,25 @@ class WindowsRegistryValueType(_STIXBase):
_properties = { _properties = {
'name': StringProperty(required=True), 'name': StringProperty(required=True),
'data': StringProperty(), 'data': StringProperty(),
'data_type': Property() 'data_type': EnumProperty([
'REG_NONE',
'REG_SZ',
'REG_EXPAND_SZ',
'REG_BINARY',
'REG_DWORD',
'REG_DWORD_BIG_ENDIAN',
'REG_LINK',
'REG_MULTI_SZ',
'REG_RESOURCE_LIST',
'REG_FULL_RESOURCE_DESCRIPTION',
'REG_RESOURCE_REQUIREMENTS_LIST',
'REG_QWORD',
'REG_INVALID_TYPE',
]),
} }
class WindowsRegistryKey(Observable): class WindowsRegistryKey(_Observable):
_type = 'windows-registry-key' _type = 'windows-registry-key'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),
@ -291,8 +305,9 @@ class WindowsRegistryKey(Observable):
@property @property
def values(self): def values(self):
# Needed because 'values' is a property on collections.Mapping objects
return self._inner['values'] return self._inner['values']
class X509V3ExtenstionsType(_STIXBase): class X509V3ExtenstionsType(_STIXBase):
_type = 'x509-v3-extensions-type' _type = 'x509-v3-extensions-type'
@ -314,9 +329,9 @@ class X509V3ExtenstionsType(_STIXBase):
'certificate_policies': StringProperty(), 'certificate_policies': StringProperty(),
'policy_mappings': StringProperty(), 'policy_mappings': StringProperty(),
} }
class X509Certificate(_Observable):
class X509Certificate(Observable):
_type = 'x509-certificate' _type = 'x509-certificate'
_properties = { _properties = {
'type': TypeProperty(_type), 'type': TypeProperty(_type),

View File

@ -5,10 +5,12 @@ import datetime as dt
import inspect import inspect
import re import re
import uuid import uuid
from six import text_type
import pytz
from dateutil import parser from dateutil import parser
from .base import Observable, _STIXBase import pytz
from six import text_type
from .base import _Observable, _STIXBase
from .exceptions import DictionaryKeyError from .exceptions import DictionaryKeyError
@ -141,6 +143,7 @@ class StringProperty(Property):
class TypeProperty(Property): class TypeProperty(Property):
def __init__(self, type): def __init__(self, type):
super(TypeProperty, self).__init__(fixed=type) super(TypeProperty, self).__init__(fixed=type)
@ -226,7 +229,7 @@ class ObservableProperty(Property):
from .__init__ import parse_observable # avoid circular import from .__init__ import parse_observable # avoid circular import
for key, obj in dictified.items(): for key, obj in dictified.items():
parsed_obj = parse_observable(obj, dictified.keys()) parsed_obj = parse_observable(obj, dictified.keys())
if not issubclass(type(parsed_obj), Observable): if not issubclass(type(parsed_obj), _Observable):
raise ValueError("Objects in an observable property must be " raise ValueError("Objects in an observable property must be "
"Cyber Observable Objects") "Cyber Observable Objects")
dictified[key] = parsed_obj dictified[key] = parsed_obj
@ -308,6 +311,7 @@ REF_REGEX = re.compile("^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}"
class ReferenceProperty(Property): class ReferenceProperty(Property):
def __init__(self, required=False, type=None): def __init__(self, required=False, type=None):
""" """
references sometimes must be to a specific object type references sometimes must be to a specific object type
@ -330,6 +334,7 @@ SELECTOR_REGEX = re.compile("^[a-z0-9_-]{3,250}(\\.(\\[\\d+\\]|[a-z0-9_-]{1,250}
class SelectorProperty(Property): class SelectorProperty(Property):
def __init__(self, type=None): def __init__(self, type=None):
# ignore type # ignore type
super(SelectorProperty, self).__init__() super(SelectorProperty, self).__init__()
@ -345,6 +350,7 @@ class ObjectReferenceProperty(StringProperty):
class EmbeddedObjectProperty(Property): class EmbeddedObjectProperty(Property):
def __init__(self, type, required=False): def __init__(self, type, required=False):
self.type = type self.type = type
super(EmbeddedObjectProperty, self).__init__(required, type=type) super(EmbeddedObjectProperty, self).__init__(required, type=type)
@ -355,3 +361,18 @@ class EmbeddedObjectProperty(Property):
elif not isinstance(value, self.type): elif not isinstance(value, self.type):
raise ValueError("must be of type %s." % self.type.__name__) raise ValueError("must be of type %s." % self.type.__name__)
return value 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)

View File

@ -4,8 +4,8 @@ from .base import _STIXBase
from .common import COMMON_PROPERTIES from .common import COMMON_PROPERTIES
from .other import KillChainPhase from .other import KillChainPhase
from .properties import (IDProperty, IntegerProperty, ListProperty, from .properties import (IDProperty, IntegerProperty, ListProperty,
ObservableProperty, ReferenceProperty, ObservableProperty, ReferenceProperty, StringProperty,
StringProperty, TimestampProperty, TypeProperty) TimestampProperty, TypeProperty)
from .utils import NOW from .utils import NOW

View File

@ -587,7 +587,7 @@ def test_software_example():
assert s.version == "2002" assert s.version == "2002"
assert s.vendor == "Microsoft" assert s.vendor == "Microsoft"
def test_url_example(): def test_url_example():
s = stix2.URL(value="https://example.com/research/index.html") s = stix2.URL(value="https://example.com/research/index.html")
@ -619,26 +619,23 @@ def test_user_account_example():
assert a.password_last_changed == dt.datetime(2016, 1, 20, 14, 27, 43, tzinfo=pytz.utc) assert a.password_last_changed == dt.datetime(2016, 1, 20, 14, 27, 43, tzinfo=pytz.utc)
assert a.account_first_login == dt.datetime(2016, 1, 20, 14, 26, 7, tzinfo=pytz.utc) assert a.account_first_login == dt.datetime(2016, 1, 20, 14, 26, 7, tzinfo=pytz.utc)
assert a.account_last_login == dt.datetime(2016, 7, 22, 16, 8, 28, tzinfo=pytz.utc) assert a.account_last_login == dt.datetime(2016, 7, 22, 16, 8, 28, tzinfo=pytz.utc)
def test_windows_registry_key_example(): def test_windows_registry_key_example():
rk = stix2.WindowsRegistryKey(key="hkey_local_machine\\system\\bar\\foo", with pytest.raises(ValueError):
values=[{ v = stix2.WindowsRegistryValueType(name="Foo",
"name": "Foo", data="qwerty",
"data": "qwerty", data_type="string")
"data_type": "REG_SZ"
},
{
"name": "Bar",
"data": "42",
"data_type": "REG_DWORD"
}])
assert rk.type == "windows-registry-key" v = stix2.WindowsRegistryValueType(name="Foo",
assert rk.key == "hkey_local_machine\\system\\bar\\foo" data="qwerty",
assert rk.values[0].name == "Foo" data_type="REG_SZ")
assert rk.values[0].data == "qwerty" w = stix2.WindowsRegistryKey(key="hkey_local_machine\\system\\bar\\foo",
assert rk.values[0].data_type == "REG_SZ" values=[v])
assert w.key == "hkey_local_machine\\system\\bar\\foo"
assert w.values[0].name == "Foo"
assert w.values[0].data == "qwerty"
assert w.values[0].data_type == "REG_SZ"
def test_x509_certificate_example(): def test_x509_certificate_example():
@ -651,3 +648,4 @@ def test_x509_certificate_example():
assert x509.type == "x509-certificate" assert x509.type == "x509-certificate"
assert x509.issuer == "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com" # noqa assert x509.issuer == "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com" # noqa
assert x509.subject == "C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala@freesoft.org" # noqa assert x509.subject == "C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala@freesoft.org" # noqa

View File

@ -4,9 +4,9 @@ from stix2.exceptions import DictionaryKeyError
from stix2.observables import EmailMIMEComponent from stix2.observables import EmailMIMEComponent
from stix2.properties import (BinaryProperty, BooleanProperty, from stix2.properties import (BinaryProperty, BooleanProperty,
DictionaryProperty, EmbeddedObjectProperty, DictionaryProperty, EmbeddedObjectProperty,
HashesProperty, HexProperty, IDProperty, EnumProperty, HashesProperty, HexProperty,
IntegerProperty, ListProperty, Property, IDProperty, IntegerProperty, ListProperty,
ReferenceProperty, StringProperty, Property, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty) TimestampProperty, TypeProperty)
from .constants import FAKE_TIME from .constants import FAKE_TIME
@ -247,3 +247,11 @@ def test_embedded_property():
with pytest.raises(ValueError): with pytest.raises(ValueError):
emb_prop.clean("string") emb_prop.clean("string")
def test_enum_property():
enum_prop = EnumProperty(['a', 'b', 'c'])
assert enum_prop.clean('b')
with pytest.raises(ValueError):
enum_prop.clean('z')

View File

@ -3,8 +3,8 @@
import datetime as dt import datetime as dt
import json import json
import pytz
from dateutil import parser from dateutil import parser
import pytz
# Sentinel value for fields that should be set to the current time. # Sentinel value for fields that should be set to the current time.
# We can't use the standard 'default' approach, since if there are multiple # We can't use the standard 'default' approach, since if there are multiple

View File

@ -32,7 +32,7 @@ max-line-length=160
[testenv:isort-check] [testenv:isort-check]
deps = isort deps = isort
commands = isort -ns __init__.py -c -rc stix2 commands = isort -rc stix2 -c -df
[travis] [travis]
python = python =