diff --git a/CHANGELOG b/CHANGELOG index 9911e84..f4cce28 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,15 @@ CHANGELOG ========= +1.1.3 - 2019-08-12 + +* #258 Ignores empty values for optional fields +* #259 Adds support for lang granular markings +* #261 Prevents instantiation or serialization of TLP marking-definitions that don't follow the spec +* #262 Supports actual objects in _valid_refs instead of just strings +* #264 Supports accessing objects in bundles via STIX Object IDs +* #274 Fixes bug parsing bundle containing custom objects + 1.1.2 - 2019-02-13 * #86 Adds helper function to Location objects to generate a URL to the location in an online map engine. diff --git a/README.rst b/README.rst index c509d46..256e4d6 100644 --- a/README.rst +++ b/README.rst @@ -135,6 +135,9 @@ select additional or substitute Maintainers, per `consensus agreements https://github.com/emmanvg/; WWW: `MITRE Corporation `__ +- `Jason Keirstead `__; GitHub ID: + https://github.com/JasonKeirstead; WWW: `IBM `__ + About OASIS TC Open Repositories -------------------------------- diff --git a/docs/contributing.rst b/docs/contributing.rst index 9aa2f0e..20cab3a 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -109,3 +109,11 @@ then look at the resulting report in ``htmlcov/index.html``. All commits pushed to the ``master`` branch or submitted as a pull request are tested with `Travis-CI `_ automatically. + +Adding a dependency +------------------- + +One of the pre-commit hooks we use in our develoment environment enforces a +consistent ordering to imports. If you need to add a new library as a dependency +please add it to the `known_third_party` section of `.isort.cfg` to make sure +the import is sorted correctly. diff --git a/docs/guide/custom.ipynb b/docs/guide/custom.ipynb index 6031e5f..042f11e 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 */
{\n",
        "    "type": "identity",\n",
-       "    "id": "identity--87aac643-341b-413a-b702-ea5820416155",\n",
-       "    "created": "2018-04-05T18:38:10.269Z",\n",
-       "    "modified": "2018-04-05T18:38:10.269Z",\n",
+       "    "id": "identity--e7fd0fe0-25ff-4fcb-abe5-b6254a9d1a22",\n",
+       "    "created": "2019-07-25T18:18:18.241Z",\n",
+       "    "modified": "2019-07-25T18:18:18.241Z",\n",
        "    "name": "John Smith",\n",
        "    "identity_class": "individual",\n",
        "    "x_foo": "bar"\n",
@@ -194,8 +194,6 @@
     }
    ],
    "source": [
-    "from stix2 import Identity\n",
-    "\n",
     "identity = Identity(name=\"John Smith\",\n",
     "                    identity_class=\"individual\",\n",
     "                    custom_properties={\n",
@@ -289,9 +287,9 @@
        ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n",
        ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
{\n",
        "    "type": "identity",\n",
-       "    "id": "identity--a1ad0a6f-39ab-4642-9a72-aaa198b1eee2",\n",
-       "    "created": "2018-04-05T18:38:12.270Z",\n",
-       "    "modified": "2018-04-05T18:38:12.270Z",\n",
+       "    "id": "identity--033b5f42-c94f-488f-8efa-2b6a167f0d6f",\n",
+       "    "created": "2019-07-25T18:18:21.352Z",\n",
+       "    "modified": "2019-07-25T18:18:21.352Z",\n",
        "    "name": "John Smith",\n",
        "    "identity_class": "individual",\n",
        "    "x_foo": "bar"\n",
@@ -426,6 +424,113 @@
     "print(identity3.x_foo)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "To remove a custom properties, use `new_version()` and set it to `None`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "
{\n",
+       "    "type": "identity",\n",
+       "    "id": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",\n",
+       "    "created": "2015-12-21T19:59:11.000Z",\n",
+       "    "modified": "2019-07-25T18:18:25.927Z",\n",
+       "    "name": "John Smith",\n",
+       "    "identity_class": "individual"\n",
+       "}\n",
+       "
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "identity4 = identity3.new_version(x_foo=None)\n", + "print(identity4)" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/guide/versioning.ipynb b/docs/guide/versioning.ipynb index 6074d00..eb0708a 100644 --- a/docs/guide/versioning.ipynb +++ b/docs/guide/versioning.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "nbsphinx": "hidden" }, @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "nbsphinx": "hidden" }, @@ -63,12 +63,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To create a new version of an existing object, specify the property(ies) you want to change and their new values:" + "To create a new version of an existing object, specify the property(ies) you want to change and their new values. For example, here we change the label from \"anomalous-activity\" to \"malicious-activity\":" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -144,12 +144,13 @@ ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
{\n",
        "    "type": "indicator",\n",
-       "    "id": "indicator--dd052ff6-e404-444b-beb9-eae96d1e79ea",\n",
+       "    "id": "indicator--8ad18fc7-457c-475d-b292-1ec44febe0fd",\n",
        "    "created": "2016-01-01T08:00:00.000Z",\n",
-       "    "modified": "2018-04-05T20:02:51.161Z",\n",
+       "    "modified": "2019-07-25T17:59:34.815Z",\n",
        "    "name": "File hash for Foobar malware",\n",
+       "    "description": "A file indicator",\n",
        "    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",\n",
-       "    "valid_from": "2018-04-05T20:02:51.138312Z",\n",
+       "    "valid_from": "2019-07-25T17:59:34.779826Z",\n",
        "    "labels": [\n",
        "        "malicious-activity"\n",
        "    ]\n",
@@ -160,7 +161,7 @@
        ""
       ]
      },
-     "execution_count": 4,
+     "execution_count": 3,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -170,6 +171,7 @@
     "\n",
     "indicator = Indicator(created=\"2016-01-01T08:00:00.000Z\",\n",
     "                      name=\"File hash for suspicious file\",\n",
+    "                      description=\"A file indicator\",\n",
     "                      labels=[\"anomalous-activity\"],\n",
     "                      pattern=\"[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']\")\n",
     "\n",
@@ -187,7 +189,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 4,
    "metadata": {
     "scrolled": true
    },
@@ -205,6 +207,117 @@
     "indicator.new_version(id=\"indicator--cc42e358-8b9b-493c-9646-6ecd73b41c21\")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "You can remove optional or custom properties by setting them to `None` when you call `new_version()`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "
{\n",
+       "    "type": "indicator",\n",
+       "    "id": "indicator--8ad18fc7-457c-475d-b292-1ec44febe0fd",\n",
+       "    "created": "2016-01-01T08:00:00.000Z",\n",
+       "    "modified": "2019-07-25T17:59:42.648Z",\n",
+       "    "name": "File hash for suspicious file",\n",
+       "    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",\n",
+       "    "valid_from": "2019-07-25T17:59:34.779826Z",\n",
+       "    "labels": [\n",
+       "        "anomalous-activity"\n",
+       "    ]\n",
+       "}\n",
+       "
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "indicator3 = indicator.new_version(description=None)\n", + "print(indicator3)" + ] + }, { "cell_type": "markdown", "metadata": { @@ -292,15 +405,15 @@ ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
{\n",
        "    "type": "indicator",\n",
-       "    "id": "indicator--dd052ff6-e404-444b-beb9-eae96d1e79ea",\n",
+       "    "id": "indicator--8ad18fc7-457c-475d-b292-1ec44febe0fd",\n",
        "    "created": "2016-01-01T08:00:00.000Z",\n",
-       "    "modified": "2018-04-05T20:02:54.704Z",\n",
-       "    "name": "File hash for Foobar malware",\n",
+       "    "modified": "2019-07-25T17:59:52.198Z",\n",
+       "    "name": "File hash for suspicious file",\n",
        "    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",\n",
-       "    "valid_from": "2018-04-05T20:02:51.138312Z",\n",
+       "    "valid_from": "2019-07-25T17:59:34.779826Z",\n",
        "    "revoked": true,\n",
        "    "labels": [\n",
-       "        "malicious-activity"\n",
+       "        "anomalous-activity"\n",
        "    ]\n",
        "}\n",
        "
\n" @@ -315,8 +428,8 @@ } ], "source": [ - "indicator2 = indicator2.revoke()\n", - "print(indicator2)" + "indicator4 = indicator3.revoke()\n", + "print(indicator4)" ] } ], diff --git a/setup.cfg b/setup.cfg index ae45560..b012bb9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.2 +current_version = 1.1.3 commit = True tag = True diff --git a/setup.py b/setup.py index 07de2a4..497bf01 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ setup( version=get_version(), description='Produce and consume STIX 2 JSON content', long_description=get_long_description(), + long_description_content_type='text/x-rst', url='https://oasis-open.github.io/cti-documentation/', author='OASIS Cyber Threat Intelligence Technical Committee', author_email='cti-users@lists.oasis-open.org', @@ -47,7 +48,7 @@ setup( 'Programming Language :: Python :: 3.7', ], keywords='stix stix2 json cti cyber threat intelligence', - packages=find_packages(exclude=['*.test']), + packages=find_packages(exclude=['*.test', '*.test.*']), install_requires=[ 'python-dateutil', 'pytz', diff --git a/stix2/test/v21/test_markings.py b/stix2/test/v21/test_markings.py index bd247e6..1f9f5e8 100644 --- a/stix2/test/v21/test_markings.py +++ b/stix2/test/v21/test_markings.py @@ -25,7 +25,7 @@ EXPECTED_STATEMENT_MARKING_DEFINITION = """{ "type": "marking-definition", "spec_version": "2.1", "id": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", - "created": "2017-01-20T00:00:00Z", + "created": "2017-01-20T00:00:00.000Z", "definition_type": "statement", "definition": { "statement": "Copyright 2016, Example Corp" diff --git a/stix2/v20/bundle.py b/stix2/v20/bundle.py index 9e0b313..1383757 100644 --- a/stix2/v20/bundle.py +++ b/stix2/v20/bundle.py @@ -38,7 +38,7 @@ class Bundle(_STIXBase): def get_obj(self, obj_uuid): if "objects" in self._inner: - found_objs = [elem for elem in self.objects if elem.id == obj_uuid] + found_objs = [elem for elem in self.objects if elem['id'] == obj_uuid] if found_objs == []: raise KeyError("'%s' does not match the id property of any of the bundle's objects" % obj_uuid) return found_objs diff --git a/stix2/v20/common.py b/stix2/v20/common.py index 377d992..a1ffa60 100644 --- a/stix2/v20/common.py +++ b/stix2/v20/common.py @@ -3,6 +3,8 @@ from collections import OrderedDict import copy +import six + from ..base import _STIXBase from ..custom import _custom_marking_builder from ..markings import _MarkingsMixin @@ -14,6 +16,21 @@ from ..properties import ( from ..utils import NOW, _get_dict +def _should_set_millisecond(cr, marking_type): + # TLP instances in the 2.0 spec have millisecond precision unlike other markings + if marking_type == TLPMarking: + return True + # otherwise, precision is kept from how it was given + if isinstance(cr, six.string_types): + if '.' in cr: + return True + else: + return False + if cr.precision == 'millisecond': + return True + return False + + class ExternalReference(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.0 specification `__. @@ -122,12 +139,12 @@ class MarkingDefinition(_STIXBase, _MarkingsMixin): except KeyError: raise ValueError("definition_type must be a valid marking type") - if marking_type == TLPMarking: - # TLP instances in the spec have millisecond precision unlike other markings - self._properties = copy.deepcopy(self._properties) - self._properties.update([ - ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), - ]) + if 'created' in kwargs: + if _should_set_millisecond(kwargs['created'], marking_type): + self._properties = copy.deepcopy(self._properties) + self._properties.update([ + ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), + ]) if not isinstance(kwargs['definition'], marking_type): defn = _get_dict(kwargs['definition']) diff --git a/stix2/v21/bundle.py b/stix2/v21/bundle.py index 4ebd96c..fb35be2 100644 --- a/stix2/v21/bundle.py +++ b/stix2/v21/bundle.py @@ -36,7 +36,7 @@ class Bundle(_STIXBase): def get_obj(self, obj_uuid): if "objects" in self._inner: - found_objs = [elem for elem in self.objects if elem.id == obj_uuid] + found_objs = [elem for elem in self.objects if elem['id'] == obj_uuid] if found_objs == []: raise KeyError("'%s' does not match the id property of any of the bundle's objects" % obj_uuid) return found_objs diff --git a/stix2/v21/common.py b/stix2/v21/common.py index 44d3675..a31e710 100644 --- a/stix2/v21/common.py +++ b/stix2/v21/common.py @@ -1,7 +1,6 @@ """STIX 2.1 Common Data Types and Properties.""" from collections import OrderedDict -import copy from ..base import _STIXBase from ..custom import _custom_marking_builder @@ -146,7 +145,7 @@ class MarkingDefinition(_STIXBase, _MarkingsMixin): ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type)), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), - ('created', TimestampProperty(default=lambda: NOW)), + ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), @@ -162,13 +161,6 @@ class MarkingDefinition(_STIXBase, _MarkingsMixin): except KeyError: raise ValueError("definition_type must be a valid marking type") - if marking_type == TLPMarking: - # TLP instances in the spec have millisecond precision unlike other markings - self._properties = copy.deepcopy(self._properties) - self._properties.update([ - ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), - ]) - if not isinstance(kwargs['definition'], marking_type): defn = _get_dict(kwargs['definition']) kwargs['definition'] = marking_type(**defn) diff --git a/stix2/version.py b/stix2/version.py index 72f26f5..0b2f79d 100644 --- a/stix2/version.py +++ b/stix2/version.py @@ -1 +1 @@ -__version__ = "1.1.2" +__version__ = "1.1.3" diff --git a/tox.ini b/tox.ini index d4ab42c..2225bae 100644 --- a/tox.ini +++ b/tox.ini @@ -32,9 +32,10 @@ commands = [testenv:packaging] deps = - readme_renderer + twine commands = - python setup.py check -r -s + python setup.py bdist_wheel --universal + twine check dist/* [travis] python =