Merge branch 'parse-cyber-observables' into cyber-observables
commit
ae5fb51564
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue