Repository: oasis-open/cti-python-stix2
Pull request #240 (2019-01-09 15:25:12)

Fix issue 232


Patch:
diff --git a/stix2/datastore/filesystem.py b/stix2/datastore/filesystem.py
index b4f3d15..0e8bed2 100644
--- a/stix2/datastore/filesystem.py
+++ b/stix2/datastore/filesystem.py
@@ -1,21 +1,19 @@
 """Python STIX2 FileSystem Source/Sink"""
-# Temporary while we address TODO statement
-from __future__ import print_function
-
 import errno
 import io
 import json
 import os
 import re
 import stat
-import sys
 
 import six
 
 from stix2 import v20, v21
 from stix2.base import _STIXBase
 from stix2.core import parse
-from stix2.datastore import DataSink, DataSource, DataStoreMixin
+from stix2.datastore import (
+    DataSink, DataSource, DataSourceError, DataStoreMixin,
+)
 from stix2.datastore.filters import Filter, FilterSet, apply_common_filters
 from stix2.utils import format_datetime, get_type_from_id, is_marking
 
@@ -544,9 +542,8 @@ class FileSystemSink(DataSink):
             else:
                 stix_obj = v20.Bundle(stix_obj, allow_custom=self.allow_custom)
 
-        # TODO: Better handling of the overwriting case.
         if os.path.isfile(file_path):
-            print("Attempted to overwrite file!", file_path, file=sys.stderr)
+            raise DataSourceError("Attempted to overwrite file (!) at: {}".format(file_path))
         else:
             with io.open(file_path, 'w', encoding=encoding) as f:
                 stix_obj = stix_obj.serialize(pretty=True, encoding=encoding, ensure_ascii=False)
diff --git a/stix2/properties.py b/stix2/properties.py
index 7202f9a..24549aa 100644
--- a/stix2/properties.py
+++ b/stix2/properties.py
@@ -291,6 +291,8 @@ class DictionaryProperty(Property):
             dictified = _get_dict(value)
         except ValueError:
             raise ValueError("The dictionary property must contain a dictionary")
+        if dictified == {}:
+            raise ValueError("The dictionary property must contain a non-empty dictionary")
         for k in dictified.keys():
             if self.spec_version == '2.0':
                 if len(k) < 3:
@@ -496,6 +498,8 @@ class ExtensionsProperty(DictionaryProperty):
             dictified = copy.deepcopy(dictified)
         except ValueError:
             raise ValueError("The extensions property must contain a dictionary")
+        if dictified == {}:
+            raise ValueError("The extensions property must contain a non-empty dictionary")
 
         v = 'v' + self.spec_version.replace('.', '')
 


Test patch:
diff --git a/stix2/test/v20/test_datastore_filesystem.py b/stix2/test/v20/test_datastore_filesystem.py
index 84a3034..86846c4 100644
--- a/stix2/test/v20/test_datastore_filesystem.py
+++ b/stix2/test/v20/test_datastore_filesystem.py
@@ -9,6 +9,7 @@ import pytest
 import pytz
 
 import stix2
+from stix2.datastore import DataSourceError
 from stix2.datastore.filesystem import (
     AuthSet, _find_search_optimizations, _get_matching_dir_entries,
     _timestamp2filename,
@@ -420,6 +421,33 @@ def test_filesystem_sink_add_objects_list(fs_sink, fs_source):
     os.remove(camp7filepath)
 
 
+def test_filesystem_attempt_stix_file_overwrite(fs_store):
+    # add python stix object
+    camp8 = stix2.v20.Campaign(
+        name="George Washington",
+        objective="Create an awesome country",
+        aliases=["Georgey"],
+    )
+
+    fs_store.add(camp8)
+
+    camp8_r = fs_store.get(camp8.id)
+    assert camp8_r.id == camp8_r.id
+    assert camp8_r.name == camp8.name
+
+    filepath = os.path.join(
+        FS_PATH, "campaign", camp8_r.id,
+        _timestamp2filename(camp8_r.modified) + ".json",
+    )
+
+    # Now attempt to overwrite the existing file
+    with pytest.raises(DataSourceError) as excinfo:
+        fs_store.add(camp8)
+    assert "Attempted to overwrite file" in str(excinfo)
+
+    os.remove(filepath)
+
+
 def test_filesystem_sink_marking(fs_sink):
     marking = stix2.v20.MarkingDefinition(
         definition_type="tlp",
diff --git a/stix2/test/v20/test_observed_data.py b/stix2/test/v20/test_observed_data.py
index 17d732e..41a80d6 100644
--- a/stix2/test/v20/test_observed_data.py
+++ b/stix2/test/v20/test_observed_data.py
@@ -1141,13 +1141,12 @@ def test_process_example_windows_process_ext_empty():
 
 
 def test_process_example_extensions_empty():
-    proc = stix2.v20.Process(
-        pid=314,
-        name="foobar.exe",
-        extensions={},
-    )
+    with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
+            stix2.v20.Process(extensions={})
 
-    assert '{}' in str(proc)
+    assert excinfo.value.cls == stix2.v20.Process
+    assert excinfo.value.prop_name == 'extensions'
+    assert 'non-empty dictionary' in excinfo.value.reason
 
 
 def test_process_example_with_WindowsProcessExt_Object():
diff --git a/stix2/test/v20/test_properties.py b/stix2/test/v20/test_properties.py
index e9a513e..24c1c99 100644
--- a/stix2/test/v20/test_properties.py
+++ b/stix2/test/v20/test_properties.py
@@ -360,6 +360,7 @@ def test_dictionary_property_invalid_key(d):
 
 @pytest.mark.parametrize(
     "d", [
+        ({}, "The dictionary property must contain a non-empty dictionary"),
         # TODO: This error message could be made more helpful. The error is caused
         # because `json.loads()` doesn't like the *single* quotes around the key
         # name, even though they are valid in a Python dictionary. While technically
diff --git a/stix2/test/v21/test_observed_data.py b/stix2/test/v21/test_observed_data.py
index d2aaf52..5a5881a 100644
--- a/stix2/test/v21/test_observed_data.py
+++ b/stix2/test/v21/test_observed_data.py
@@ -1115,12 +1115,12 @@ def test_process_example_windows_process_ext_empty():
 
 
 def test_process_example_extensions_empty():
-    proc = stix2.v21.Process(
-        pid=314,
-        extensions={},
-    )
+    with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
+            stix2.v21.Process(extensions={})
 
-    assert '{}' in str(proc)
+    assert excinfo.value.cls == stix2.v21.Process
+    assert excinfo.value.prop_name == 'extensions'
+    assert 'non-empty dictionary' in excinfo.value.reason
 
 
 def test_process_example_with_WindowsProcessExt_Object():
diff --git a/stix2/test/v21/test_properties.py b/stix2/test/v21/test_properties.py
index 298a8df..611ec5e 100644
--- a/stix2/test/v21/test_properties.py
+++ b/stix2/test/v21/test_properties.py
@@ -369,6 +369,7 @@ def test_dictionary_property_invalid_key(d):
 
 @pytest.mark.parametrize(
     "d", [
+        ({}, "The dictionary property must contain a non-empty dictionary"),
         # TODO: This error message could be made more helpful. The error is caused
         # because `json.loads()` doesn't like the *single* quotes around the key
         # name, even though they are valid in a Python dictionary. While technically