diff --git a/docs/guide/extensions.ipynb b/docs/guide/extensions.ipynb index 0283981..817b08a 100644 --- a/docs/guide/extensions.ipynb +++ b/docs/guide/extensions.ipynb @@ -57,10 +57,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "STIX Extensions\n", + "## STIX Extensions\n", "\n", "This page is specific for the STIX Extensions mechanism defined in STIX 2.1 CS 02, for the deprecated STIX Customization mechanisms see the [Custom](custom.ipynb) section.\n", "\n", + "### Top Level Property Extensions\n", + "\n", "The example below shows how to create an `indicator` object with a `top-level-property-extension`. " ] }, @@ -332,6 +334,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### Using CustomExtension decorator\n", + "\n", "However, in order to prevent repetitive instantiation of the same extension, the `@CustomExtension` when used in a class for registering the `extension-definition` with stix2, it allows passing the id. Use the `extension_type` class variable to define what kind of extension it is." ] }, @@ -490,7 +494,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Similarly, now when registering new objects `@CustomObject` now supports passing an `extension-definition` id. \n", + "### Using CustomObservable for Extension Definition\n", + "\n", + "Similarly, when registering new objects via `@CustomObservable` you can pass the `extension-definition` id that defines this new SCO. \n", "\n", "---\n", "**Note:**\n", @@ -501,7 +507,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -598,7 +604,7 @@ "" ] }, - "execution_count": 15, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -626,6 +632,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### Using CustomMarking for Extension Definition\n", + "\n", "The example below shows the use for MarkingDefinition extensions. Currently this is only supported as a `property-extension`. Now, as another option to building the `extensions` as a dictionary, it can also be built with objects as shown below by extracting the registered class." ] }, @@ -757,10 +765,148 @@ "\n", "print(my_favorite_marking.serialize(pretty=True))" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using CustomObject for Extension Definition\n", + "\n", + "Similar to the examples above, the same can be done for SDOs and SROs." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{\n",
+       "    "type": "my-favorite-sro",\n",
+       "    "spec_version": "2.1",\n",
+       "    "id": "my-favorite-sro--d6306d62-c08d-4d78-baf7-11e7a4c9bc36",\n",
+       "    "created": "2021-03-31T22:43:42.807698Z",\n",
+       "    "modified": "2021-03-31T22:43:42.807698Z",\n",
+       "    "name": "My First SRO",\n",
+       "    "some_source_ref": "identity--b1da8c3e-34d8-470f-9d2b-392e275f1f7d",\n",
+       "    "some_target_ref": "identity--1ddfed54-e8cd-49c9-9c7d-8d1b03c94685",\n",
+       "    "extensions": {\n",
+       "        "extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce": {\n",
+       "            "extension_type": "new-sro"\n",
+       "        }\n",
+       "    }\n",
+       "}\n",
+       "
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "invalid_refs = ['bundle', 'language-content', 'marking-definition', 'relationship', 'sighting', 'foobar']\n", + "\n", + "@stix2.v21.CustomObject(\n", + " 'my-favorite-sro', [\n", + " ('name', stix2.properties.StringProperty(required=False)),\n", + " ('some_source_ref', stix2.properties.ReferenceProperty(invalid_types=invalid_refs, spec_version='2.1', required=True)),\n", + " ('some_target_ref', stix2.properties.ReferenceProperty(invalid_types=invalid_refs, spec_version='2.1', required=True)),\n", + " ], extension_name='extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce', is_sdo=False,\n", + ")\n", + "class MyFavSRO:\n", + " pass\n", + "\n", + "\n", + "my_favorite_sro = MyFavSRO(\n", + " name=\"My First SRO\",\n", + " some_source_ref=\"identity--b1da8c3e-34d8-470f-9d2b-392e275f1f7d\",\n", + " some_target_ref=\"identity--1ddfed54-e8cd-49c9-9c7d-8d1b03c94685\",\n", + ")\n", + "\n", + "print(my_favorite_sro.serialize(pretty=True))" + ] } ], "metadata": { - "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "language": "python", diff --git a/stix2/test/v21/test_custom.py b/stix2/test/v21/test_custom.py index ac46d84..2fc8db0 100644 --- a/stix2/test/v21/test_custom.py +++ b/stix2/test/v21/test_custom.py @@ -1462,6 +1462,41 @@ def test_registered_new_extension_sdo_allow_custom_false(): assert '"extensions": {"extension-definition--d83fce45-ef58-4c6c-a3f4-1fbc32e9999": {"extension_type": "new-sdo"}}' in sdo_serialized +def test_registered_new_extension_sro_allow_custom_false(): + @stix2.v21.CustomObject( + 'my-favorite-sro', [ + ('name', stix2.properties.StringProperty(required=True)), + ('some_property_name1', stix2.properties.StringProperty(required=True)), + ('some_property_name2', stix2.properties.StringProperty()), + ], 'extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce', False, + ) + class MyFavSRO: + pass + + my_favorite_sro = { + 'type': 'my-favorite-sro', + 'spec_version': '2.1', + 'id': 'my-favorite-sro--c5ba9dba-5ad9-4bbe-9825-df4cb8675774', + 'created': '2014-02-20T09:16:08.989000Z', + 'modified': '2014-02-20T09:16:08.989000Z', + 'name': 'This is the name of my favorite', + 'some_property_name1': 'value1', + 'some_property_name2': 'value2', + # 'extensions': { + # 'extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce': ExtensionDefinitiond83fce45ef584c6ca3f41fbc32e98c6e() + # } + } + sro_object = stix2.parse(my_favorite_sro) + assert isinstance(sro_object, MyFavSRO) + assert isinstance( + sro_object.extensions['extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce'], + stix2.v21.EXT_MAP['extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce'], + ) + + sdo_serialized = sro_object.serialize() + assert '"extensions": {"extension-definition--e96690a5-dc13-4f27-99dd-0f2188ad74ce": {"extension_type": "new-sro"}}' in sdo_serialized + + def test_registered_new_extension_sco_allow_custom_false(): @stix2.v21.CustomObservable( 'my-favorite-sco', [ diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1ca448a..a834ce5 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -806,7 +806,7 @@ class Vulnerability(_DomainObject): ]) -def CustomObject(type='x-custom-type', properties=None, extension_name=None): +def CustomObject(type='x-custom-type', properties=None, extension_name=None, is_sdo=True): """Custom STIX Object type decorator. Example: @@ -864,7 +864,10 @@ def CustomObject(type='x-custom-type', properties=None, extension_name=None): if extension_name: @observables.CustomExtension(type=extension_name, properties=extension_properties) class NameExtension: - extension_type = 'new-sdo' + if is_sdo: + extension_type = 'new-sdo' + else: + extension_type = 'new-sro' extension = extension_name.split('--')[1] extension = extension.replace('-', '')