Repository: oasis-open/cti-python-stix2

Issue:
Add tests for FileSystem Data Stores, Sources, Sinks 
Specifically for adding/pushing STIX objects to these DataStores/Sinks as they can now be supplied with STIX content of a variety of forms

Discussion:

Patch:
diff --git a/stix2/__init__.py b/stix2/__init__.py
index 53c2fb1..7be0904 100644
--- a/stix2/__init__.py
+++ b/stix2/__init__.py
@@ -8,8 +8,6 @@ from .common import (TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking,
                      MarkingDefinition, StatementMarking, TLPMarking)
 from .core import Bundle, _register_type, parse
 from .environment import Environment, ObjectFactory
-from .markings import (add_markings, clear_markings, get_markings, is_marked,
-                       remove_markings, set_markings)
 from .observables import (URL, AlternateDataStream, ArchiveExt, Artifact,
                           AutonomousSystem, CustomObservable, Directory,
                           DomainName, EmailAddress, EmailMessage,
diff --git a/stix2/common.py b/stix2/common.py
index d7994c6..a2e6918 100644
--- a/stix2/common.py
+++ b/stix2/common.py
@@ -3,7 +3,6 @@
 from collections import OrderedDict
 
 from .base import _STIXBase
-from .markings import MarkingsMixin
 from .properties import (HashesProperty, IDProperty, ListProperty, Property,
                          ReferenceProperty, SelectorProperty, StringProperty,
                          TimestampProperty, TypeProperty)
@@ -77,7 +76,7 @@ class MarkingProperty(Property):
             raise ValueError("must be a Statement, TLP Marking or a registered marking.")
 
 
-class MarkingDefinition(_STIXBase, MarkingsMixin):
+class MarkingDefinition(_STIXBase):
     _type = 'marking-definition'
     _properties = OrderedDict()
     _properties.update([
diff --git a/stix2/markings/__init__.py b/stix2/markings/__init__.py
index 41c761d..4f72e4c 100644
--- a/stix2/markings/__init__.py
+++ b/stix2/markings/__init__.py
@@ -212,16 +212,3 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa
         result = result or object_markings.is_marked(obj, object_marks)
 
     return result
-
-
-class MarkingsMixin():
-    pass
-
-
-# Note that all of these methods will return a new object because of immutability
-MarkingsMixin.get_markings = get_markings
-MarkingsMixin.set_markings = set_markings
-MarkingsMixin.remove_markings = remove_markings
-MarkingsMixin.add_markings = add_markings
-MarkingsMixin.clear_markings = clear_markings
-MarkingsMixin.is_marked = is_marked
diff --git a/stix2/markings/granular_markings.py b/stix2/markings/granular_markings.py
index 5afd1cc..7e9ccc7 100644
--- a/stix2/markings/granular_markings.py
+++ b/stix2/markings/granular_markings.py
@@ -88,7 +88,6 @@ def remove_markings(obj, marking, selectors):
 
     """
     selectors = utils.convert_to_list(selectors)
-    marking = utils.convert_to_marking_list(marking)
     utils.validate(obj, selectors)
 
     granular_markings = obj.get("granular_markings")
@@ -98,9 +97,12 @@ def remove_markings(obj, marking, selectors):
 
     granular_markings = utils.expand_markings(granular_markings)
 
-    to_remove = []
-    for m in marking:
-        to_remove.append({"marking_ref": m, "selectors": selectors})
+    if isinstance(marking, list):
+        to_remove = []
+        for m in marking:
+            to_remove.append({"marking_ref": m, "selectors": selectors})
+    else:
+        to_remove = [{"marking_ref": marking, "selectors": selectors}]
 
     remove = utils.build_granular_marking(to_remove).get("granular_markings")
 
@@ -138,12 +140,14 @@ def add_markings(obj, marking, selectors):
 
     """
     selectors = utils.convert_to_list(selectors)
-    marking = utils.convert_to_marking_list(marking)
     utils.validate(obj, selectors)
 
-    granular_marking = []
-    for m in marking:
-        granular_marking.append({"marking_ref": m, "selectors": sorted(selectors)})
+    if isinstance(marking, list):
+        granular_marking = []
+        for m in marking:
+            granular_marking.append({"marking_ref": m, "selectors": sorted(selectors)})
+    else:
+        granular_marking = [{"marking_ref": marking, "selectors": sorted(selectors)}]
 
     if obj.get("granular_markings"):
         granular_marking.extend(obj.get("granular_markings"))
@@ -240,7 +244,7 @@ def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=Fa
         raise TypeError("Required argument 'selectors' must be provided")
 
     selectors = utils.convert_to_list(selectors)
-    marking = utils.convert_to_marking_list(marking)
+    marking = utils.convert_to_list(marking)
     utils.validate(obj, selectors)
 
     granular_markings = obj.get("granular_markings", [])
diff --git a/stix2/markings/object_markings.py b/stix2/markings/object_markings.py
index a775ddc..c39c036 100644
--- a/stix2/markings/object_markings.py
+++ b/stix2/markings/object_markings.py
@@ -31,7 +31,7 @@ def add_markings(obj, marking):
         A new version of the given SDO or SRO with specified markings added.
 
     """
-    marking = utils.convert_to_marking_list(marking)
+    marking = utils.convert_to_list(marking)
 
     object_markings = set(obj.get("object_marking_refs", []) + marking)
 
@@ -55,7 +55,7 @@ def remove_markings(obj, marking):
         A new version of the given SDO or SRO with specified markings removed.
 
     """
-    marking = utils.convert_to_marking_list(marking)
+    marking = utils.convert_to_list(marking)
 
     object_markings = obj.get("object_marking_refs", [])
 
@@ -121,7 +121,7 @@ def is_marked(obj, marking=None):
         provided marking refs match, True is returned.
 
     """
-    marking = utils.convert_to_marking_list(marking)
+    marking = utils.convert_to_list(marking)
     object_markings = obj.get("object_marking_refs", [])
 
     if marking:
diff --git a/stix2/markings/utils.py b/stix2/markings/utils.py
index 1154d19..d0d38bb 100644
--- a/stix2/markings/utils.py
+++ b/stix2/markings/utils.py
@@ -37,12 +37,6 @@ def _validate_selector(obj, selector):
         return True
 
 
-def _get_marking_id(marking):
-    if type(marking).__name__ is 'MarkingDefinition':  # avoid circular import
-        return marking.id
-    return marking
-
-
 def validate(obj, selectors):
     """Given an SDO or SRO, check that each selector is valid."""
     if selectors:
@@ -63,15 +57,6 @@ def convert_to_list(data):
             return [data]
 
 
-def convert_to_marking_list(data):
-    """Convert input into a list of marking identifiers."""
-    if data is not None:
-        if isinstance(data, list):
-            return [_get_marking_id(x) for x in data]
-        else:
-            return [_get_marking_id(data)]
-
-
 def compress_markings(granular_markings):
     """
     Compress granular markings list. If there is more than one marking
diff --git a/stix2/patterns.py b/stix2/patterns.py
index 03b7657..7858146 100644
--- a/stix2/patterns.py
+++ b/stix2/patterns.py
@@ -24,7 +24,7 @@ class TimestampConstant(_Constant):
         self.value = value
 
     def __str__(self):
-        return "t'%s'" % escape_quotes_and_backslashes(self.value)
+        return "'%s'" % escape_quotes_and_backslashes(self.value)
 
 
 class IntegerConstant(_Constant):
diff --git a/stix2/sdo.py b/stix2/sdo.py
index 53f965d..77c781a 100644
--- a/stix2/sdo.py
+++ b/stix2/sdo.py
@@ -6,7 +6,6 @@ import stix2
 
 from .base import _STIXBase
 from .common import ExternalReference, GranularMarking, KillChainPhase
-from .markings import MarkingsMixin
 from .observables import ObservableProperty
 from .properties import (BooleanProperty, IDProperty, IntegerProperty,
                          ListProperty, PatternProperty, ReferenceProperty,
@@ -14,11 +13,7 @@ from .properties import (BooleanProperty, IDProperty, IntegerProperty,
 from .utils import NOW
 
 
-class STIXDomainObject(_STIXBase, MarkingsMixin):
-    pass
-
-
-class AttackPattern(STIXDomainObject):
+class AttackPattern(_STIXBase):
 
     _type = 'attack-pattern'
     _properties = OrderedDict()
@@ -39,7 +34,7 @@ class AttackPattern(STIXDomainObject):
     ])
 
 
-class Campaign(STIXDomainObject):
+class Campaign(_STIXBase):
 
     _type = 'campaign'
     _properties = OrderedDict()
@@ -63,7 +58,7 @@ class Campaign(STIXDomainObject):
     ])
 
 
-class CourseOfAction(STIXDomainObject):
+class CourseOfAction(_STIXBase):
 
     _type = 'course-of-action'
     _properties = OrderedDict()
@@ -83,7 +78,7 @@ class CourseOfAction(STIXDomainObject):
     ])
 
 
-class Identity(STIXDomainObject):
+class Identity(_STIXBase):
 
     _type = 'identity'
     _properties = OrderedDict()
@@ -106,7 +101,7 @@ class Identity(STIXDomainObject):
     ])
 
 
-class Indicator(STIXDomainObject):
+class Indicator(_STIXBase):
 
     _type = 'indicator'
     _properties = OrderedDict()
@@ -130,7 +125,7 @@ class Indicator(STIXDomainObject):
     ])
 
 
-class IntrusionSet(STIXDomainObject):
+class IntrusionSet(_STIXBase):
 
     _type = 'intrusion-set'
     _properties = OrderedDict()
@@ -157,7 +152,7 @@ class IntrusionSet(STIXDomainObject):
     ])
 
 
-class Malware(STIXDomainObject):
+class Malware(_STIXBase):
 
     _type = 'malware'
     _properties = OrderedDict()
@@ -178,7 +173,7 @@ class Malware(STIXDomainObject):
     ])
 
 
-class ObservedData(STIXDomainObject):
+class ObservedData(_STIXBase):
 
     _type = 'observed-data'
     _properties = OrderedDict()
@@ -200,7 +195,7 @@ class ObservedData(STIXDomainObject):
     ])
 
 
-class Report(STIXDomainObject):
+class Report(_STIXBase):
 
     _type = 'report'
     _properties = OrderedDict()
@@ -222,7 +217,7 @@ class Report(STIXDomainObject):
     ])
 
 
-class ThreatActor(STIXDomainObject):
+class ThreatActor(_STIXBase):
 
     _type = 'threat-actor'
     _properties = OrderedDict()
@@ -250,7 +245,7 @@ class ThreatActor(STIXDomainObject):
     ])
 
 
-class Tool(STIXDomainObject):
+class Tool(_STIXBase):
 
     _type = 'tool'
     _properties = OrderedDict()
@@ -272,7 +267,7 @@ class Tool(STIXDomainObject):
     ])
 
 
-class Vulnerability(STIXDomainObject):
+class Vulnerability(_STIXBase):
 
     _type = 'vulnerability'
     _properties = OrderedDict()
@@ -321,7 +316,7 @@ def CustomObject(type='x-custom-type', properties=None):
 
     def custom_builder(cls):
 
-        class _Custom(cls, STIXDomainObject):
+        class _Custom(cls, _STIXBase):
             _type = type
             _properties = OrderedDict()
             _properties.update([
diff --git a/stix2/sources/filesystem.py b/stix2/sources/filesystem.py
index c45f281..d80468f 100644
--- a/stix2/sources/filesystem.py
+++ b/stix2/sources/filesystem.py
@@ -102,7 +102,7 @@ class FileSystemSink(DataSink):
             # adding json encoded string of STIX content
             stix_data = parse(stix_data)
             if stix_data["type"] == "bundle":
-                for stix_obj in stix_data:
+                for stix_obj in stix_data["objects"]:
                     self.add(stix_obj)
             else:
                 self.add(stix_data)
@@ -113,7 +113,7 @@ class FileSystemSink(DataSink):
                 self.add(stix_obj)
 
         else:
-            raise ValueError("stix_data must be a STIX object(or list of, json formatted STIX(or list of) or a json formatted STIX bundle")
+            raise ValueError("stix_data must be a STIX object(or list of), json formatted STIX(or list of) or a json formatted STIX bundle")
 
 
 class FileSystemSource(DataSource):
@@ -159,9 +159,13 @@ class FileSystemSource(DataSource):
 
         all_data = self.query(query=query, _composite_filters=_composite_filters)
 
-        stix_obj = sorted(all_data, key=lambda k: k['modified'])[0]
+        if all_data:
+            stix_obj = sorted(all_data, key=lambda k: k['modified'])[0]
+            stix_obj = parse(stix_obj)
+        else:
+            stix_obj = None
 
-        return parse(stix_obj)
+        return stix_obj
 
     def all_versions(self, stix_id, _composite_filters=None):
         """retrieve STIX object from file directory via STIX ID, all versions
diff --git a/stix2/sources/filters.py b/stix2/sources/filters.py
index a565006..3b476a3 100644
--- a/stix2/sources/filters.py
+++ b/stix2/sources/filters.py
@@ -250,9 +250,10 @@ def check_external_references_filter(filter_, stix_obj):
     for er in stix_obj["external_references"]:
         # grab er property name from filter field
         filter_field = filter_.field.split(".")[1]
-        r = _string_filter(filter_, er[filter_field])
-        if r:
-            return r
+        if filter_field in er:
+            r = _string_filter(filter_, er[filter_field])
+            if r:
+                return r
     return False
 
 
diff --git a/stix2/sources/taxii.py b/stix2/sources/taxii.py
index 63d5226..10ec745 100644
--- a/stix2/sources/taxii.py
+++ b/stix2/sources/taxii.py
@@ -130,10 +130,11 @@ class TAXIICollectionSource(DataSource):
 
         if len(stix_obj):
             stix_obj = stix_obj[0]
+            stix_obj = parse(stix_obj)
         else:
             stix_obj = None
 
-        return parse(stix_obj)
+        return stix_obj
 
     def all_versions(self, stix_id, _composite_filters=None):
         """retrieve STIX object from local/remote TAXII Collection
diff --git a/stix2/sro.py b/stix2/sro.py
index 4fa0465..af483bc 100644
--- a/stix2/sro.py
+++ b/stix2/sro.py
@@ -4,18 +4,13 @@ from collections import OrderedDict
 
 from .base import _STIXBase
 from .common import ExternalReference, GranularMarking
-from .markings import MarkingsMixin
 from .properties import (BooleanProperty, IDProperty, IntegerProperty,
                          ListProperty, ReferenceProperty, StringProperty,
                          TimestampProperty, TypeProperty)
 from .utils import NOW
 
 
-class STIXRelationshipObject(_STIXBase, MarkingsMixin):
-    pass
-
-
-class Relationship(STIXRelationshipObject):
+class Relationship(_STIXBase):
 
     _type = 'relationship'
     _properties = OrderedDict()
@@ -50,7 +45,7 @@ class Relationship(STIXRelationshipObject):
         super(Relationship, self).__init__(**kwargs)
 
 
-class Sighting(STIXRelationshipObject):
+class Sighting(_STIXBase):
     _type = 'sighting'
     _properties = OrderedDict()
     _properties.update([


Test patch:
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22.json
new file mode 100755
index 0000000..47dd5f8
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22.json
@@ -0,0 +1,42 @@
+{
+    "id": "bundle--f68640b4-0cdc-42ae-b176-def1754a1ea0",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:19.73501Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Credential dumping is the process of obtaining account login and password information from the operating system and software. Credentials can be used to perform Windows Credential Editor, Mimikatz, and gsecdump. These tools are in use by both professional security testers and adversaries.\n\nPlaintext passwords can be obtained using tools such as Mimikatz to extract passwords stored by the Local Security Authority (LSA). If smart cards are used to authenticate to a domain using a personal identification number (PIN), then that PIN is also cached as a result and may be dumped.Mimikatz access the LSA Subsystem Service (LSASS) process by opening the process, locating the LSA secrets key, and decrypting the sections in memory where credential details are stored. Credential dumpers may also use methods for reflective DLL Injection to reduce potential indicators of malicious activity.\n\nNTLM hash dumpers open the Security Accounts Manager (SAM) on the local file system (%SystemRoot%/system32/config/SAM) or create a dump of the Registry SAM key to access stored account password hashes. Some hash dumpers will open the local file system as a device and parse to the SAM table to avoid file access defenses. Others will make an in-memory copy of the SAM table before reading hashes. Detection of compromised Legitimate Credentials in-use by adversaries may help as well. \n\nOn Windows 8.1 and Windows Server 2012 R2, monitor Windows Logs for LSASS.exe creation to verify that LSASS started as a protected process.\n\nMonitor processes and command-line arguments for program execution that may be indicative of credential dumping. Remote access tools may contain built-in features or incorporate existing tools like Mimikatz. PowerShell scripts also exist that contain credential dumping functionality, such as PowerSploit's Invoke-Mimikatz module,[[Citation: Powersploit]] which may require additional logging features to be configured in the operating system to collect necessary information for analysis.\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: API monitoring, Process command-line parameters, Process monitoring, PowerShell logs",
+            "external_references": [
+                {
+                    "external_id": "T1003",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1003"
+                },
+                {
+                    "description": "Delpy, B. (2014, September 14). Mimikatz module ~ sekurlsa. Retrieved January 10, 2016.",
+                    "source_name": "Github Mimikatz Module sekurlsa",
+                    "url": "https://github.com/gentilkiwi/mimikatz/wiki/module-~-sekurlsa"
+                },
+                {
+                    "description": "PowerSploit. (n.d.).  Retrieved December 4, 2014.",
+                    "source_name": "Powersploit",
+                    "url": "https://github.com/mattifestation/PowerSploit"
+                }
+            ],
+            "id": "attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "credential-access"
+                }
+            ],
+            "modified": "2017-05-31T21:30:19.73501Z",
+            "name": "Credential Dumping",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--0f20e3cb-245b-4a61-8a91-2d93f7cb0e9b.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--0f20e3cb-245b-4a61-8a91-2d93f7cb0e9b.json
new file mode 100755
index 0000000..13f900f
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--0f20e3cb-245b-4a61-8a91-2d93f7cb0e9b.json
@@ -0,0 +1,37 @@
+{
+    "id": "bundle--b07d6fd6-7cc5-492d-a1eb-9ba956b329d5",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:26.496201Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Rootkits are programs that hide the existence of malware by intercepting and modifying operating system API calls that supply system information. Rootkits or rootkit enabling functionality may reside at the user or kernel level in the operating system or lower, to include a Hypervisor, Master Boot Record, or the Basic Input/Output System.[[Citation: Wikipedia Rootkit]]\n\nAdversaries may use rootkits to hide the presence of programs, files, network connections, services, drivers, and other system components.\n\nDetection: Some rootkit protections may be built into anti-virus or operating system software. There are dedicated rootkit detection tools that look for specific types of rootkit behavior. Monitor for the existence of unrecognized DLLs, devices, services, and changes to the MBR.[[Citation: Wikipedia Rootkit]]\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: BIOS, MBR, System calls",
+            "external_references": [
+                {
+                    "external_id": "T1014",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1014"
+                },
+                {
+                    "description": "Wikipedia. (2016, June 1). Rootkit. Retrieved June 2, 2016.",
+                    "source_name": "Wikipedia Rootkit",
+                    "url": "https://en.wikipedia.org/wiki/Rootkit"
+                }
+            ],
+            "id": "attack-pattern--0f20e3cb-245b-4a61-8a91-2d93f7cb0e9b",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "defense-evasion"
+                }
+            ],
+            "modified": "2017-05-31T21:30:26.496201Z",
+            "name": "Rootkit",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--774a3188-6ba9-4dc4-879d-d54ee48a5ce9.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--774a3188-6ba9-4dc4-879d-d54ee48a5ce9.json
new file mode 100755
index 0000000..db57e2c
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--774a3188-6ba9-4dc4-879d-d54ee48a5ce9.json
@@ -0,0 +1,32 @@
+{
+    "id": "bundle--1a854c96-639e-4771-befb-e7b960a65974",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:29.45894Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Data, such as sensitive documents, may be exfiltrated through the use of automated processing or Scripting after being gathered during Exfiltration Over Command and Control Channel and Exfiltration Over Alternative Protocol.\n\nDetection: Monitor process file access patterns and network behavior. Unrecognized processes or scripts that appear to be traversing file systems and sending network traffic may be suspicious.\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: File monitoring, Process monitoring, Process use of network",
+            "external_references": [
+                {
+                    "external_id": "T1020",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1020"
+                }
+            ],
+            "id": "attack-pattern--774a3188-6ba9-4dc4-879d-d54ee48a5ce9",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "exfiltration"
+                }
+            ],
+            "modified": "2017-05-31T21:30:29.45894Z",
+            "name": "Automated Exfiltration",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475.json
new file mode 100755
index 0000000..d48092d
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475.json
@@ -0,0 +1,32 @@
+{
+    "id": "bundle--33e3e33a-38b8-4a37-9455-5b8c82d3b10a",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:45.139269Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Adversaries may attempt to get a listing of network connections to or from the compromised system.\nUtilities and commands that acquire this information include netstat, \"net use,\" and \"net session\" with Net.\n\nDetection: System and network discovery techniques normally occur throughout an operation as an adversary learns the environment. Data and events should not be viewed in isolation, but as part of a chain of behavior that could lead to other activities, such as Windows Management Instrumentation and PowerShell.\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: Process command-line parameters, Process monitoring",
+            "external_references": [
+                {
+                    "external_id": "T1049",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1049"
+                }
+            ],
+            "id": "attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "discovery"
+                }
+            ],
+            "modified": "2017-05-31T21:30:45.139269Z",
+            "name": "Local Network Connections Discovery",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--ae676644-d2d2-41b7-af7e-9bed1b55898c.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--ae676644-d2d2-41b7-af7e-9bed1b55898c.json
new file mode 100755
index 0000000..031419e
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--ae676644-d2d2-41b7-af7e-9bed1b55898c.json
@@ -0,0 +1,32 @@
+{
+    "id": "bundle--a87938c5-cc1e-4e06-a8a3-b10243ae397d",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:41.022897Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Sensitive data can be collected from remote systems via shared network drives (host shared directory, network file server, etc.) that are accessible from the current system prior to cmd may be used to gather information.\n\nDetection: Monitor processes and command-line arguments for actions that could be taken to collect files from a network share. Remote access tools with built-in features may interact directly with the Windows API to gather data. Data may also be acquired through Windows system management tools such as Windows Management Instrumentation and PowerShell.\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: File monitoring, Process monitoring, Process command-line parameters",
+            "external_references": [
+                {
+                    "external_id": "T1039",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1039"
+                }
+            ],
+            "id": "attack-pattern--ae676644-d2d2-41b7-af7e-9bed1b55898c",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "collection"
+                }
+            ],
+            "modified": "2017-05-31T21:30:41.022897Z",
+            "name": "Data from Network Shared Drive",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/attack-pattern/attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a.json b/stix2/test/stix2_data/attack-pattern/attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a.json
new file mode 100755
index 0000000..67c380c
--- /dev/null
+++ b/stix2/test/stix2_data/attack-pattern/attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a.json
@@ -0,0 +1,32 @@
+{
+    "id": "bundle--5ddaeff9-eca7-4094-9e65-4f53da21a444",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:32.662702Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Adversaries may attempt to make an executable or file difficult to discover or analyze by encrypting, encoding, or otherwise obfuscating its contents on the system.\n\nDetection: Detection of file obfuscation is difficult unless artifacts are left behind by the obfuscation process that are uniquely detectable with a signature. If detection of the obfuscation itself is not possible, it may be possible to detect the malicious activity that caused the obfuscated file (for example, the method that was used to write, read, or modify the file on the file system).\n\nPlatforms: Windows Server 2003, Windows Server 2008, Windows Server 2012, Windows XP, Windows 7, Windows 8, Windows Server 2003 R2, Windows Server 2008 R2, Windows Server 2012 R2, Windows Vista, Windows 8.1\n\nData Sources: Network protocol analysis, Process use of network, Binary file metadata, File monitoring, Malware reverse engineering",
+            "external_references": [
+                {
+                    "external_id": "T1027",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Technique/T1027"
+                }
+            ],
+            "id": "attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a",
+            "kill_chain_phases": [
+                {
+                    "kill_chain_name": "mitre-attack",
+                    "phase_name": "defense-evasion"
+                }
+            ],
+            "modified": "2017-05-31T21:30:32.662702Z",
+            "name": "Obfuscated Files or Information",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "attack-pattern"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/course-of-action/course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f.json b/stix2/test/stix2_data/course-of-action/course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f.json
new file mode 100755
index 0000000..bf14aa7
--- /dev/null
+++ b/stix2/test/stix2_data/course-of-action/course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f.json
@@ -0,0 +1,16 @@
+{
+    "id": "bundle--a42d26fe-c938-4074-a1b3-50d852e6f0bd",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:26.495974Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Identify potentially malicious software that may contain rootkit functionality, and audit and/or block it by using whitelisting[[CiteRef::Beechey 2010]] tools, like AppLocker,[[CiteRef::Windows Commands JPCERT]][[CiteRef::NSA MS AppLocker]] or Software Restriction Policies[[CiteRef::Corio 2008]] where appropriate.[[CiteRef::TechNet Applocker vs SRP]]",
+            "id": "course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f",
+            "modified": "2017-05-31T21:30:26.495974Z",
+            "name": "Rootkit Mitigation",
+            "type": "course-of-action"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/course-of-action/course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd.json b/stix2/test/stix2_data/course-of-action/course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd.json
new file mode 100755
index 0000000..5bfb8bb
--- /dev/null
+++ b/stix2/test/stix2_data/course-of-action/course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd.json
@@ -0,0 +1,16 @@
+{
+    "id": "bundle--2ed6ab6a-ca68-414f-8493-e4db8b75dd51",
+    "objects": [
+        {
+            "created": "2017-05-31T21:30:41.022744Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Identify unnecessary system utilities or potentially malicious software that may be used to collect data from a network share, and audit and/or block them by using whitelisting[[CiteRef::Beechey 2010]] tools, like AppLocker,[[CiteRef::Windows Commands JPCERT]][[CiteRef::NSA MS AppLocker]] or Software Restriction Policies[[CiteRef::Corio 2008]] where appropriate.[[CiteRef::TechNet Applocker vs SRP]]",
+            "id": "course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd",
+            "modified": "2017-05-31T21:30:41.022744Z",
+            "name": "Data from Network Shared Drive Mitigation",
+            "type": "course-of-action"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/identity/identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5.json b/stix2/test/stix2_data/identity/identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5.json
new file mode 100755
index 0000000..77d4464
--- /dev/null
+++ b/stix2/test/stix2_data/identity/identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5.json
@@ -0,0 +1,15 @@
+{
+    "id": "bundle--81884287-2548-47fc-a997-39489ddd5462",
+    "objects": [
+        {
+            "created": "2017-06-01T00:00:00Z",
+            "id": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "identity_class": "organization",
+            "modified": "2017-06-01T00:00:00Z",
+            "name": "The MITRE Corporation",
+            "type": "identity"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/intrusion-set/intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064.json b/stix2/test/stix2_data/intrusion-set/intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064.json
new file mode 100755
index 0000000..10ef3a5
--- /dev/null
+++ b/stix2/test/stix2_data/intrusion-set/intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064.json
@@ -0,0 +1,54 @@
+{
+    "id": "bundle--7790ee4c-2d57-419a-bc9d-8805b5bb4118",
+    "objects": [
+        {
+            "aliases": [
+                "Deep Panda",
+                "Shell Crew",
+                "WebMasters",
+                "KungFu Kittens",
+                "PinkPanther",
+                "Black Vine"
+            ],
+            "created": "2017-05-31T21:31:49.412497Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Deep Panda is a suspected Chinese threat group known to target many industries, including government, defense, financial, and telecommunications.Deep Panda.Deep Panda also appears to be known as Black Vine based on the attribution of both group names to the Anthem intrusion.[[Citation: Symantec Black Vine]]",
+            "external_references": [
+                {
+                    "external_id": "G0009",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Group/G0009"
+                },
+                {
+                    "description": "Alperovitch, D. (2014, July 7). Deep in Thought: Chinese Targeting of National Security Think Tanks. Retrieved November 12, 2014.",
+                    "source_name": "Alperovitch 2014",
+                    "url": "http://blog.crowdstrike.com/deep-thought-chinese-targeting-national-security-think-tanks/"
+                },
+                {
+                    "description": "DiMaggio, J.. (2015, August 6). The Black Vine cyberespionage group. Retrieved January 26, 2016.",
+                    "source_name": "Symantec Black Vine",
+                    "url": "http://www.symantec.com/content/en/us/enterprise/media/security%20response/whitepapers/the-black-vine-cyberespionage-group.pdf"
+                },
+                {
+                    "description": "RSA Incident Response. (2014, January). RSA Incident Response Emerging Threat Profile: Shell Crew. Retrieved January 14, 2016.",
+                    "source_name": "RSA Shell Crew",
+                    "url": "https://www.emc.com/collateral/white-papers/h12756-wp-shell-crew.pdf"
+                },
+                {
+                    "description": "ThreatConnect Research Team. (2015, February 27). The Anthem Hack: All Roads Lead to China. Retrieved January 26, 2016.",
+                    "source_name": "ThreatConnect Anthem",
+                    "url": "https://www.threatconnect.com/the-anthem-hack-all-roads-lead-to-china/"
+                }
+            ],
+            "id": "intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064",
+            "modified": "2017-05-31T21:31:49.412497Z",
+            "name": "Deep Panda",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "intrusion-set"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/intrusion-set/intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a.json b/stix2/test/stix2_data/intrusion-set/intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a.json
new file mode 100755
index 0000000..84b75b1
--- /dev/null
+++ b/stix2/test/stix2_data/intrusion-set/intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a.json
@@ -0,0 +1,44 @@
+{
+    "id": "bundle--96a6ea7a-fcff-4aab-925b-a494bcdf0480",
+    "objects": [
+        {
+            "aliases": [
+                "DragonOK"
+            ],
+            "created": "2017-05-31T21:31:53.197755Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "DragonOK is a threat group that has targeted Japanese organizations with phishing emails. Due to overlapping TTPs, including similar custom tools, DragonOK is thought to have a direct or indirect relationship with the threat group Moafee. [[Citation: Operation Quantum Entanglement]][[Citation: Symbiotic APT Groups]] It is known to use a variety of malware, including Sysget/HelloBridge, PlugX, PoisonIvy, FormerFirstRat, NFlog, and NewCT. [[Citation: New DragonOK]]",
+            "external_references": [
+                {
+                    "external_id": "G0017",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Group/G0017"
+                },
+                {
+                    "description": "Haq, T., Moran, N., Vashisht, S., Scott, M. (2014, September). OPERATION QUANTUM ENTANGLEMENT. Retrieved November 4, 2015.",
+                    "source_name": "Operation Quantum Entanglement",
+                    "url": "https://www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/wp-operation-quantum-entanglement.pdf"
+                },
+                {
+                    "description": "Haq, T. (2014, October). An Insight into Symbiotic APT Groups. Retrieved November 4, 2015.",
+                    "source_name": "Symbiotic APT Groups",
+                    "url": "https://dl.mandiant.com/EE/library/MIRcon2014/MIRcon%202014%20R&D%20Track%20Insight%20into%20Symbiotic%20APT.pdf"
+                },
+                {
+                    "description": "Miller-Osborn, J., Grunzweig, J.. (2015, April). Unit 42 Identifies New DragonOK Backdoor Malware Deployed Against Japanese Targets. Retrieved November 4, 2015.",
+                    "source_name": "New DragonOK",
+                    "url": "http://researchcenter.paloaltonetworks.com/2015/04/unit-42-identifies-new-dragonok-backdoor-malware-deployed-against-japanese-targets/"
+                }
+            ],
+            "id": "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a",
+            "modified": "2017-05-31T21:31:53.197755Z",
+            "name": "DragonOK",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "intrusion-set"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/malware/malware--6b616fc1-1505-48e3-8b2c-0d19337bff38.json b/stix2/test/stix2_data/malware/malware--6b616fc1-1505-48e3-8b2c-0d19337bff38.json
new file mode 100755
index 0000000..669f00c
--- /dev/null
+++ b/stix2/test/stix2_data/malware/malware--6b616fc1-1505-48e3-8b2c-0d19337bff38.json
@@ -0,0 +1,34 @@
+{
+    "id": "bundle--f64de948-7067-4534-8018-85f03d470625",
+    "objects": [
+        {
+            "created": "2017-05-31T21:32:58.226477Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
+            "external_references": [
+                {
+                    "external_id": "S0090",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0090"
+                },
+                {
+                    "description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
+                    "source_name": "Palo Alto Rover",
+                    "url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
+                }
+            ],
+            "id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
+            "labels": [
+                "malware"
+            ],
+            "modified": "2017-05-31T21:32:58.226477Z",
+            "name": "Rover",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "malware"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/malware/malware--92ec0cbd-2c30-44a2-b270-73f4ec949841.json b/stix2/test/stix2_data/malware/malware--92ec0cbd-2c30-44a2-b270-73f4ec949841.json
new file mode 100755
index 0000000..9eaf8ad
--- /dev/null
+++ b/stix2/test/stix2_data/malware/malware--92ec0cbd-2c30-44a2-b270-73f4ec949841.json
@@ -0,0 +1,34 @@
+{
+    "id": "bundle--c633942b-545c-4c87-91b7-9fe5740365e0",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:26.565056Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "RTM is custom malware written in Delphi. It is used by the group of the same name (RTM).[[Citation: ESET RTM Feb 2017]]",
+            "external_references": [
+                {
+                    "external_id": "S0148",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0148"
+                },
+                {
+                    "description": "Faou, M. and Boutin, J.. (2017, February). Read The Manual: A Guide to the RTM Banking Trojan. Retrieved March 9, 2017.",
+                    "source_name": "ESET RTM Feb 2017",
+                    "url": "https://www.welivesecurity.com/wp-content/uploads/2017/02/Read-The-Manual.pdf"
+                }
+            ],
+            "id": "malware--92ec0cbd-2c30-44a2-b270-73f4ec949841",
+            "labels": [
+                "malware"
+            ],
+            "modified": "2017-05-31T21:33:26.565056Z",
+            "name": "RTM",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "malware"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/malware/malware--96b08451-b27a-4ff6-893f-790e26393a8e.json b/stix2/test/stix2_data/malware/malware--96b08451-b27a-4ff6-893f-790e26393a8e.json
new file mode 100755
index 0000000..224f6a9
--- /dev/null
+++ b/stix2/test/stix2_data/malware/malware--96b08451-b27a-4ff6-893f-790e26393a8e.json
@@ -0,0 +1,34 @@
+{
+    "id": "bundle--09ce4338-8741-4fcf-9738-d216c8e40974",
+    "objects": [
+        {
+            "created": "2017-05-31T21:32:48.482655Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Sakula is a remote access tool (RAT) that first surfaced in 2012 and was used in intrusions throughout 2015.[[Citation: Dell Sakula]]\n\nAliases: Sakula, Sakurel, VIPER",
+            "external_references": [
+                {
+                    "external_id": "S0074",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0074"
+                },
+                {
+                    "description": "Dell SecureWorks Counter Threat Unit Threat Intelligence. (2015, July 30). Sakula Malware Family. Retrieved January 26, 2016.",
+                    "source_name": "Dell Sakula",
+                    "url": "http://www.secureworks.com/cyber-threat-intelligence/threats/sakula-malware-family/"
+                }
+            ],
+            "id": "malware--96b08451-b27a-4ff6-893f-790e26393a8e",
+            "labels": [
+                "malware"
+            ],
+            "modified": "2017-05-31T21:32:48.482655Z",
+            "name": "Sakula",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "malware"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/malware/malware--b42378e0-f147-496f-992a-26a49705395b.json b/stix2/test/stix2_data/malware/malware--b42378e0-f147-496f-992a-26a49705395b.json
new file mode 100755
index 0000000..3e1c870
--- /dev/null
+++ b/stix2/test/stix2_data/malware/malware--b42378e0-f147-496f-992a-26a49705395b.json
@@ -0,0 +1,34 @@
+{
+    "id": "bundle--611947ce-ae3b-4fdb-b297-aed8eab22e4f",
+    "objects": [
+        {
+            "created": "2017-05-31T21:32:15.263882Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "PoisonIvy is a popular remote access tool (RAT) that has been used by many groups.[[Citation: FireEye Poison Ivy]]\n\nAliases: PoisonIvy, Poison Ivy",
+            "external_references": [
+                {
+                    "external_id": "S0012",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0012"
+                },
+                {
+                    "description": "FireEye. (2014). POISON IVY: Assessing Damage and Extracting Intelligence. Retrieved November 12, 2014.",
+                    "source_name": "FireEye Poison Ivy",
+                    "url": "https://www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/rpt-poison-ivy.pdf"
+                }
+            ],
+            "id": "malware--b42378e0-f147-496f-992a-26a49705395b",
+            "labels": [
+                "malware"
+            ],
+            "modified": "2017-05-31T21:32:15.263882Z",
+            "name": "PoisonIvy",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "malware"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/marking-definition/marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168.json b/stix2/test/stix2_data/marking-definition/marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168.json
new file mode 100755
index 0000000..bcae183
--- /dev/null
+++ b/stix2/test/stix2_data/marking-definition/marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168.json
@@ -0,0 +1,16 @@
+{
+    "id": "bundle--0f4a3025-7788-4f25-a0c7-26171056dfae",
+    "objects": [
+        {
+            "created": "2017-06-01T00:00:00Z",
+            "definition": {
+                "statement": "Copyright 2017, The MITRE Corporation"
+            },
+            "definition_type": "statement",
+            "id": "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168",
+            "type": "marking-definition"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--0d4a7788-7f3b-4df8-a498-31a38003c883.json b/stix2/test/stix2_data/relationship/relationship--0d4a7788-7f3b-4df8-a498-31a38003c883.json
new file mode 100755
index 0000000..ac59925
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--0d4a7788-7f3b-4df8-a498-31a38003c883.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--7e715462-dd9d-40b9-968a-10ef0ecf126d",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.182784Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--0d4a7788-7f3b-4df8-a498-31a38003c883",
+            "modified": "2017-05-31T21:33:27.182784Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a",
+            "target_ref": "malware--92ec0cbd-2c30-44a2-b270-73f4ec949841",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--0e55ee98-0c6d-43d4-b424-b18a0036b227.json b/stix2/test/stix2_data/relationship/relationship--0e55ee98-0c6d-43d4-b424-b18a0036b227.json
new file mode 100755
index 0000000..ee97edf
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--0e55ee98-0c6d-43d4-b424-b18a0036b227.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--a53eef35-abfc-4bcd-b84e-a048f7b4a9bf",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.082801Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--0e55ee98-0c6d-43d4-b424-b18a0036b227",
+            "modified": "2017-05-31T21:33:27.082801Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22",
+            "target_ref": "tool--242f3da3-4425-4d11-8f5c-b842886da966",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--1e91cd45-a725-4965-abe3-700694374432.json b/stix2/test/stix2_data/relationship/relationship--1e91cd45-a725-4965-abe3-700694374432.json
new file mode 100755
index 0000000..ff0d8cc
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--1e91cd45-a725-4965-abe3-700694374432.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--0b9f6412-314f-44e3-8779-9738c9578ef5",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.018782Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--1e91cd45-a725-4965-abe3-700694374432",
+            "modified": "2017-05-31T21:33:27.018782Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "mitigates",
+            "source_ref": "course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f",
+            "target_ref": "attack-pattern--0f20e3cb-245b-4a61-8a91-2d93f7cb0e9b",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--3a3084f9-0302-4fd5-9b8a-e0db10f5345e.json b/stix2/test/stix2_data/relationship/relationship--3a3084f9-0302-4fd5-9b8a-e0db10f5345e.json
new file mode 100755
index 0000000..36d1482
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--3a3084f9-0302-4fd5-9b8a-e0db10f5345e.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--6d5b04a8-efb2-4179-990e-74f1dcc76e0c",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.100701Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--3a3084f9-0302-4fd5-9b8a-e0db10f5345e",
+            "modified": "2017-05-31T21:33:27.100701Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475",
+            "target_ref": "tool--03342581-f790-4f03-ba41-e82e67392e23",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--3a3ed0b2-0c38-441f-ac40-53b873e545d1.json b/stix2/test/stix2_data/relationship/relationship--3a3ed0b2-0c38-441f-ac40-53b873e545d1.json
new file mode 100755
index 0000000..888cc3b
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--3a3ed0b2-0c38-441f-ac40-53b873e545d1.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--a7efc025-040d-49c7-bf97-e5a1120ecacc",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.143973Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--3a3ed0b2-0c38-441f-ac40-53b873e545d1",
+            "modified": "2017-05-31T21:33:27.143973Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "attack-pattern--774a3188-6ba9-4dc4-879d-d54ee48a5ce9",
+            "target_ref": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--592d0c31-e61f-495e-a60e-70d7be59a719.json b/stix2/test/stix2_data/relationship/relationship--592d0c31-e61f-495e-a60e-70d7be59a719.json
new file mode 100755
index 0000000..d9078d1
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--592d0c31-e61f-495e-a60e-70d7be59a719.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--9f013d47-7704-41c2-9749-23d0d94af94d",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.021562Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--592d0c31-e61f-495e-a60e-70d7be59a719",
+            "modified": "2017-05-31T21:33:27.021562Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "mitigates",
+            "source_ref": "course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd",
+            "target_ref": "attack-pattern--ae676644-d2d2-41b7-af7e-9bed1b55898c",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1.json b/stix2/test/stix2_data/relationship/relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1.json
new file mode 100755
index 0000000..ef1c4b2
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--15167b24-4cee-4c96-a140-32a6c37df4b4",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.044387Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1",
+            "modified": "2017-05-31T21:33:27.044387Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064",
+            "target_ref": "malware--96b08451-b27a-4ff6-893f-790e26393a8e",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/relationship/relationship--8797579b-e3be-4209-a71b-255a4d08243d.json b/stix2/test/stix2_data/relationship/relationship--8797579b-e3be-4209-a71b-255a4d08243d.json
new file mode 100755
index 0000000..1f20179
--- /dev/null
+++ b/stix2/test/stix2_data/relationship/relationship--8797579b-e3be-4209-a71b-255a4d08243d.json
@@ -0,0 +1,20 @@
+{
+    "id": "bundle--ff845dca-7036-416f-aae0-95030994c49f",
+    "objects": [
+        {
+            "created": "2017-05-31T21:33:27.051532Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "id": "relationship--8797579b-e3be-4209-a71b-255a4d08243d",
+            "modified": "2017-05-31T21:33:27.051532Z",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "relationship_type": "uses",
+            "source_ref": "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a",
+            "target_ref": "malware--b42378e0-f147-496f-992a-26a49705395b",
+            "type": "relationship"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
\ No newline at end of file
diff --git a/stix2/test/stix2_data/tool/tool--03342581-f790-4f03-ba41-e82e67392e23.json b/stix2/test/stix2_data/tool/tool--03342581-f790-4f03-ba41-e82e67392e23.json
new file mode 100755
index 0000000..9d47880
--- /dev/null
+++ b/stix2/test/stix2_data/tool/tool--03342581-f790-4f03-ba41-e82e67392e23.json
@@ -0,0 +1,39 @@
+{
+    "id": "bundle--d8826afc-1561-4362-a4e3-05a4c2c3ac3c",
+    "objects": [
+        {
+            "created": "2017-05-31T21:32:31.601148Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "The Net utility is a component of the Windows operating system. It is used in command-line operations for control of users, groups, services, and network connections.Net has a great deal of functionality,[[Citation: Savill 1999]] much of which is useful for an adversary, such as gathering system and network information for [[Discovery]], moving laterally through [[Windows admin shares]] using <code>net use</code> commands, and interacting with services.\n\nAliases: Net, net.exe",
+            "external_references": [
+                {
+                    "external_id": "S0039",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0039"
+                },
+                {
+                    "description": "Microsoft. (2006, October 18). Net.exe Utility. Retrieved September 22, 2015.",
+                    "source_name": "Microsoft Net Utility",
+                    "url": "https://msdn.microsoft.com/en-us/library/aa939914"
+                },
+                {
+                    "description": "Savill, J. (1999, March 4). Net.exe reference. Retrieved September 22, 2015.",
+                    "source_name": "Savill 1999",
+                    "url": "http://windowsitpro.com/windows/netexe-reference"
+                }
+            ],
+            "id": "tool--03342581-f790-4f03-ba41-e82e67392e23",
+            "labels": [
+                "tool"
+            ],
+            "modified": "2017-05-31T21:32:31.601148Z",
+            "name": "Net",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "tool"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/stix2_data/tool/tool--242f3da3-4425-4d11-8f5c-b842886da966.json b/stix2/test/stix2_data/tool/tool--242f3da3-4425-4d11-8f5c-b842886da966.json
new file mode 100755
index 0000000..281888e
--- /dev/null
+++ b/stix2/test/stix2_data/tool/tool--242f3da3-4425-4d11-8f5c-b842886da966.json
@@ -0,0 +1,34 @@
+{
+    "id": "bundle--7dbde18f-6f14-4bf0-8389-505c89d6d5a6",
+    "objects": [
+        {
+            "created": "2017-05-31T21:32:12.684914Z",
+            "created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
+            "description": "Windows Credential Editor is a password dumping tool.[[Citation: Amplia WCE]]\n\nAliases: Windows Credential Editor, WCE",
+            "external_references": [
+                {
+                    "external_id": "S0005",
+                    "source_name": "mitre-attack",
+                    "url": "https://attack.mitre.org/wiki/Software/S0005"
+                },
+                {
+                    "description": "Amplia Security. (n.d.). Windows Credentials Editor (WCE) F.A.Q.. Retrieved December 17, 2015.",
+                    "source_name": "Amplia WCE",
+                    "url": "http://www.ampliasecurity.com/research/wcefaq.html"
+                }
+            ],
+            "id": "tool--242f3da3-4425-4d11-8f5c-b842886da966",
+            "labels": [
+                "tool"
+            ],
+            "modified": "2017-05-31T21:32:12.684914Z",
+            "name": "Windows Credential Editor",
+            "object_marking_refs": [
+                "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
+            ],
+            "type": "tool"
+        }
+    ],
+    "spec_version": "2.0",
+    "type": "bundle"
+}
diff --git a/stix2/test/test_data_sources.py b/stix2/test/test_data_sources.py
index e34d603..35e1e23 100644
--- a/stix2/test/test_data_sources.py
+++ b/stix2/test/test_data_sources.py
@@ -1,13 +1,17 @@
+import os
+
 import pytest
 from taxii2client import Collection
 
+from stix2 import (Campaign, FileSystemSink, FileSystemSource, FileSystemStore,
+                   Filter, MemorySource, MemoryStore)
 from stix2.sources import (CompositeDataSource, DataSink, DataSource,
                            DataStore, make_id, taxii)
-from stix2.sources.filters import Filter, apply_common_filters
-from stix2.sources.memory import MemorySource, MemoryStore
+from stix2.sources.filters import apply_common_filters
 from stix2.utils import deduplicate
 
 COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/'
+FS_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "stix2_data")
 
 
 class MockTAXIIClient(object):
@@ -508,3 +512,207 @@ def test_composite_datasource_operations():
     # STIX_OBJS2 has indicator with later time, one with different id, one with
     # original time in STIX_OBJS1
     assert len(results) == 3
+
+
+def test_filesytem_source():
+    # creation
+    fs_source = FileSystemSource(FS_PATH)
+    assert fs_source.stix_dir == FS_PATH
+
+    # get object
+    mal = fs_source.get("malware--6b616fc1-1505-48e3-8b2c-0d19337bff38")
+    assert mal.id == "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38"
+    assert mal.name == "Rover"
+
+    # all versions - (currently not a true all versions call as FileSystem cant have multiple versions)
+    id_ = fs_source.get("identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5")
+    assert id_.id == "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5"
+    assert id_.name == "The MITRE Corporation"
+    assert id_.type == "identity"
+
+    # query
+    intrusion_sets = fs_source.query([Filter("type", '=', "intrusion-set")])
+    assert len(intrusion_sets) == 2
+    assert "intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064" in [is_.id for is_ in intrusion_sets]
+    assert "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a" in [is_.id for is_ in intrusion_sets]
+
+    is_1 = [is_ for is_ in intrusion_sets if is_.id == "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a"][0]
+    assert "DragonOK" in is_1.aliases
+    assert len(is_1.external_references) == 4
+
+    # query2
+    is_2 = fs_source.query([Filter("external_references.external_id", '=', "T1027")])
+    assert len(is_2) == 1
+
+    is_2 = is_2[0]
+    assert is_2.id == "attack-pattern--b3d682b6-98f2-4fb0-aa3b-b4df007ca70a"
+    assert is_2.type == "attack-pattern"
+
+
+def test_filesystem_sink():
+    # creation
+    fs_sink = FileSystemSink(FS_PATH)
+    assert fs_sink.stix_dir == FS_PATH
+
+    fs_source = FileSystemSource(FS_PATH)
+
+    # Test all the ways stix objects can be added (via different supplied forms)
+
+    # add python stix object
+    camp1 = Campaign(name="Hannibal",
+                     objective="Targeting Italian and Spanish Diplomat internet accounts",
+                     aliases=["War Elephant"])
+
+    fs_sink.add(camp1)
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", camp1.id + ".json"))
+
+    camp1_r = fs_source.get(camp1.id)
+    assert camp1_r.id == camp1.id
+    assert camp1_r.name == "Hannibal"
+    assert "War Elephant" in camp1_r.aliases
+
+    # add stix object dict
+    camp2 = {
+        "name": "Aurelius",
+        "type": "campaign",
+        "objective": "German and French Intelligence Services",
+        "aliases": ["Purple Robes"],
+        "id": "campaign--111111b6-1112-4fb0-111b-b111107ca70a",
+        "created": "2017-05-31T21:31:53.197755Z"
+    }
+
+    fs_sink.add(camp2)
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", camp2["id"] + ".json"))
+
+    camp2_r = fs_source.get(camp2["id"])
+    assert camp2_r.id == camp2["id"]
+    assert camp2_r.name == camp2["name"]
+    assert "Purple Robes" in camp2_r.aliases
+
+    # add stix bundle dict
+    bund = {
+        "type": "bundle",
+        "id": "bundle--112211b6-1112-4fb0-111b-b111107ca70a",
+        "spec_version": "2.0",
+        "objects": [
+            {
+                "name": "Atilla",
+                "type": "campaign",
+                "objective": "Bulgarian, Albanian and Romanian Intelligence Services",
+                "aliases": ["Huns"],
+                "id": "campaign--133111b6-1112-4fb0-111b-b111107ca70a",
+                "created": "2017-05-31T21:31:53.197755Z"
+            }
+        ]
+    }
+
+    fs_sink.add(bund)
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", bund["objects"][0]["id"] + ".json"))
+
+    camp3_r = fs_source.get(bund["objects"][0]["id"])
+    assert camp3_r.id == bund["objects"][0]["id"]
+    assert camp3_r.name == bund["objects"][0]["name"]
+    assert "Huns" in camp3_r.aliases
+
+    # add json-encoded stix obj
+    camp4 = '{"type": "campaign", "id":"campaign--144111b6-1112-4fb0-111b-b111107ca70a",'\
+            ' "created":"2017-05-31T21:31:53.197755Z", "name": "Ghengis Khan", "objective": "China and Russian infrastructure"}'
+
+    fs_sink.add(camp4)
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", "campaign--144111b6-1112-4fb0-111b-b111107ca70a" + ".json"))
+
+    camp4_r = fs_source.get("campaign--144111b6-1112-4fb0-111b-b111107ca70a")
+    assert camp4_r.id == "campaign--144111b6-1112-4fb0-111b-b111107ca70a"
+    assert camp4_r.name == "Ghengis Khan"
+
+    # add json-encoded stix bundle
+    bund2 = '{"type": "bundle", "id": "bundle--332211b6-1132-4fb0-111b-b111107ca70a",' \
+            ' "spec_version": "2.0", "objects": [{"type": "campaign", "id": "campaign--155155b6-1112-4fb0-111b-b111107ca70a",' \
+            ' "created":"2017-05-31T21:31:53.197755Z", "name": "Spartacus", "objective": "Oppressive regimes of Africa and Middle East"}]}'
+    fs_sink.add(bund2)
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", "campaign--155155b6-1112-4fb0-111b-b111107ca70a" + ".json"))
+
+    camp5_r = fs_source.get("campaign--155155b6-1112-4fb0-111b-b111107ca70a")
+    assert camp5_r.id == "campaign--155155b6-1112-4fb0-111b-b111107ca70a"
+    assert camp5_r.name == "Spartacus"
+
+    # add list of objects
+    camp6 = Campaign(name="Comanche",
+                     objective="US Midwest manufacturing firms, oil refineries, and businesses",
+                     aliases=["Horse Warrior"])
+
+    camp7 = {
+        "name": "Napolean",
+        "type": "campaign",
+        "objective": "Central and Eastern Europe military commands and departments",
+        "aliases": ["The Frenchmen"],
+        "id": "campaign--122818b6-1112-4fb0-111b-b111107ca70a",
+        "created": "2017-05-31T21:31:53.197755Z"
+    }
+
+    fs_sink.add([camp6, camp7])
+
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", camp6.id + ".json"))
+    assert os.path.exists(os.path.join(FS_PATH, "campaign", "campaign--122818b6-1112-4fb0-111b-b111107ca70a" + ".json"))
+
+    camp6_r = fs_source.get(camp6.id)
+    assert camp6_r.id == camp6.id
+    assert "Horse Warrior" in camp6_r.aliases
+
+    camp7_r = fs_source.get(camp7["id"])
+    assert camp7_r.id == camp7["id"]
+    assert "The Frenchmen" in camp7_r.aliases
+
+    # remove all added objects
+    os.remove(os.path.join(FS_PATH, "campaign", camp1_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp2_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp3_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp4_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp5_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp6_r.id + ".json"))
+    os.remove(os.path.join(FS_PATH, "campaign", camp7_r.id + ".json"))
+
+    # remove campaign dir (that was added in course of testing)
+    os.rmdir(os.path.join(FS_PATH, "campaign"))
+
+
+def test_filesystem_store():
+    # creation
+    fs_store = FileSystemStore(FS_PATH)
+
+    # get()
+    coa = fs_store.get("course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd")
+    assert coa.id == "course-of-action--d9727aee-48b8-4fdb-89e2-4c49746ba4dd"
+    assert coa.type == "course-of-action"
+
+    # all versions() - (note at this time, all_versions() is still not applicable to FileSystem, as only one version is ever stored)
+    rel = fs_store.all_versions("relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1")[0]
+    assert rel.id == "relationship--70dc6b5c-c524-429e-a6ab-0dd40f0482c1"
+    assert rel.type == "relationship"
+
+    # query()
+    tools = fs_store.query([Filter("labels", "in", "tool")])
+    assert len(tools) == 2
+    assert "tool--242f3da3-4425-4d11-8f5c-b842886da966" in [tool.id for tool in tools]
+    assert "tool--03342581-f790-4f03-ba41-e82e67392e23" in [tool.id for tool in tools]
+
+    # add()
+    camp1 = Campaign(name="Great Heathen Army",
+                     objective="Targeting the government of United Kingdom and insitutions affiliated with the Church Of Englang",
+                     aliases=["Ragnar"])
+    fs_store.add(camp1)
+
+    camp1_r = fs_store.get(camp1.id)
+    assert camp1_r.id == camp1.id
+    assert camp1_r.name == camp1.name
+
+    # remove
+    os.remove(os.path.join(FS_PATH, "campaign", camp1_r.id + ".json"))
+
+    # remove campaign dir
+    os.rmdir(os.path.join(FS_PATH, "campaign"))
diff --git a/stix2/test/test_granular_markings.py b/stix2/test/test_granular_markings.py
index f8fc803..e910ad3 100644
--- a/stix2/test/test_granular_markings.py
+++ b/stix2/test/test_granular_markings.py
@@ -1,7 +1,7 @@
 
 import pytest
 
-from stix2 import TLP_RED, Malware, markings
+from stix2 import Malware, markings
 
 from .constants import MALWARE_MORE_KWARGS as MALWARE_KWARGS_CONST
 from .constants import MARKING_IDS
@@ -45,7 +45,6 @@ def test_add_marking_mark_one_selector_multiple_refs():
                 },
             ],
             **MALWARE_KWARGS),
-        MARKING_IDS[0],
     ),
     (
         MALWARE_KWARGS,
@@ -57,26 +56,13 @@ def test_add_marking_mark_one_selector_multiple_refs():
                 },
             ],
             **MALWARE_KWARGS),
-        MARKING_IDS[0],
-    ),
-    (
-        Malware(**MALWARE_KWARGS),
-        Malware(
-            granular_markings=[
-                {
-                    "selectors": ["description", "name"],
-                    "marking_ref": TLP_RED.id,
-                },
-            ],
-            **MALWARE_KWARGS),
-        TLP_RED,
     ),
 ])
 def test_add_marking_mark_multiple_selector_one_refs(data):
     before = data[0]
     after = data[1]
 
-    before = markings.add_markings(before, data[2], ["description", "name"])
+    before = markings.add_markings(before, [MARKING_IDS[0]], ["description", "name"])
 
     for m in before["granular_markings"]:
         assert m in after["granular_markings"]
@@ -361,42 +347,36 @@ def test_get_markings_positional_arguments_combinations(data):
     assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"])
 
 
-@pytest.mark.parametrize("data", [
-    (
-        Malware(
-            granular_markings=[
-                {
-                    "selectors": ["description"],
-                    "marking_ref": MARKING_IDS[0]
-                },
-                {
-                    "selectors": ["description"],
-                    "marking_ref": MARKING_IDS[1]
-                },
-            ],
-            **MALWARE_KWARGS
-        ),
-        [MARKING_IDS[0], MARKING_IDS[1]],
+@pytest.mark.parametrize("before", [
+    Malware(
+        granular_markings=[
+            {
+                "selectors": ["description"],
+                "marking_ref": MARKING_IDS[0]
+            },
+            {
+                "selectors": ["description"],
+                "marking_ref": MARKING_IDS[1]
+            },
+        ],
+        **MALWARE_KWARGS
     ),
-    (
-        dict(
-            granular_markings=[
-                {
-                    "selectors": ["description"],
-                    "marking_ref": MARKING_IDS[0]
-                },
-                {
-                    "selectors": ["description"],
-                    "marking_ref": MARKING_IDS[1]
-                },
-            ],
-            **MALWARE_KWARGS
-        ),
-        [MARKING_IDS[0], MARKING_IDS[1]],
+    dict(
+        granular_markings=[
+            {
+                "selectors": ["description"],
+                "marking_ref": MARKING_IDS[0]
+            },
+            {
+                "selectors": ["description"],
+                "marking_ref": MARKING_IDS[1]
+            },
+        ],
+        **MALWARE_KWARGS
     ),
 ])
-def test_remove_marking_remove_one_selector_with_multiple_refs(data):
-    before = markings.remove_markings(data[0], data[1], ["description"])
+def test_remove_marking_remove_one_selector_with_multiple_refs(before):
+    before = markings.remove_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description"])
     assert "granular_markings" not in before
 
 
diff --git a/stix2/test/test_markings.py b/stix2/test/test_markings.py
index 456bf92..0c6069a 100644
--- a/stix2/test/test_markings.py
+++ b/stix2/test/test_markings.py
@@ -241,14 +241,4 @@ def test_marking_wrong_type_construction():
     assert str(excinfo.value) == "Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]"
 
 
-def test_campaign_add_markings():
-    campaign = stix2.Campaign(
-        id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
-        created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
-        created="2016-04-06T20:03:00Z",
-        modified="2016-04-06T20:03:00Z",
-        name="Green Group Attacks Against Finance",
-        description="Campaign by Green Group against a series of targets in the financial services sector.",
-    )
-    campaign = campaign.add_markings(TLP_WHITE)
-    assert campaign.object_marking_refs[0] == TLP_WHITE.id
+# TODO: Add other examples
diff --git a/stix2/test/test_object_markings.py b/stix2/test/test_object_markings.py
index 10949ab..36e8e4d 100644
--- a/stix2/test/test_object_markings.py
+++ b/stix2/test/test_object_markings.py
@@ -1,7 +1,7 @@
 
 import pytest
 
-from stix2 import TLP_AMBER, Malware, exceptions, markings
+from stix2 import Malware, exceptions, markings
 
 from .constants import FAKE_TIME, MALWARE_ID, MARKING_IDS
 from .constants import MALWARE_KWARGS as MALWARE_KWARGS_CONST
@@ -21,26 +21,18 @@ MALWARE_KWARGS.update({
         Malware(**MALWARE_KWARGS),
         Malware(object_marking_refs=[MARKING_IDS[0]],
                 **MALWARE_KWARGS),
-        MARKING_IDS[0],
     ),
     (
         MALWARE_KWARGS,
         dict(object_marking_refs=[MARKING_IDS[0]],
              **MALWARE_KWARGS),
-        MARKING_IDS[0],
-    ),
-    (
-        Malware(**MALWARE_KWARGS),
-        Malware(object_marking_refs=[TLP_AMBER.id],
-                **MALWARE_KWARGS),
-        TLP_AMBER,
     ),
 ])
 def test_add_markings_one_marking(data):
     before = data[0]
     after = data[1]
 
-    before = markings.add_markings(before, data[2], None)
+    before = markings.add_markings(before, MARKING_IDS[0], None)
 
     for m in before["object_marking_refs"]:
         assert m in after["object_marking_refs"]
@@ -288,28 +280,19 @@ def test_remove_markings_object_level(data):
                 **MALWARE_KWARGS),
         Malware(object_marking_refs=[MARKING_IDS[1]],
                 **MALWARE_KWARGS),
-        [MARKING_IDS[0], MARKING_IDS[2]],
     ),
     (
         dict(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]],
              **MALWARE_KWARGS),
         dict(object_marking_refs=[MARKING_IDS[1]],
              **MALWARE_KWARGS),
-        [MARKING_IDS[0], MARKING_IDS[2]],
-    ),
-    (
-        Malware(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], TLP_AMBER.id],
-                **MALWARE_KWARGS),
-        Malware(object_marking_refs=[MARKING_IDS[1]],
-                **MALWARE_KWARGS),
-        [MARKING_IDS[0], TLP_AMBER],
     ),
 ])
 def test_remove_markings_multiple(data):
     before = data[0]
     after = data[1]
 
-    before = markings.remove_markings(before, data[2], None)
+    before = markings.remove_markings(before, [MARKING_IDS[0], MARKING_IDS[2]], None)
 
     assert before['object_marking_refs'] == after['object_marking_refs']
 
diff --git a/stix2/test/test_pattern_expressions.py b/stix2/test/test_pattern_expressions.py
index 0db1083..e806aa6 100644
--- a/stix2/test/test_pattern_expressions.py
+++ b/stix2/test/test_pattern_expressions.py
@@ -170,8 +170,3 @@ def test_set_op():
     exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression("network-traffic:dst_ref.value",
                                                                          "2001:0db8:dead:beef:0000:0000:0000:0000/64"))
     assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
-
-
-def test_timestamp():
-    ts = stix2.TimestampConstant('2014-01-13T07:03:17Z')
-    assert str(ts) == "t'2014-01-13T07:03:17Z'"