diff --git a/docs/guide/custom.ipynb b/docs/guide/custom.ipynb
index 8185269..7ceb33b 100644
--- a/docs/guide/custom.ipynb
+++ b/docs/guide/custom.ipynb
@@ -175,9 +175,9 @@
        ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
        ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
        "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity--d6996982-5fb7-4364-b716-b618516989b6&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:27.349Z&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:27.349Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity--e7fd0fe0-25ff-4fcb-abe5-b6254a9d1a22&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2019-07-25T18:18:18.241Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2019-07-25T18:18:18.241Z&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;name&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;John Smith&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;identity_class&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;individual&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;x_foo&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;bar&quot;</span>\n",
@@ -287,9 +287,9 @@
        ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
        ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
        "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity--a167d2de-9fc4-4734-a1ae-57a548aad22a&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:29.180Z&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:29.180Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity--033b5f42-c94f-488f-8efa-2b6a167f0d6f&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2019-07-25T18:18:21.352Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2019-07-25T18:18:21.352Z&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;name&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;John Smith&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;identity_class&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;individual&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;x_foo&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;bar&quot;</span>\n",
@@ -511,7 +511,7 @@
        "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;identity--311b2d2d-f010-4473-83ec-1edf84858f4c&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2015-12-21T19:59:11.000Z&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:32.934Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2019-07-25T18:18:25.927Z&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;name&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;John Smith&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;identity_class&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;individual&quot;</span>\n",
        "<span class=\"p\">}</span>\n",
@@ -544,7 +544,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -569,7 +569,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -645,9 +645,9 @@
        ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
        ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
        "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-animal&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-animal--1f7ce0ad-fd3a-4cf0-9cd7-13f7bef9ecd4&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:38.010Z&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2020-03-05T05:06:38.010Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-animal--b1e4fe7f-7985-451d-855c-6ba5c265b22a&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;created&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2018-04-05T18:38:19.790Z&quot;</span><span class=\"p\">,</span>\n",
+       "    <span class=\"nt\">&quot;modified&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;2018-04-05T18:38:19.790Z&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;species&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;lion&quot;</span><span class=\"p\">,</span>\n",
        "    <span class=\"nt\">&quot;animal_class&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;mammal&quot;</span>\n",
        "<span class=\"p\">}</span>\n",
@@ -657,7 +657,7 @@
        "<IPython.core.display.HTML object>"
       ]
      },
-     "execution_count": 9,
+     "execution_count": 8,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -677,7 +677,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
@@ -703,7 +703,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [
     {
@@ -784,7 +784,7 @@
        "<IPython.core.display.HTML object>"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -811,7 +811,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -846,7 +846,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [
     {
@@ -931,7 +931,7 @@
        "<IPython.core.display.HTML object>"
       ]
      },
-     "execution_count": 13,
+     "execution_count": 12,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -962,7 +962,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -1043,7 +1043,7 @@
        "<IPython.core.display.HTML object>"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     },
@@ -1125,7 +1125,7 @@
        "<IPython.core.display.HTML object>"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1155,316 +1155,6 @@
     "print(obs_data.objects[\"0\"].property_2)"
    ]
   },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### ID-Contributing Properties for Custom Cyber Observables\n",
-    "STIX 2.1 Cyber Observables (SCOs) have deterministic IDs, meaning that the ID of a SCO is based on the values of some of its properties. Thus, if multiple cyber observables of the same type have the same values for their ID-contributing properties, then these SCOs will have the same ID. UUIDv5 is used for the deterministic IDs, using the namespace `\"00abedb4-aa42-466c-9c01-fed23315a9b7\"`. A SCO's ID-contributing properties may consist of a combination of required properties and optional properties.\n",
-    "\n",
-    "If a SCO type does not have any ID contributing properties defined, or all of the ID-contributing properties are not present on the object, then the SCO uses a randomly-generated UUIDv4. Thus, you can optionally define which of your custom SCO's properties should be ID-contributing properties. Similar to standard SCOs, your custom SCO's ID-contributing properties can be any combination of the SCO's required and optional properties.\n",
-    "\n",
-    "You define the ID-contributing properties when defining your custom SCO with the `CustomObservable` decorator. After the list of properties, you can optionally define the list of id-contributing properties. If you do not want to specify any id-contributing properties for your custom SCO, then you do not need to do anything additional.\n",
-    "\n",
-    "See the example below:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<style type=\"text/css\">.highlight .hll { background-color: #ffffcc }\n",
-       ".highlight  { background: #f8f8f8; }\n",
-       ".highlight .c { color: #408080; font-style: italic } /* Comment */\n",
-       ".highlight .err { border: 1px solid #FF0000 } /* Error */\n",
-       ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n",
-       ".highlight .o { color: #666666 } /* Operator */\n",
-       ".highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
-       ".highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
-       ".highlight .cp { color: #BC7A00 } /* Comment.Preproc */\n",
-       ".highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
-       ".highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
-       ".highlight .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
-       ".highlight .gd { color: #A00000 } /* Generic.Deleted */\n",
-       ".highlight .ge { font-style: italic } /* Generic.Emph */\n",
-       ".highlight .gr { color: #FF0000 } /* Generic.Error */\n",
-       ".highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
-       ".highlight .gi { color: #00A000 } /* Generic.Inserted */\n",
-       ".highlight .go { color: #888888 } /* Generic.Output */\n",
-       ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
-       ".highlight .gs { font-weight: bold } /* Generic.Strong */\n",
-       ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
-       ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n",
-       ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
-       ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
-       ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
-       ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n",
-       ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
-       ".highlight .kt { color: #B00040 } /* Keyword.Type */\n",
-       ".highlight .m { color: #666666 } /* Literal.Number */\n",
-       ".highlight .s { color: #BA2121 } /* Literal.String */\n",
-       ".highlight .na { color: #7D9029 } /* Name.Attribute */\n",
-       ".highlight .nb { color: #008000 } /* Name.Builtin */\n",
-       ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
-       ".highlight .no { color: #880000 } /* Name.Constant */\n",
-       ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n",
-       ".highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
-       ".highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
-       ".highlight .nf { color: #0000FF } /* Name.Function */\n",
-       ".highlight .nl { color: #A0A000 } /* Name.Label */\n",
-       ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
-       ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
-       ".highlight .nv { color: #19177C } /* Name.Variable */\n",
-       ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
-       ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n",
-       ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n",
-       ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n",
-       ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n",
-       ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n",
-       ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n",
-       ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n",
-       ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
-       ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n",
-       ".highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
-       ".highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
-       ".highlight .s2 { color: #BA2121 } /* Literal.String.Double */\n",
-       ".highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
-       ".highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
-       ".highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
-       ".highlight .sx { color: #008000 } /* Literal.String.Other */\n",
-       ".highlight .sr { color: #BB6688 } /* Literal.String.Regex */\n",
-       ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n",
-       ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n",
-       ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
-       ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n",
-       ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n",
-       ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n",
-       ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n",
-       ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
-       ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
-       "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2--6bc655d6-dcb8-52a3-a862-46848c17e599&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;a_property&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;A property&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;property_2&quot;</span><span class=\"p\">:</span> <span class=\"mi\">2000</span>\n",
-       "<span class=\"p\">}</span>\n",
-       "</pre></div>\n"
-      ],
-      "text/plain": [
-       "<IPython.core.display.HTML object>"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "text/html": [
-       "<style type=\"text/css\">.highlight .hll { background-color: #ffffcc }\n",
-       ".highlight  { background: #f8f8f8; }\n",
-       ".highlight .c { color: #408080; font-style: italic } /* Comment */\n",
-       ".highlight .err { border: 1px solid #FF0000 } /* Error */\n",
-       ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n",
-       ".highlight .o { color: #666666 } /* Operator */\n",
-       ".highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
-       ".highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
-       ".highlight .cp { color: #BC7A00 } /* Comment.Preproc */\n",
-       ".highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
-       ".highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
-       ".highlight .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
-       ".highlight .gd { color: #A00000 } /* Generic.Deleted */\n",
-       ".highlight .ge { font-style: italic } /* Generic.Emph */\n",
-       ".highlight .gr { color: #FF0000 } /* Generic.Error */\n",
-       ".highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
-       ".highlight .gi { color: #00A000 } /* Generic.Inserted */\n",
-       ".highlight .go { color: #888888 } /* Generic.Output */\n",
-       ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
-       ".highlight .gs { font-weight: bold } /* Generic.Strong */\n",
-       ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
-       ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n",
-       ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
-       ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
-       ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
-       ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n",
-       ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
-       ".highlight .kt { color: #B00040 } /* Keyword.Type */\n",
-       ".highlight .m { color: #666666 } /* Literal.Number */\n",
-       ".highlight .s { color: #BA2121 } /* Literal.String */\n",
-       ".highlight .na { color: #7D9029 } /* Name.Attribute */\n",
-       ".highlight .nb { color: #008000 } /* Name.Builtin */\n",
-       ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
-       ".highlight .no { color: #880000 } /* Name.Constant */\n",
-       ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n",
-       ".highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
-       ".highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
-       ".highlight .nf { color: #0000FF } /* Name.Function */\n",
-       ".highlight .nl { color: #A0A000 } /* Name.Label */\n",
-       ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
-       ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
-       ".highlight .nv { color: #19177C } /* Name.Variable */\n",
-       ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
-       ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n",
-       ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n",
-       ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n",
-       ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n",
-       ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n",
-       ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n",
-       ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n",
-       ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
-       ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n",
-       ".highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
-       ".highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
-       ".highlight .s2 { color: #BA2121 } /* Literal.String.Double */\n",
-       ".highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
-       ".highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
-       ".highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
-       ".highlight .sx { color: #008000 } /* Literal.String.Other */\n",
-       ".highlight .sr { color: #BB6688 } /* Literal.String.Regex */\n",
-       ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n",
-       ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n",
-       ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
-       ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n",
-       ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n",
-       ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n",
-       ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n",
-       ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
-       ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
-       "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2--6bc655d6-dcb8-52a3-a862-46848c17e599&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;a_property&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;A property&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;property_2&quot;</span><span class=\"p\">:</span> <span class=\"mi\">3000</span>\n",
-       "<span class=\"p\">}</span>\n",
-       "</pre></div>\n"
-      ],
-      "text/plain": [
-       "<IPython.core.display.HTML object>"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "text/html": [
-       "<style type=\"text/css\">.highlight .hll { background-color: #ffffcc }\n",
-       ".highlight  { background: #f8f8f8; }\n",
-       ".highlight .c { color: #408080; font-style: italic } /* Comment */\n",
-       ".highlight .err { border: 1px solid #FF0000 } /* Error */\n",
-       ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n",
-       ".highlight .o { color: #666666 } /* Operator */\n",
-       ".highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
-       ".highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
-       ".highlight .cp { color: #BC7A00 } /* Comment.Preproc */\n",
-       ".highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
-       ".highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
-       ".highlight .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
-       ".highlight .gd { color: #A00000 } /* Generic.Deleted */\n",
-       ".highlight .ge { font-style: italic } /* Generic.Emph */\n",
-       ".highlight .gr { color: #FF0000 } /* Generic.Error */\n",
-       ".highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
-       ".highlight .gi { color: #00A000 } /* Generic.Inserted */\n",
-       ".highlight .go { color: #888888 } /* Generic.Output */\n",
-       ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
-       ".highlight .gs { font-weight: bold } /* Generic.Strong */\n",
-       ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
-       ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n",
-       ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
-       ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
-       ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
-       ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n",
-       ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
-       ".highlight .kt { color: #B00040 } /* Keyword.Type */\n",
-       ".highlight .m { color: #666666 } /* Literal.Number */\n",
-       ".highlight .s { color: #BA2121 } /* Literal.String */\n",
-       ".highlight .na { color: #7D9029 } /* Name.Attribute */\n",
-       ".highlight .nb { color: #008000 } /* Name.Builtin */\n",
-       ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
-       ".highlight .no { color: #880000 } /* Name.Constant */\n",
-       ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n",
-       ".highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
-       ".highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
-       ".highlight .nf { color: #0000FF } /* Name.Function */\n",
-       ".highlight .nl { color: #A0A000 } /* Name.Label */\n",
-       ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
-       ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
-       ".highlight .nv { color: #19177C } /* Name.Variable */\n",
-       ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
-       ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n",
-       ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n",
-       ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n",
-       ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n",
-       ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n",
-       ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n",
-       ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n",
-       ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
-       ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n",
-       ".highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
-       ".highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
-       ".highlight .s2 { color: #BA2121 } /* Literal.String.Double */\n",
-       ".highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
-       ".highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
-       ".highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
-       ".highlight .sx { color: #008000 } /* Literal.String.Other */\n",
-       ".highlight .sr { color: #BB6688 } /* Literal.String.Regex */\n",
-       ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n",
-       ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n",
-       ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
-       ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n",
-       ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n",
-       ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n",
-       ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n",
-       ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
-       ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"p\">{</span>\n",
-       "    <span class=\"nt\">&quot;type&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;id&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;x-new-observable-2--1e56f9c3-a73b-5fbd-b348-83c76523c4df&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;a_property&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;A different property&quot;</span><span class=\"p\">,</span>\n",
-       "    <span class=\"nt\">&quot;property_2&quot;</span><span class=\"p\">:</span> <span class=\"mi\">3000</span>\n",
-       "<span class=\"p\">}</span>\n",
-       "</pre></div>\n"
-      ],
-      "text/plain": [
-       "<IPython.core.display.HTML object>"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "from stix2.v21 import CustomObservable # IDs and Deterministic IDs are NOT part of STIX 2.0 Custom Observables\n",
-    "\n",
-    "@CustomObservable('x-new-observable-2', [\n",
-    "    ('a_property', properties.StringProperty(required=True)),\n",
-    "    ('property_2', properties.IntegerProperty()),\n",
-    "], [\n",
-    "    'a_property'\n",
-    "])\n",
-    "class NewObservable2():\n",
-    "    pass\n",
-    "\n",
-    "new_observable_a = NewObservable2(a_property=\"A property\", property_2=2000)\n",
-    "print(new_observable_a)\n",
-    "\n",
-    "new_observable_b = NewObservable2(a_property=\"A property\", property_2=3000)\n",
-    "print(new_observable_b)\n",
-    "\n",
-    "new_observable_c = NewObservable2(a_property=\"A different property\", property_2=3000)\n",
-    "print(new_observable_c)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "In this example, `a_property` is the only id-contributing property. Notice that the ID for `new_observable_a` and `new_observable_b` is the same since they have the same value for the id-contributing `a_property` property."
-   ]
-  },
   {
    "cell_type": "markdown",
    "metadata": {
@@ -1793,21 +1483,21 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 2",
+   "display_name": "Python 3",
    "language": "python",
-   "name": "python2"
+   "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
     "name": "ipython",
-    "version": 2
+    "version": 3
    },
    "file_extension": ".py",
    "mimetype": "text/x-python",
    "name": "python",
    "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython2",
-   "version": "2.7.15+"
+   "pygments_lexer": "ipython3",
+   "version": "3.6.3"
   }
  },
  "nbformat": 4,
diff --git a/stix2/custom.py b/stix2/custom.py
index 802fd07..a00498b 100644
--- a/stix2/custom.py
+++ b/stix2/custom.py
@@ -53,10 +53,7 @@ def _custom_marking_builder(cls, type, properties, version):
     return _CustomMarking
 
 
-def _custom_observable_builder(cls, type, properties, version, id_contrib_props=None):
-    if id_contrib_props is None:
-        id_contrib_props = []
-
+def _custom_observable_builder(cls, type, properties, version):
     class _CustomObservable(cls, _Observable):
 
         if not re.match(TYPE_REGEX, type):
@@ -101,8 +98,6 @@ def _custom_observable_builder(cls, type, properties, version, id_contrib_props=
 
         _type = type
         _properties = OrderedDict(properties)
-        if version != '2.0':
-            _id_contributing_properties = id_contrib_props
 
         def __init__(self, **kwargs):
             _Observable.__init__(self, **kwargs)
diff --git a/stix2/datastore/filesystem.py b/stix2/datastore/filesystem.py
index d5acc24..e8442e6 100644
--- a/stix2/datastore/filesystem.py
+++ b/stix2/datastore/filesystem.py
@@ -15,7 +15,7 @@ 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
+from stix2.utils import format_datetime, get_type_from_id, is_marking
 
 
 def _timestamp2filename(timestamp):
@@ -329,50 +329,11 @@ def _check_object_from_file(query, filepath, allow_custom, version, encoding):
     return result
 
 
-def _is_versioned_type_dir(type_path, type_name):
-    """
-    Try to detect whether the given directory is for a versioned type of STIX
-    object.  This is done by looking for a directory whose name is a STIX ID
-    of the appropriate type.  If found, treat this type as versioned.  This
-    doesn't work when a versioned type directory is empty (it will be
-    mis-classified as unversioned), but this detection is only necessary when
-    reading/querying data.  If a directory is empty, you'll get no results
-    either way.
-
-    Args:
-        type_path: A path to a directory containing one type of STIX object.
-        type_name: The STIX type name.
-
-    Returns:
-        True if the directory looks like it contains versioned objects; False
-        if not.
-
-    Raises:
-        OSError: If there are errors accessing directory contents or stat()'ing
-            files
-    """
-    id_regex = re.compile(
-        r"^" + re.escape(type_name) +
-        r"--[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}"
-        r"-[0-9a-f]{12}$",
-        re.I,
-    )
-
-    for entry in os.listdir(type_path):
-        s = os.stat(os.path.join(type_path, entry))
-        if stat.S_ISDIR(s.st_mode) and id_regex.match(entry):
-            is_versioned = True
-            break
-    else:
-        is_versioned = False
-
-    return is_versioned
-
-
 def _search_versioned(query, type_path, auth_ids, allow_custom, version, encoding):
     """
     Searches the given directory, which contains data for STIX objects of a
-    particular versioned type, and return any which match the query.
+    particular versioned type (i.e. not markings), and return any which match
+    the query.
 
     Args:
         query: The query to match against
@@ -429,24 +390,36 @@ def _search_versioned(query, type_path, auth_ids, allow_custom, version, encodin
 
     # For backward-compatibility, also search for plain files named after
     # object IDs, in the type directory.
-    backcompat_results = _search_unversioned(
-        query, type_path, auth_ids, allow_custom, version, encoding,
+    id_files = _get_matching_dir_entries(
+        type_path, auth_ids, stat.S_ISREG,
+        ".json",
     )
-    results.extend(backcompat_results)
+    for id_file in id_files:
+        id_path = os.path.join(type_path, id_file)
+
+        try:
+            stix_obj = _check_object_from_file(
+                query, id_path, allow_custom,
+                version, encoding,
+            )
+            if stix_obj:
+                results.append(stix_obj)
+        except IOError as e:
+            if e.errno != errno.ENOENT:
+                raise
+            # else, file-not-found is ok, just skip
 
     return results
 
 
-def _search_unversioned(
-    query, type_path, auth_ids, allow_custom, version, encoding,
-):
+def _search_markings(query, markings_path, auth_ids, allow_custom, version, encoding):
     """
-    Searches the given directory, which contains unversioned data, and return
-    any objects which match the query.
+    Searches the given directory, which contains markings data, and return any
+    which match the query.
 
     Args:
         query: The query to match against
-        type_path: The directory with STIX files of unversioned type
+        markings_path: The directory with STIX markings files
         auth_ids: Search optimization based on object ID
         allow_custom (bool): Whether to allow custom properties as well unknown
             custom objects.
@@ -468,11 +441,11 @@ def _search_unversioned(
     """
     results = []
     id_files = _get_matching_dir_entries(
-        type_path, auth_ids, stat.S_ISREG,
+        markings_path, auth_ids, stat.S_ISREG,
         ".json",
     )
     for id_file in id_files:
-        id_path = os.path.join(type_path, id_file)
+        id_path = os.path.join(markings_path, id_file)
 
         try:
             stix_obj = _check_object_from_file(
@@ -557,14 +530,12 @@ class FileSystemSink(DataSink):
         """Write the given STIX object to a file in the STIX file directory.
         """
         type_dir = os.path.join(self._stix_dir, stix_obj["type"])
-
-        # All versioned objects should have a "modified" property.
-        if "modified" in stix_obj:
-            filename = _timestamp2filename(stix_obj["modified"])
-            obj_dir = os.path.join(type_dir, stix_obj["id"])
-        else:
+        if is_marking(stix_obj):
             filename = stix_obj["id"]
             obj_dir = type_dir
+        else:
+            filename = _timestamp2filename(stix_obj["modified"])
+            obj_dir = os.path.join(type_dir, stix_obj["id"])
 
         file_path = os.path.join(obj_dir, filename + ".json")
 
@@ -678,14 +649,12 @@ class FileSystemSource(DataSource):
         all_data = self.all_versions(stix_id, version=version, _composite_filters=_composite_filters)
 
         if all_data:
-            # Simple check for a versioned STIX type: see if the objects have a
-            # "modified" property.  (Need only check one, since they are all of
-            # the same type.)
-            is_versioned = "modified" in all_data[0]
-            if is_versioned:
-                stix_obj = sorted(all_data, key=lambda k: k['modified'])[-1]
-            else:
+            if is_marking(stix_id):
+                # Markings are unversioned; there shouldn't be more than one
+                # result.
                 stix_obj = all_data[0]
+            else:
+                stix_obj = sorted(all_data, key=lambda k: k['modified'])[-1]
         else:
             stix_obj = None
 
@@ -751,15 +720,14 @@ class FileSystemSource(DataSource):
         )
         for type_dir in type_dirs:
             type_path = os.path.join(self._stix_dir, type_dir)
-            type_is_versioned = _is_versioned_type_dir(type_path, type_dir)
-            if type_is_versioned:
-                type_results = _search_versioned(
+            if type_dir == "marking-definition":
+                type_results = _search_markings(
                     query, type_path, auth_ids,
                     self.allow_custom, version,
                     self.encoding,
                 )
             else:
-                type_results = _search_unversioned(
+                type_results = _search_versioned(
                     query, type_path, auth_ids,
                     self.allow_custom, version,
                     self.encoding,
diff --git a/stix2/v21/common.py b/stix2/v21/common.py
index cf3a3b3..ac8daf1 100644
--- a/stix2/v21/common.py
+++ b/stix2/v21/common.py
@@ -4,6 +4,7 @@ from collections import OrderedDict
 
 from ..base import _STIXBase
 from ..custom import _custom_marking_builder
+from ..exceptions import InvalidValueError
 from ..markings import _MarkingsMixin
 from ..markings.utils import check_tlp_marking
 from ..properties import (
@@ -28,10 +29,26 @@ class ExternalReference(_STIXBase):
         ('external_id', StringProperty()),
     ])
 
+    # This is hash-algorithm-ov
+    _LEGAL_HASHES = {
+        "MD5", "SHA-1", "SHA-256", "SHA-512", "SHA3-256", "SHA3-512", "SSDEEP",
+        "TLSH",
+    }
+
     def _check_object_constraints(self):
         super(ExternalReference, self)._check_object_constraints()
         self._check_at_least_one_property(['description', 'external_id', 'url'])
 
+        if "hashes" in self:
+            if any(
+                hash_ not in self._LEGAL_HASHES
+                for hash_ in self["hashes"]
+            ):
+                raise InvalidValueError(
+                    ExternalReference, "hashes",
+                    "Hash algorithm names must be members of hash-algorithm-ov",
+                )
+
 
 class KillChainPhase(_STIXBase):
     # TODO: Add link
diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py
index e8c1925..ed560a6 100644
--- a/stix2/v21/observables.py
+++ b/stix2/v21/observables.py
@@ -966,7 +966,7 @@ class X509Certificate(_Observable):
         self._check_at_least_one_property(att_list)
 
 
-def CustomObservable(type='x-custom-observable', properties=None, id_contrib_props=None):
+def CustomObservable(type='x-custom-observable', properties=None):
     """Custom STIX Cyber Observable Object type decorator.
 
     Example:
@@ -987,7 +987,7 @@ def CustomObservable(type='x-custom-observable', properties=None, id_contrib_pro
             properties,
             [('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))],
         ]))
-        return _custom_observable_builder(cls, type, _properties, '2.1', id_contrib_props)
+        return _custom_observable_builder(cls, type, _properties, '2.1')
     return wrapper
 
 

Enforce hash keys in 2.1 external-references
Came out of https://github.com/oasis-tcs/cti-stix2/issues/192
diff --git a/stix2/test/v21/stix2_data/directory/directory--572827aa-e0cd-44fd-afd5-a717a7585f39.json b/stix2/test/v21/stix2_data/directory/directory--572827aa-e0cd-44fd-afd5-a717a7585f39.json
deleted file mode 100644
index 3812ed4..0000000
--- a/stix2/test/v21/stix2_data/directory/directory--572827aa-e0cd-44fd-afd5-a717a7585f39.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-    "ctime": "2020-10-06T01:54:32.000Z",
-    "contains_refs": [
-        "directory--80539e31-85f3-4304-bd14-e2e8c10859a5",
-        "file--e9e03175-0357-41b5-a2aa-eb99b455cd0c",
-        "directory--f6c54233-027b-4464-8126-da1324d8f66c"
-    ],
-    "path": "/performance/Democrat.gif",
-    "type": "directory",
-    "id": "directory--572827aa-e0cd-44fd-afd5-a717a7585f39"
-}
diff --git a/stix2/test/v21/test_custom.py b/stix2/test/v21/test_custom.py
index b46288d..9c650eb 100644
--- a/stix2/test/v21/test_custom.py
+++ b/stix2/test/v21/test_custom.py
@@ -1,5 +1,3 @@
-import uuid
-
 import pytest
 
 import stix2
@@ -667,76 +665,6 @@ def test_observed_data_with_custom_observable_object():
     assert ob_data.objects['0'].property1 == 'something'
 
 
-def test_custom_observable_object_det_id_1():
-    @stix2.v21.CustomObservable(
-        'x-det-id-observable-1', [
-            ('property1', stix2.properties.StringProperty(required=True)),
-            ('property2', stix2.properties.IntegerProperty()),
-        ], [
-            'property1',
-        ],
-    )
-    class DetIdObs1():
-        pass
-
-    dio_1 = DetIdObs1(property1='I am property1!', property2=42)
-    dio_2 = DetIdObs1(property1='I am property1!', property2=24)
-    assert dio_1.property1 == dio_2.property1 == 'I am property1!'
-    assert dio_1.id == dio_2.id
-
-    uuid_obj = uuid.UUID(dio_1.id[-36:])
-    assert uuid_obj.variant == uuid.RFC_4122
-    assert uuid_obj.version == 5
-
-    dio_3 = DetIdObs1(property1='I am property1!', property2=42)
-    dio_4 = DetIdObs1(property1='I am also property1!', property2=24)
-    assert dio_3.property1 == 'I am property1!'
-    assert dio_4.property1 == 'I am also property1!'
-    assert dio_3.id != dio_4.id
-
-
-def test_custom_observable_object_det_id_2():
-    @stix2.v21.CustomObservable(
-        'x-det-id-observable-2', [
-            ('property1', stix2.properties.StringProperty(required=True)),
-            ('property2', stix2.properties.IntegerProperty()),
-        ], [
-            'property1', 'property2',
-        ],
-    )
-    class DetIdObs2():
-        pass
-
-    dio_1 = DetIdObs2(property1='I am property1!', property2=42)
-    dio_2 = DetIdObs2(property1='I am property1!', property2=42)
-    assert dio_1.property1 == dio_2.property1 == 'I am property1!'
-    assert dio_1.property2 == dio_2.property2 == 42
-    assert dio_1.id == dio_2.id
-
-    dio_3 = DetIdObs2(property1='I am property1!', property2=42)
-    dio_4 = DetIdObs2(property1='I am also property1!', property2=42)
-    assert dio_3.property1 == 'I am property1!'
-    assert dio_4.property1 == 'I am also property1!'
-    assert dio_3.property2 == dio_4.property2 == 42
-    assert dio_3.id != dio_4.id
-
-
-def test_custom_observable_object_no_id_contrib_props():
-    @stix2.v21.CustomObservable(
-        'x-det-id-observable-3', [
-            ('property1', stix2.properties.StringProperty(required=True)),
-        ],
-    )
-    class DetIdObs3():
-        pass
-
-    dio = DetIdObs3(property1="I am property1!")
-
-    uuid_obj = uuid.UUID(dio.id[-36:])
-    assert uuid_obj.variant == uuid.RFC_4122
-    assert uuid_obj.version == 4
-
-
 @stix2.v21.CustomExtension(
     stix2.v21.DomainName, 'x-new-ext', [
         ('property1', stix2.properties.StringProperty(required=True)),
diff --git a/stix2/test/v21/test_datastore_filesystem.py b/stix2/test/v21/test_datastore_filesystem.py
index 3eb8aaa..9917ccd 100644
--- a/stix2/test/v21/test_datastore_filesystem.py
+++ b/stix2/test/v21/test_datastore_filesystem.py
@@ -221,16 +221,6 @@ def test_filesystem_source_backward_compatible(fs_source):
     assert result.malware_types == ["version four"]
 
 
-def test_filesystem_source_sco(fs_source):
-    results = fs_source.query([stix2.Filter("type", "=", "directory")])
-
-    assert len(results) == 1
-    result = results[0]
-    assert result["type"] == "directory"
-    assert result["id"] == "directory--572827aa-e0cd-44fd-afd5-a717a7585f39"
-    assert result["path"] == "/performance/Democrat.gif"
-
-
 def test_filesystem_sink_add_python_stix_object(fs_sink, fs_source):
     # add python stix object
     camp1 = stix2.v21.Campaign(
@@ -445,24 +435,6 @@ def test_filesystem_sink_marking(fs_sink):
     os.remove(marking_filepath)
 
 
-def test_filesystem_sink_sco(fs_sink):
-    file_sco = {
-        "type": "file",
-        "id": "file--decfcc48-31b3-45f5-87c8-1b3a5d71a307",
-        "name": "cats.png",
-    }
-
-    fs_sink.add(file_sco)
-    sco_filepath = os.path.join(
-        FS_PATH, "file", file_sco["id"] + ".json",
-    )
-
-    assert os.path.exists(sco_filepath)
-
-    os.remove(sco_filepath)
-    os.rmdir(os.path.dirname(sco_filepath))
-
-
 def test_filesystem_store_get_stored_as_bundle(fs_store):
     coa = fs_store.get("course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f")
     assert coa.id == "course-of-action--95ddb356-7ba0-4bd9-a889-247262b8946f"
@@ -501,10 +473,9 @@ def test_filesystem_store_query_single_filter(fs_store):
 
 def test_filesystem_store_empty_query(fs_store):
     results = fs_store.query()  # returns all
-    assert len(results) == 31
+    assert len(results) == 30
     assert "tool--242f3da3-4425-4d11-8f5c-b842886da966" in [obj.id for obj in results]
     assert "marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168" in [obj.id for obj in results]
-    assert "directory--572827aa-e0cd-44fd-afd5-a717a7585f39" in [obj.id for obj in results]
 
 
 def test_filesystem_store_query_multiple_filters(fs_store):
@@ -516,7 +487,7 @@ def test_filesystem_store_query_multiple_filters(fs_store):
 
 def test_filesystem_store_query_dont_include_type_folder(fs_store):
     results = fs_store.query(stix2.Filter("type", "!=", "tool"))
-    assert len(results) == 29
+    assert len(results) == 28
 
 
 def test_filesystem_store_add(fs_store):
@@ -603,26 +574,6 @@ def test_filesystem_store_add_marking(fs_store):
     os.remove(marking_filepath)
 
 
-def test_filesystem_store_add_sco(fs_store):
-    sco = stix2.v21.EmailAddress(
-        value="jdoe@example.com",
-    )
-
-    fs_store.add(sco)
-    sco_filepath = os.path.join(
-        FS_PATH, "email-addr", sco["id"] + ".json",
-    )
-
-    assert os.path.exists(sco_filepath)
-
-    sco_r = fs_store.get(sco["id"])
-    assert sco_r["id"] == sco["id"]
-    assert sco_r["value"] == sco["value"]
-
-    os.remove(sco_filepath)
-    os.rmdir(os.path.dirname(sco_filepath))
-
-
 def test_filesystem_object_with_custom_property(fs_store):
     camp = stix2.v21.Campaign(
         name="Scipio Africanus",
@@ -1073,7 +1024,6 @@ def test_search_auth_set_black_empty(rel_fs_store):
         "attack-pattern",
         "campaign",
         "course-of-action",
-        "directory",
         "identity",
         "indicator",
         "intrusion-set",
diff --git a/stix2/test/v21/test_external_reference.py b/stix2/test/v21/test_external_reference.py
index d192a11..f347191 100644
--- a/stix2/test/v21/test_external_reference.py
+++ b/stix2/test/v21/test_external_reference.py
@@ -120,3 +120,14 @@ def test_external_reference_source_required():
 
     assert excinfo.value.cls == stix2.v21.ExternalReference
     assert excinfo.value.properties == ["source_name"]
+
+
+def test_external_reference_bad_hash():
+    with pytest.raises(stix2.exceptions.InvalidValueError):
+        stix2.v21.ExternalReference(
+            source_name="ACME Threat Intel",
+            description="Threat report",
+            hashes={
+                "SHA-123": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+            },
+        )
diff --git a/stix2/test/v21/test_indicator.py b/stix2/test/v21/test_indicator.py
index d7d7e47..23bad29 100644
--- a/stix2/test/v21/test_indicator.py
+++ b/stix2/test/v21/test_indicator.py
@@ -271,7 +271,7 @@ def test_indicator_stix20_invalid_pattern():
         )
 
     assert excinfo.value.cls == stix2.v21.Indicator
-    assert "FAIL: Duplicate qualifier type encountered: WITHIN" in str(excinfo.value)
+    assert "FAIL: Duplicate qualifier type encountered" in str(excinfo.value)
 
     ind = stix2.v21.Indicator(
         type="indicator",

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
apt-get update apt-get install -y gcc
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
antlr4-python3-runtime==4.9.3
attrs==24.2.0
Babel==2.14.0
backcall==0.2.0
beautifulsoup4==4.13.3
bleach==6.0.0
bump2version==1.0.1
bumpversion==0.6.0
cachetools==5.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
cfgv==3.3.1
chardet==5.2.0
charset-normalizer==3.4.1
colorama==0.4.6
coverage==7.2.7
decorator==5.1.1
defusedxml==0.7.1
distlib==0.3.9
docutils==0.17.1
entrypoints==0.4
exceptiongroup==1.2.2
fastjsonschema==2.21.1
filelock==3.12.2
identify==2.5.24
idna==3.10
imagesize==1.4.1
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig==2.0.0
ipython==7.34.0
jedi==0.19.2
Jinja2==3.1.6
jsonschema==4.17.3
jupyter_client==7.4.9
jupyter_core==4.12.0
jupyterlab-pygments==0.2.2
MarkupSafe==2.1.5
matplotlib-inline==0.1.6
mistune==3.0.2
nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nbsphinx==0.4.3
nest-asyncio==1.6.0
nodeenv==1.9.1
packaging==24.0
pandocfilters==1.5.1
parso==0.8.4
pexpect==4.9.0
pickleshare==0.7.5
pkgutil_resolve_name==1.3.10
platformdirs==4.0.0
pluggy==1.2.0
pre-commit==2.21.0
prompt_toolkit==3.0.48
ptyprocess==0.7.0
Pygments==2.17.2
pyproject-api==1.5.3
pyrsistent==0.19.3
pytest==7.4.4
pytest-cov==4.1.0
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.1
pyzmq==26.2.1
requests==2.31.0
simplejson==3.20.1
six==1.17.0
snowballstemmer==2.2.0
soupsieve==2.4.1
Sphinx==1.8.6
sphinx-prompt==1.5.0
sphinxcontrib-serializinghtml==1.1.5
sphinxcontrib-websupport==1.2.4
-e git+https://github.com/oasis-open/cti-python-stix2.git@3803e4bdd7877af8e11211f42261f324b2c92255#egg=stix2
stix2-patterns==2.0.0
tinycss2==1.2.1
tomli==2.0.1
tornado==6.2
tox==4.8.0
traitlets==5.9.0
typing_extensions==4.7.1
urllib3==2.0.7
virtualenv==20.26.6
wcwidth==0.2.13
webencodings==0.5.1
zipp==3.15.0

name: cti-python-stix2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - antlr4-python3-runtime==4.9.3
      - attrs==24.2.0
      - babel==2.14.0
      - backcall==0.2.0
      - beautifulsoup4==4.13.3
      - bleach==6.0.0
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - cachetools==5.5.2
      - cfgv==3.3.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - coverage==7.2.7
      - decorator==5.1.1
      - defusedxml==0.7.1
      - distlib==0.3.9
      - docutils==0.17.1
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - fastjsonschema==2.21.1
      - filelock==3.12.2
      - identify==2.5.24
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - ipython==7.34.0
      - jedi==0.19.2
      - jinja2==3.1.6
      - jsonschema==4.17.3
      - jupyter-client==7.4.9
      - jupyter-core==4.12.0
      - jupyterlab-pygments==0.2.2
      - markupsafe==2.1.5
      - matplotlib-inline==0.1.6
      - mistune==3.0.2
      - nbclient==0.7.4
      - nbconvert==7.6.0
      - nbformat==5.8.0
      - nbsphinx==0.4.3
      - nest-asyncio==1.6.0
      - nodeenv==1.9.1
      - packaging==24.0
      - pandocfilters==1.5.1
      - parso==0.8.4
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pkgutil-resolve-name==1.3.10
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - pre-commit==2.21.0
      - prompt-toolkit==3.0.48
      - ptyprocess==0.7.0
      - pygments==2.17.2
      - pyproject-api==1.5.3
      - pyrsistent==0.19.3
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.1
      - pyzmq==26.2.1
      - requests==2.31.0
      - simplejson==3.20.1
      - six==1.17.0
      - snowballstemmer==2.2.0
      - soupsieve==2.4.1
      - sphinx==1.8.6
      - sphinx-prompt==1.5.0
      - sphinxcontrib-serializinghtml==1.1.5
      - sphinxcontrib-websupport==1.2.4
      - stix2-patterns==2.0.0
      - tinycss2==1.2.1
      - tomli==2.0.1
      - tornado==6.2
      - tox==4.8.0
      - traitlets==5.9.0
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - virtualenv==20.26.6
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - zipp==3.15.0
prefix: /opt/conda/envs/cti-python-stix2

stix2/test/v21/test_external_reference.py::test_external_reference_bad_hash
stix2/test/v21/test_custom.py::test_identity_custom_property stix2/test/v21/test_custom.py::test_identity_custom_property_invalid stix2/test/v21/test_custom.py::test_identity_custom_property_allowed stix2/test/v21/test_custom.py::test_parse_identity_custom_property[{\n stix2/test/v21/test_custom.py::test_custom_property_object_in_bundled_object stix2/test/v21/test_custom.py::test_custom_properties_object_in_bundled_object stix2/test/v21/test_custom.py::test_custom_property_dict_in_bundled_object stix2/test/v21/test_custom.py::test_custom_properties_dict_in_bundled_object stix2/test/v21/test_custom.py::test_custom_property_in_observed_data stix2/test/v21/test_custom.py::test_custom_property_object_in_observable_extension stix2/test/v21/test_custom.py::test_custom_property_dict_in_observable_extension stix2/test/v21/test_custom.py::test_identity_custom_property_revoke stix2/test/v21/test_custom.py::test_identity_custom_property_edit_markings stix2/test/v21/test_custom.py::test_custom_marking_no_init_1 stix2/test/v21/test_custom.py::test_custom_marking_no_init_2 stix2/test/v21/test_custom.py::test_custom_object_raises_exception stix2/test/v21/test_custom.py::test_custom_object_type stix2/test/v21/test_custom.py::test_custom_object_no_init_1 stix2/test/v21/test_custom.py::test_custom_object_no_init_2 stix2/test/v21/test_custom.py::test_custom_object_invalid_type_name stix2/test/v21/test_custom.py::test_parse_custom_object_type stix2/test/v21/test_custom.py::test_parse_unregistered_custom_object_type stix2/test/v21/test_custom.py::test_parse_unregistered_custom_object_type_w_allow_custom stix2/test/v21/test_custom.py::test_custom_observable_object_1 stix2/test/v21/test_custom.py::test_custom_observable_object_2 stix2/test/v21/test_custom.py::test_custom_observable_object_3 stix2/test/v21/test_custom.py::test_custom_observable_raises_exception stix2/test/v21/test_custom.py::test_custom_observable_object_no_init_1 stix2/test/v21/test_custom.py::test_custom_observable_object_no_init_2 stix2/test/v21/test_custom.py::test_custom_observable_object_invalid_type_name stix2/test/v21/test_custom.py::test_custom_observable_object_invalid_ref_property stix2/test/v21/test_custom.py::test_custom_observable_object_invalid_refs_property stix2/test/v21/test_custom.py::test_custom_observable_object_invalid_refs_list_property stix2/test/v21/test_custom.py::test_custom_no_properties_raises_exception stix2/test/v21/test_custom.py::test_custom_wrong_properties_arg_raises_exception stix2/test/v21/test_custom.py::test_parse_custom_observable_object stix2/test/v21/test_custom.py::test_parse_unregistered_custom_observable_object stix2/test/v21/test_custom.py::test_parse_unregistered_custom_observable_object_with_no_type stix2/test/v21/test_custom.py::test_parse_observed_data_with_custom_observable stix2/test/v21/test_custom.py::test_parse_invalid_custom_observable_object stix2/test/v21/test_custom.py::test_observable_custom_property stix2/test/v21/test_custom.py::test_observable_custom_property_invalid stix2/test/v21/test_custom.py::test_observable_custom_property_allowed stix2/test/v21/test_custom.py::test_observed_data_with_custom_observable_object stix2/test/v21/test_custom.py::test_custom_extension_raises_exception stix2/test/v21/test_custom.py::test_custom_extension stix2/test/v21/test_custom.py::test_custom_extension_wrong_observable_type stix2/test/v21/test_custom.py::test_custom_extension_with_list_and_dict_properties_observable_type[{\n stix2/test/v21/test_custom.py::test_custom_extension_invalid_observable stix2/test/v21/test_custom.py::test_custom_extension_invalid_type_name stix2/test/v21/test_custom.py::test_custom_extension_no_properties stix2/test/v21/test_custom.py::test_custom_extension_empty_properties stix2/test/v21/test_custom.py::test_custom_extension_dict_properties stix2/test/v21/test_custom.py::test_custom_extension_no_init_1 stix2/test/v21/test_custom.py::test_custom_extension_no_init_2 stix2/test/v21/test_custom.py::test_parse_observable_with_custom_extension stix2/test/v21/test_custom.py::test_custom_and_spec_extension_mix stix2/test/v21/test_custom.py::test_parse_observable_with_unregistered_custom_extension[{\n stix2/test/v21/test_custom.py::test_register_custom_object stix2/test/v21/test_custom.py::test_extension_property_location stix2/test/v21/test_custom.py::test_custom_object_nested_dictionary[{\n stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_nonexistent_folder stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_nonexistent_folder stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_bad_json_file stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_bad_stix_file stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_get_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_get_nonexistent_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_all_versions stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_query_single stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_query_multiple stix2/test/v21/test_datastore_filesystem.py::test_filesystem_source_backward_compatible stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_add_python_stix_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_add_stix_object_dict stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_add_stix_bundle_dict stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_add_json_stix_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_json_stix_bundle stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_add_objects_list stix2/test/v21/test_datastore_filesystem.py::test_filesystem_sink_marking stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_get_stored_as_bundle stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_get_stored_as_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_all_versions stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_query stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_query_single_filter stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_empty_query stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_query_multiple_filters stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_query_dont_include_type_folder stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_add stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_add_as_bundle stix2/test/v21/test_datastore_filesystem.py::test_filesystem_add_bundle_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_add_invalid_object stix2/test/v21/test_datastore_filesystem.py::test_filesystem_store_add_marking stix2/test/v21/test_datastore_filesystem.py::test_filesystem_object_with_custom_property stix2/test/v21/test_datastore_filesystem.py::test_filesystem_object_with_custom_property_in_bundle stix2/test/v21/test_datastore_filesystem.py::test_filesystem_custom_object stix2/test/v21/test_datastore_filesystem.py::test_relationships stix2/test/v21/test_datastore_filesystem.py::test_relationships_by_type stix2/test/v21/test_datastore_filesystem.py::test_relationships_by_source stix2/test/v21/test_datastore_filesystem.py::test_relationships_by_target stix2/test/v21/test_datastore_filesystem.py::test_relationships_by_target_and_type stix2/test/v21/test_datastore_filesystem.py::test_relationships_by_target_and_source stix2/test/v21/test_datastore_filesystem.py::test_related_to stix2/test/v21/test_datastore_filesystem.py::test_related_to_by_source stix2/test/v21/test_datastore_filesystem.py::test_related_to_by_target stix2/test/v21/test_datastore_filesystem.py::test_auth_set_white1 stix2/test/v21/test_datastore_filesystem.py::test_auth_set_white2 stix2/test/v21/test_datastore_filesystem.py::test_auth_set_white3 stix2/test/v21/test_datastore_filesystem.py::test_auth_set_black1 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types1 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types2 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types3 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types4 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types5 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types6 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types7 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types8 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids1 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids2 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids3 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids4 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids5 stix2/test/v21/test_datastore_filesystem.py::test_optimize_types_ids6 stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_white1 stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_white2 stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_white3 stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_black1 stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_white_empty stix2/test/v21/test_datastore_filesystem.py::test_search_auth_set_black_empty stix2/test/v21/test_datastore_filesystem.py::test_timestamp2filename_naive stix2/test/v21/test_datastore_filesystem.py::test_timestamp2filename_tz stix2/test/v21/test_external_reference.py::test_external_reference_veris stix2/test/v21/test_external_reference.py::test_external_reference_capec stix2/test/v21/test_external_reference.py::test_external_reference_capec_url stix2/test/v21/test_external_reference.py::test_external_reference_threat_report stix2/test/v21/test_external_reference.py::test_external_reference_bugzilla stix2/test/v21/test_external_reference.py::test_external_reference_offline stix2/test/v21/test_external_reference.py::test_external_reference_source_required stix2/test/v21/test_indicator.py::test_indicator_with_all_required_properties stix2/test/v21/test_indicator.py::test_indicator_autogenerated_properties stix2/test/v21/test_indicator.py::test_indicator_type_must_be_indicator stix2/test/v21/test_indicator.py::test_indicator_id_must_start_with_indicator stix2/test/v21/test_indicator.py::test_indicator_required_properties stix2/test/v21/test_indicator.py::test_indicator_required_property_pattern stix2/test/v21/test_indicator.py::test_indicator_created_ref_invalid_format stix2/test/v21/test_indicator.py::test_indicator_revoked_invalid stix2/test/v21/test_indicator.py::test_cannot_assign_to_indicator_attributes stix2/test/v21/test_indicator.py::test_invalid_kwarg_to_indicator stix2/test/v21/test_indicator.py::test_created_modified_time_are_identical_by_default stix2/test/v21/test_indicator.py::test_parse_indicator[{\n stix2/test/v21/test_indicator.py::test_parse_indicator[data1] stix2/test/v21/test_indicator.py::test_invalid_indicator_pattern stix2/test/v21/test_indicator.py::test_indicator_with_custom_embedded_objs stix2/test/v21/test_indicator.py::test_indicator_with_custom_embed_objs_extra_props_error stix2/test/v21/test_indicator.py::test_indicator_stix20_invalid_pattern
stix2/v21/common.py stix2/custom.py docs/guide/custom.ipynb stix2/v21/observables.py stix2/datastore/filesystem.py
stix2/v21/common.py stix2/custom.py docs/guide/custom.ipynb stix2/v21/observables.py stix2/datastore/filesystem.py