Repository: oasis-open/cti-stix-validator
Pull request #59 (2018-06-13 20:12:12)

 Improve errors for objects missing 'type' property
We weren't checking SDOs inside Bundles, or Cyber Observables inside Observed Data objects, which resulted in very unclear error messages (`Fatal Error: 'type'`).


Patch:
diff --git a/stix2validator/validator.py b/stix2validator/validator.py
index 1d1ad16..74c3fd0 100644
--- a/stix2validator/validator.py
+++ b/stix2validator/validator.py
@@ -12,8 +12,8 @@ import simplejson as json
 from six import iteritems, string_types, text_type
 
 from . import musts, output, shoulds
-from .errors import (NoJSONFileFoundError, SchemaError, SchemaInvalidError,
-                     ValidationError, pretty_error)
+from .errors import (JSONError, NoJSONFileFoundError, SchemaError,
+                     SchemaInvalidError, ValidationError, pretty_error)
 from .util import ValidationOptions, clear_requests_cache, init_requests_cache
 
 DEFAULT_SCHEMA_DIR = os.path.abspath(os.path.dirname(__file__) + '/schemas/')
@@ -601,6 +601,10 @@ def _schema_validate(sdo, options):
     # Validate each cyber observable object separately
     if sdo['type'] == 'observed-data' and 'objects' in sdo:
         for key, obj in iteritems(sdo['objects']):
+            if 'type' not in obj:
+                error_gens.append(([JSONError("Observable object must contain a 'type' property.", error_prefix)],
+                                   error_prefix + 'object \'' + key + '\': '))
+                continue
             # Get validator for built-in schemas
             base_obs_errors = _get_error_generator(obj['type'],
                                                    obj,
@@ -648,6 +652,8 @@ def validate_instance(instance, options=None):
     if instance['type'] == 'bundle' and 'objects' in instance:
         # Validate each object in a bundle separately
         for sdo in instance['objects']:
+            if 'type' not in sdo:
+                raise ValidationError("Each object in bundle must have a 'type' property.")
             error_gens += _schema_validate(sdo, options)
     else:
         error_gens += _schema_validate(instance, options)


Test patch:
diff --git a/stix2validator/test/bundle_tests.py b/stix2validator/test/bundle_tests.py
index 52235ba..ee39d7b 100644
--- a/stix2validator/test/bundle_tests.py
+++ b/stix2validator/test/bundle_tests.py
@@ -4,6 +4,7 @@ import json
 import pytest
 
 from . import ValidatorTest
+from .. import ValidationError
 
 VALID_BUNDLE = u"""
 {
@@ -58,3 +59,9 @@ class BundleTestCases(ValidatorTest):
         bundle = json.loads(VALID_BUNDLE)
         with pytest.raises(SystemExit):
             self.assertFalseWithOptions(bundle, silent=True, verbose=True)
+
+    def test_bundle_sdo_missing_type(self):
+        bundle = copy.deepcopy(self.valid_bundle)
+        del bundle['objects'][0]['type']
+        with pytest.raises(ValidationError):
+            self.assertFalseWithOptions(bundle)
diff --git a/stix2validator/test/observed_data_tests.py b/stix2validator/test/observed_data_tests.py
index 985d4e1..f102f97 100644
--- a/stix2validator/test/observed_data_tests.py
+++ b/stix2validator/test/observed_data_tests.py
@@ -288,6 +288,11 @@ class ObservedDataTestCases(ValidatorTest):
 
         self.check_ignore(observed_data, 'observable-object-keys')
 
+    def test_observable_object_no_type(self):
+        observed_data = copy.deepcopy(self.valid_observed_data)
+        del observed_data['objects']['0']['type']
+        self.assertFalseWithOptions(observed_data)
+
     def test_observable_object_types(self):
         observed_data = copy.deepcopy(self.valid_observed_data)
         observed_data['objects']['0']['type'] = "x--foo"