Disallow missing 'type' property with allow_custom

There was a bug where if you allowed custom content the library would parse an
object without the required 'type' property.
stix2.0
Chris Lenk 2018-04-13 11:18:56 -04:00
parent 91376586d4
commit fc6a33b23e
4 changed files with 30 additions and 12 deletions

View File

@ -6,11 +6,11 @@ import datetime as dt
import simplejson as json import simplejson as json
from .exceptions import (AtLeastOnePropertyError, DependentPropertiesError, from .exceptions import (AtLeastOnePropertyError, CustomContentError,
ExtraPropertiesError, ImmutableError, DependentPropertiesError, ExtraPropertiesError,
InvalidObjRefError, InvalidValueError, ImmutableError, InvalidObjRefError, InvalidValueError,
MissingPropertiesError, MissingPropertiesError,
MutuallyExclusivePropertiesError, ParseError) MutuallyExclusivePropertiesError)
from .markings.utils import validate from .markings.utils import validate
from .utils import NOW, find_property_index, format_datetime, get_timestamp from .utils import NOW, find_property_index, format_datetime, get_timestamp
from .utils import new_version as _new_version from .utils import new_version as _new_version
@ -61,7 +61,7 @@ class _STIXBase(collections.Mapping):
try: try:
kwargs[prop_name] = prop.clean(kwargs[prop_name]) kwargs[prop_name] = prop.clean(kwargs[prop_name])
except ValueError as exc: except ValueError as exc:
if self.__allow_custom and isinstance(exc, ParseError): if self.__allow_custom and isinstance(exc, CustomContentError):
return return
raise InvalidValueError(self.__class__, prop_name, reason=str(exc)) raise InvalidValueError(self.__class__, prop_name, reason=str(exc))

View File

@ -163,6 +163,13 @@ class ParseError(STIXError, ValueError):
super(ParseError, self).__init__(msg) super(ParseError, self).__init__(msg)
class CustomContentError(STIXError, ValueError):
"""Custom STIX Content (SDO, Observable, Extension, etc.) detected."""
def __init__(self, msg):
super(CustomContentError, self).__init__(msg)
class InvalidSelectorError(STIXError, AssertionError): class InvalidSelectorError(STIXError, AssertionError):
"""Granular Marking selector violation. The selector must resolve into an existing STIX object property.""" """Granular Marking selector violation. The selector must resolve into an existing STIX object property."""

View File

@ -373,7 +373,7 @@ def test_parse_unregistered_custom_observable_object():
"property1": "something" "property1": "something"
}""" }"""
with pytest.raises(stix2.exceptions.ParseError) as excinfo: with pytest.raises(stix2.exceptions.CustomContentError) as excinfo:
stix2.parse_observable(nt_string) stix2.parse_observable(nt_string)
assert "Can't parse unknown observable type" in str(excinfo.value) assert "Can't parse unknown observable type" in str(excinfo.value)
@ -384,6 +384,16 @@ def test_parse_unregistered_custom_observable_object():
assert not isinstance(parsed_custom, stix2.core._STIXBase) assert not isinstance(parsed_custom, stix2.core._STIXBase)
def test_parse_unregistered_custom_observable_object_with_no_type():
nt_string = """{
"property1": "something"
}"""
with pytest.raises(stix2.exceptions.ParseError) as excinfo:
stix2.parse_observable(nt_string, allow_custom=True)
assert "Can't parse observable with no 'type' property" in str(excinfo.value)
def test_parse_observed_data_with_custom_observable(): def test_parse_observed_data_with_custom_observable():
input_str = """{ input_str = """{
"type": "observed-data", "type": "observed-data",

View File

@ -8,8 +8,8 @@ Observable and do not have a ``_type`` attribute.
from collections import OrderedDict from collections import OrderedDict
from ..base import _Extension, _Observable, _STIXBase from ..base import _Extension, _Observable, _STIXBase
from ..exceptions import (AtLeastOnePropertyError, DependentPropertiesError, from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
ParseError) DependentPropertiesError, ParseError)
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty, from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty, EmbeddedObjectProperty, EnumProperty, FloatProperty,
HashesProperty, HexProperty, IntegerProperty, HashesProperty, HexProperty, IntegerProperty,
@ -67,7 +67,7 @@ class ExtensionsProperty(DictionaryProperty):
else: else:
raise ValueError("Cannot determine extension type.") raise ValueError("Cannot determine extension type.")
else: else:
raise ParseError("Can't parse unknown extension type: {}".format(key)) raise CustomContentError("Can't parse unknown extension type: {}".format(key))
else: else:
raise ValueError("The enclosing type '%s' has no extensions defined" % self.enclosing_type) raise ValueError("The enclosing type '%s' has no extensions defined" % self.enclosing_type)
return dictified return dictified
@ -927,8 +927,8 @@ def parse_observable(data, _valid_refs=None, allow_custom=False):
# flag allows for unknown custom objects too, but will not # flag allows for unknown custom objects too, but will not
# be parsed into STIX observable object, just returned as is # be parsed into STIX observable object, just returned as is
return obj return obj
raise ParseError("Can't parse unknown observable type '%s'! For custom observables, " raise CustomContentError("Can't parse unknown observable type '%s'! For custom observables, "
"use the CustomObservable decorator." % obj['type']) "use the CustomObservable decorator." % obj['type'])
if 'extensions' in obj and obj['type'] in EXT_MAP: if 'extensions' in obj and obj['type'] in EXT_MAP:
for name, ext in obj['extensions'].items(): for name, ext in obj['extensions'].items():
@ -936,7 +936,8 @@ def parse_observable(data, _valid_refs=None, allow_custom=False):
ext_class = EXT_MAP[obj['type']][name] ext_class = EXT_MAP[obj['type']][name]
except KeyError: except KeyError:
if not allow_custom: if not allow_custom:
raise ParseError("Can't parse unknown extension type '%s' for observable type '%s'!" % (name, obj['type'])) raise CustomContentError("Can't parse unknown extension type '%s'"
"for observable type '%s'!" % (name, obj['type']))
else: # extension was found else: # extension was found
obj['extensions'][name] = ext_class(allow_custom=allow_custom, **obj['extensions'][name]) obj['extensions'][name] = ext_class(allow_custom=allow_custom, **obj['extensions'][name])