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)
class Observable(_STIXBase):
class _Observable(_STIXBase):
def __init__(self, **kwargs):
# 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')
else:
self._STIXBase__valid_refs = []
super(Observable, self).__init__(**kwargs)
super(_Observable, self).__init__(**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:
ref = kwargs[prop_name]
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.
"""
from .base import Observable, _STIXBase
from .base import _Observable, _STIXBase
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, HashesProperty, HexProperty,
IntegerProperty, ListProperty,
EmbeddedObjectProperty, EnumProperty, HashesProperty,
HexProperty, IntegerProperty, ListProperty,
ObjectReferenceProperty, Property, StringProperty,
TimestampProperty, TypeProperty)
class Artifact(Observable):
class Artifact(_Observable):
_type = 'artifact'
_properties = {
'type': TypeProperty(_type),
@ -29,7 +29,7 @@ class Artifact(Observable):
self._check_properties_dependency(["hashes"], ["url"])
class AutonomousSystem(Observable):
class AutonomousSystem(_Observable):
_type = 'autonomous-system'
_properties = {
'type': TypeProperty(_type),
@ -39,7 +39,7 @@ class AutonomousSystem(Observable):
}
class Directory(Observable):
class Directory(_Observable):
_type = 'directory'
_properties = {
'type': TypeProperty(_type),
@ -53,7 +53,7 @@ class Directory(Observable):
}
class DomainName(Observable):
class DomainName(_Observable):
_type = 'domain-name'
_properties = {
'type': TypeProperty(_type),
@ -62,7 +62,7 @@ class DomainName(Observable):
}
class EmailAddress(Observable):
class EmailAddress(_Observable):
_type = 'email-address'
_properties = {
'type': TypeProperty(_type),
@ -85,7 +85,7 @@ class EmailMIMEComponent(_STIXBase):
self._check_at_least_one_property(["body", "body_raw_ref"])
class EmailMessage(Observable):
class EmailMessage(_Observable):
_type = 'email-message'
_properties = {
'type': TypeProperty(_type),
@ -111,7 +111,7 @@ class EmailMessage(Observable):
# self._dependency(["is_multipart"], ["body"], [False])
class File(Observable):
class File(_Observable):
_type = 'file'
_properties = {
'type': TypeProperty(_type),
@ -140,7 +140,7 @@ class File(Observable):
self._check_at_least_one_property(["hashes", "name"])
class IPv4Address(Observable):
class IPv4Address(_Observable):
_type = 'ipv4-addr'
_properties = {
'type': TypeProperty(_type),
@ -150,7 +150,7 @@ class IPv4Address(Observable):
}
class IPv6Address(Observable):
class IPv6Address(_Observable):
_type = 'ipv6-addr'
_properties = {
'type': TypeProperty(_type),
@ -160,7 +160,7 @@ class IPv6Address(Observable):
}
class MACAddress(Observable):
class MACAddress(_Observable):
_type = 'mac-addr'
_properties = {
'type': TypeProperty(_type),
@ -168,7 +168,7 @@ class MACAddress(Observable):
}
class Mutex(Observable):
class Mutex(_Observable):
_type = 'mutex'
_properties = {
'type': TypeProperty(_type),
@ -176,7 +176,7 @@ class Mutex(Observable):
}
class NetworkTraffic(Observable):
class NetworkTraffic(_Observable):
_type = 'network-traffic'
_properties = {
'type': TypeProperty(_type),
@ -205,7 +205,7 @@ class NetworkTraffic(Observable):
self._check_at_least_one_property(["src_ref", "dst_ref"])
class Process(Observable):
class Process(_Observable):
_type = 'process'
_properties = {
'type': TypeProperty(_type),
@ -227,7 +227,7 @@ class Process(Observable):
}
class Software(Observable):
class Software(_Observable):
_type = 'software'
_properties = {
'type': TypeProperty(_type),
@ -239,7 +239,7 @@ class Software(Observable):
}
class URL(Observable):
class URL(_Observable):
_type = 'url'
_properties = {
'type': TypeProperty(_type),
@ -247,7 +247,7 @@ class URL(Observable):
}
class UserAccount(Observable):
class UserAccount(_Observable):
_type = 'user-account'
_properties = {
'type': TypeProperty(_type),
@ -273,11 +273,25 @@ class WindowsRegistryValueType(_STIXBase):
_properties = {
'name': StringProperty(required=True),
'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'
_properties = {
'type': TypeProperty(_type),
@ -291,8 +305,9 @@ class WindowsRegistryKey(Observable):
@property
def values(self):
# Needed because 'values' is a property on collections.Mapping objects
return self._inner['values']
class X509V3ExtenstionsType(_STIXBase):
_type = 'x509-v3-extensions-type'
@ -314,9 +329,9 @@ class X509V3ExtenstionsType(_STIXBase):
'certificate_policies': StringProperty(),
'policy_mappings': StringProperty(),
}
class X509Certificate(Observable):
class X509Certificate(_Observable):
_type = 'x509-certificate'
_properties = {
'type': TypeProperty(_type),

View File

@ -5,10 +5,12 @@ import datetime as dt
import inspect
import re
import uuid
from six import text_type
import pytz
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
@ -141,6 +143,7 @@ class StringProperty(Property):
class TypeProperty(Property):
def __init__(self, type):
super(TypeProperty, self).__init__(fixed=type)
@ -226,7 +229,7 @@ class ObservableProperty(Property):
from .__init__ import parse_observable # avoid circular import
for key, obj in dictified.items():
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 "
"Cyber Observable Objects")
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):
def __init__(self, required=False, type=None):
"""
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):
def __init__(self, type=None):
# ignore type
super(SelectorProperty, self).__init__()
@ -345,6 +350,7 @@ class ObjectReferenceProperty(StringProperty):
class EmbeddedObjectProperty(Property):
def __init__(self, type, required=False):
self.type = type
super(EmbeddedObjectProperty, self).__init__(required, type=type)
@ -355,3 +361,18 @@ class EmbeddedObjectProperty(Property):
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)

View File

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

View File

@ -587,7 +587,7 @@ def test_software_example():
assert s.version == "2002"
assert s.vendor == "Microsoft"
def test_url_example():
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.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)
def test_windows_registry_key_example():
rk = stix2.WindowsRegistryKey(key="hkey_local_machine\\system\\bar\\foo",
values=[{
"name": "Foo",
"data": "qwerty",
"data_type": "REG_SZ"
},
{
"name": "Bar",
"data": "42",
"data_type": "REG_DWORD"
}])
with pytest.raises(ValueError):
v = stix2.WindowsRegistryValueType(name="Foo",
data="qwerty",
data_type="string")
assert rk.type == "windows-registry-key"
assert rk.key == "hkey_local_machine\\system\\bar\\foo"
assert rk.values[0].name == "Foo"
assert rk.values[0].data == "qwerty"
assert rk.values[0].data_type == "REG_SZ"
v = stix2.WindowsRegistryValueType(name="Foo",
data="qwerty",
data_type="REG_SZ")
w = stix2.WindowsRegistryKey(key="hkey_local_machine\\system\\bar\\foo",
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():
@ -651,3 +648,4 @@ def test_x509_certificate_example():
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.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.properties import (BinaryProperty, BooleanProperty,
DictionaryProperty, EmbeddedObjectProperty,
HashesProperty, HexProperty, IDProperty,
IntegerProperty, ListProperty, Property,
ReferenceProperty, StringProperty,
EnumProperty, HashesProperty, HexProperty,
IDProperty, IntegerProperty, ListProperty,
Property, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
from .constants import FAKE_TIME
@ -247,3 +247,11 @@ def test_embedded_property():
with pytest.raises(ValueError):
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 json
import pytz
from dateutil import parser
import pytz
# 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

View File

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