Convert 'type' to a new Property class.

stix2.1
Greg Back 2017-02-24 10:46:21 -06:00
parent a264ca1e5e
commit 2645bf2c71
7 changed files with 51 additions and 21 deletions

View File

@ -1,15 +1,14 @@
"""STIX 2 Bundle object""" """STIX 2 Bundle object"""
from .base import _STIXBase from .base import _STIXBase
from .common import TYPE_PROPERTY from .properties import IDProperty, TypeProperty
from .properties import IDProperty
class Bundle(_STIXBase): class Bundle(_STIXBase):
_type = 'bundle' _type = 'bundle'
_properties = { _properties = {
'type': TYPE_PROPERTY, 'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'spec_version': { 'spec_version': {
'fixed': "2.0", 'fixed': "2.0",

View File

@ -4,11 +4,6 @@ import re
from .base import _STIXBase from .base import _STIXBase
from .utils import NOW from .utils import NOW
TYPE_PROPERTY = {
'default': (lambda x: x._type),
'validate': (lambda x, val: val == x._type)
}
ref_regex = ("^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}" ref_regex = ("^[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}$") "-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
@ -23,8 +18,7 @@ BOOL_PROPERTY = {
} }
COMMON_PROPERTIES = { COMMON_PROPERTIES = {
'type': TYPE_PROPERTY, # 'type' and 'id' should be defined on each individual type
# 'id' should be defined on each individual type
'created': { 'created': {
'default': NOW, 'default': NOW,
}, },

View File

@ -44,10 +44,16 @@ class Property(object):
lambdas cannot raise their own exceptions. lambdas cannot raise their own exceptions.
""" """
def _default_validate(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, clean=None, validate=None, default=None): def __init__(self, required=False, fixed=None, clean=None, validate=None, default=None):
self.required = required self.required = required
if fixed: if fixed:
self.validate = lambda x: x == fixed self._fixed_value = fixed
self.validate = self._default_validate
self.default = lambda: fixed self.default = lambda: fixed
if clean: if clean:
self.clean = clean self.clean = clean
@ -97,6 +103,12 @@ class List(Property):
return [self.contained(x) for x in value] return [self.contained(x) for x in value]
class TypeProperty(Property):
def __init__(self, type):
super(TypeProperty, self).__init__(fixed=type)
class IDProperty(Property): class IDProperty(Property):
def __init__(self, type): def __init__(self, type):

View File

@ -2,7 +2,7 @@
from .base import _STIXBase from .base import _STIXBase
from .common import COMMON_PROPERTIES from .common import COMMON_PROPERTIES
from .properties import IDProperty from .properties import IDProperty, TypeProperty
from .utils import NOW from .utils import NOW
@ -11,6 +11,7 @@ class AttackPattern(_STIXBase):
_type = 'attack-pattern' _type = 'attack-pattern'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,
@ -37,6 +38,7 @@ class Campaign(_STIXBase):
_type = 'campaign' _type = 'campaign'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,
@ -69,6 +71,7 @@ class CourseOfAction(_STIXBase):
_type = 'course-of-action' _type = 'course-of-action'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,
@ -93,6 +96,7 @@ class Identity(_STIXBase):
_type = 'identity' _type = 'identity'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,
@ -125,6 +129,7 @@ class Indicator(_STIXBase):
_type = 'indicator' _type = 'indicator'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'labels': { 'labels': {
'required': True, 'required': True,
@ -161,6 +166,7 @@ class IntrusionSet(_STIXBase):
_type = 'intrusion-set' _type = 'intrusion-set'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,
@ -199,6 +205,7 @@ class Malware(_STIXBase):
_type = 'malware' _type = 'malware'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'labels': { 'labels': {
'required': True, 'required': True,
@ -228,6 +235,7 @@ class ObservedData(_STIXBase):
_type = 'observed-data' _type = 'observed-data'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'first_observed': {}, 'first_observed': {},
'last_observed': {}, 'last_observed': {},
@ -255,6 +263,7 @@ class Report(_STIXBase):
_type = 'report' _type = 'report'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'labels': { 'labels': {
'required': True, 'required': True,
@ -286,6 +295,7 @@ class ThreatActor(_STIXBase):
_type = 'threat-actor' _type = 'threat-actor'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'labels': { 'labels': {
'required': True, 'required': True,
@ -329,6 +339,7 @@ class Tool(_STIXBase):
_type = 'tool' _type = 'tool'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'labels': { 'labels': {
'required': True, 'required': True,
@ -360,6 +371,7 @@ class Vulnerability(_STIXBase):
_type = 'vulnerability' _type = 'vulnerability'
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'type': TypeProperty(_type),
'id': IDProperty(_type), 'id': IDProperty(_type),
'name': { 'name': {
'required': True, 'required': True,

View File

@ -2,7 +2,7 @@
from .base import _STIXBase from .base import _STIXBase
from .common import COMMON_PROPERTIES from .common import COMMON_PROPERTIES
from .properties import IDProperty from .properties import IDProperty, TypeProperty
class Relationship(_STIXBase): class Relationship(_STIXBase):
@ -11,6 +11,7 @@ class Relationship(_STIXBase):
_properties = COMMON_PROPERTIES.copy() _properties = COMMON_PROPERTIES.copy()
_properties.update({ _properties.update({
'id': IDProperty(_type), 'id': IDProperty(_type),
'type': TypeProperty(_type),
'relationship_type': { 'relationship_type': {
'required': True, 'required': True,
}, },

View File

@ -1,6 +1,6 @@
import pytest import pytest
from stix2.properties import Property, IDProperty from stix2.properties import Property, IDProperty, TypeProperty
def test_property(): def test_property():
@ -39,11 +39,23 @@ def test_default_field():
def test_fixed_property(): def test_fixed_property():
p = Property(fixed="2.0") p = Property(fixed="2.0")
assert p.validate("2.0") is True assert p.validate("2.0")
assert p.validate("x") is False with pytest.raises(ValueError):
assert p.validate(2.0) is False assert p.validate("x") is False
with pytest.raises(ValueError):
assert p.validate(2.0) is False
assert p.default() == "2.0" assert p.default() == "2.0"
assert p.validate(p.default())
def test_type_property():
prop = TypeProperty('my-type')
assert prop.validate('my-type')
with pytest.raises(ValueError):
prop.validate('not-my-type')
assert prop.validate(prop.default())
def test_id_property(): def test_id_property():

View File

@ -154,7 +154,7 @@ def test_indicator_type_must_be_indicator():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS) indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS)
assert str(excinfo.value) == "Indicator must have type='indicator'." assert str(excinfo.value) == "Invalid value for Indicator 'type': must equal 'indicator'."
def test_indicator_id_must_start_with_indicator(): def test_indicator_id_must_start_with_indicator():
@ -255,7 +255,7 @@ def test_malware_type_must_be_malware():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
malware = stix2.Malware(type='xxx', **MALWARE_KWARGS) malware = stix2.Malware(type='xxx', **MALWARE_KWARGS)
assert str(excinfo.value) == "Malware must have type='malware'." assert str(excinfo.value) == "Invalid value for Malware 'type': must equal 'malware'."
def test_malware_id_must_start_with_malware(): def test_malware_id_must_start_with_malware():
@ -338,7 +338,7 @@ def test_relationship_type_must_be_relationship():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS) relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS)
assert str(excinfo.value) == "Relationship must have type='relationship'." assert str(excinfo.value) == "Invalid value for Relationship 'type': must equal 'relationship'."
def test_relationship_id_must_start_with_relationship(): def test_relationship_id_must_start_with_relationship():
@ -457,7 +457,7 @@ def test_bundle_with_wrong_type():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
bundle = stix2.Bundle(type="not-a-bundle") bundle = stix2.Bundle(type="not-a-bundle")
assert str(excinfo.value) == "Bundle must have type='bundle'." assert str(excinfo.value) == "Invalid value for Bundle 'type': must equal 'bundle'."
def test_bundle_id_must_start_with_bundle(): def test_bundle_id_must_start_with_bundle():