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

View File

@ -163,6 +163,13 @@ class ParseError(STIXError, ValueError):
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):
"""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"
}"""
with pytest.raises(stix2.exceptions.ParseError) as excinfo:
with pytest.raises(stix2.exceptions.CustomContentError) as excinfo:
stix2.parse_observable(nt_string)
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)
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():
input_str = """{
"type": "observed-data",

View File

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