From 42962e6675b05e4c838cd67e6485a11c3072b7ab Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 1 Sep 2017 16:37:49 -0400 Subject: [PATCH] Improve markings tests - Test markings functions with both dictionaries and our _STIXBase-derived classes as input. - Slightly improve test coverage. - Drop Python 2.6 support. --- .travis.yml | 1 - setup.py | 1 - stix2/__init__.py | 2 +- stix2/common.py | 5 +- stix2/core.py | 5 +- stix2/markings/granular_markings.py | 11 +- stix2/markings/object_markings.py | 9 +- stix2/observables.py | 5 +- stix2/sdo.py | 5 +- stix2/sro.py | 5 +- stix2/test/constants.py | 7 +- stix2/test/test_granular_markings.py | 266 +++++++++++++++++---------- stix2/test/test_object_markings.py | 120 ++++++++---- stix2/utils.py | 3 +- tox.ini | 3 +- 15 files changed, 277 insertions(+), 171 deletions(-) diff --git a/.travis.yml b/.travis.yml index fdbb686..aba764d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ sudo: false language: python cache: pip python: - - "2.6" - "2.7" - "3.3" - "3.4" diff --git a/setup.py b/setup.py index 3687e57..e359147 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,6 @@ setup( keywords="stix stix2 json cti cyber threat intelligence", packages=find_packages(), install_requires=[ - 'ordereddict ; python_version<"2.7"', 'python-dateutil', 'pytz', 'requests', diff --git a/stix2/__init__.py b/stix2/__init__.py index 6e89531..c2aae2e 100644 --- a/stix2/__init__.py +++ b/stix2/__init__.py @@ -42,5 +42,5 @@ from .sdo import (AttackPattern, Campaign, CourseOfAction, CustomObject, Identity, Indicator, IntrusionSet, Malware, ObservedData, Report, ThreatActor, Tool, Vulnerability) from .sro import Relationship, Sighting -from .utils import get_dict +from .utils import get_dict, new_version, revoke from .version import __version__ diff --git a/stix2/common.py b/stix2/common.py index 6242989..a2e6918 100644 --- a/stix2/common.py +++ b/stix2/common.py @@ -1,9 +1,6 @@ """STIX 2 Common Data Types and Properties""" -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict from .base import _STIXBase from .properties import (HashesProperty, IDProperty, ListProperty, Property, diff --git a/stix2/core.py b/stix2/core.py index 0d0d1d2..be2a53d 100644 --- a/stix2/core.py +++ b/stix2/core.py @@ -1,9 +1,6 @@ """STIX 2.0 Objects that are neither SDOs nor SROs""" -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict from . import exceptions from .base import _STIXBase diff --git a/stix2/markings/granular_markings.py b/stix2/markings/granular_markings.py index 3ccc1f0..7e9ccc7 100644 --- a/stix2/markings/granular_markings.py +++ b/stix2/markings/granular_markings.py @@ -1,6 +1,7 @@ from stix2 import exceptions from stix2.markings import utils +from stix2.utils import new_version def get_markings(obj, selectors, inherited=False, descendants=False): @@ -115,9 +116,9 @@ def remove_markings(obj, marking, selectors): granular_markings = utils.compress_markings(granular_markings) if granular_markings: - return obj.new_version(granular_markings=granular_markings) + return new_version(obj, granular_markings=granular_markings) else: - return obj.new_version(granular_markings=None) + return new_version(obj, granular_markings=None) def add_markings(obj, marking, selectors): @@ -153,7 +154,7 @@ def add_markings(obj, marking, selectors): granular_marking = utils.expand_markings(granular_marking) granular_marking = utils.compress_markings(granular_marking) - return obj.new_version(granular_markings=granular_marking) + return new_version(obj, granular_markings=granular_marking) def clear_markings(obj, selectors): @@ -208,9 +209,9 @@ def clear_markings(obj, selectors): granular_markings = utils.compress_markings(granular_markings) if granular_markings: - return obj.new_version(granular_markings=granular_markings) + return new_version(obj, granular_markings=granular_markings) else: - return obj.new_version(granular_markings=None) + return new_version(obj, granular_markings=None) def is_marked(obj, marking=None, selectors=None, inherited=False, descendants=False): diff --git a/stix2/markings/object_markings.py b/stix2/markings/object_markings.py index a3b2586..c39c036 100644 --- a/stix2/markings/object_markings.py +++ b/stix2/markings/object_markings.py @@ -1,6 +1,7 @@ from stix2 import exceptions from stix2.markings import utils +from stix2.utils import new_version def get_markings(obj): @@ -34,7 +35,7 @@ def add_markings(obj, marking): object_markings = set(obj.get("object_marking_refs", []) + marking) - return obj.new_version(object_marking_refs=list(object_markings)) + return new_version(obj, object_marking_refs=list(object_markings)) def remove_markings(obj, marking): @@ -66,9 +67,9 @@ def remove_markings(obj, marking): new_markings = [x for x in object_markings if x not in marking] if new_markings: - return obj.new_version(object_marking_refs=new_markings) + return new_version(obj, object_marking_refs=new_markings) else: - return obj.new_version(object_marking_refs=None) + return new_version(obj, object_marking_refs=None) def set_markings(obj, marking): @@ -100,7 +101,7 @@ def clear_markings(obj): A new version of the given SDO or SRO with object_marking_refs cleared. """ - return obj.new_version(object_marking_refs=None) + return new_version(obj, object_marking_refs=None) def is_marked(obj, marking=None): diff --git a/stix2/observables.py b/stix2/observables.py index b9dcf7f..4caaaa5 100644 --- a/stix2/observables.py +++ b/stix2/observables.py @@ -5,10 +5,7 @@ embedded in Email Message objects, inherit from _STIXBase instead of Observable and do not have a '_type' attribute. """ -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict from .base import _Extension, _Observable, _STIXBase from .exceptions import (AtLeastOnePropertyError, DependentPropertiesError, diff --git a/stix2/sdo.py b/stix2/sdo.py index 8c27d11..77c781a 100644 --- a/stix2/sdo.py +++ b/stix2/sdo.py @@ -1,9 +1,6 @@ """STIX 2.0 Domain Objects""" -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict import stix2 diff --git a/stix2/sro.py b/stix2/sro.py index 05a29ed..af483bc 100644 --- a/stix2/sro.py +++ b/stix2/sro.py @@ -1,9 +1,6 @@ """STIX 2.0 Relationship Objects.""" -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict from .base import _STIXBase from .common import ExternalReference, GranularMarking diff --git a/stix2/test/constants.py b/stix2/test/constants.py index c93f803..839b547 100644 --- a/stix2/test/constants.py +++ b/stix2/test/constants.py @@ -31,7 +31,8 @@ MARKING_IDS = [ # All required args for a Campaign instance, plus some optional args CAMPAIGN_MORE_KWARGS = dict( - id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", + type='campaign', + id=CAMPAIGN_ID, created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", created="2016-04-06T20:03:00.000Z", modified="2016-04-06T20:03:00.000Z", @@ -59,6 +60,10 @@ MALWARE_KWARGS = dict( # All required args for a Malware instance, plus some optional args MALWARE_MORE_KWARGS = dict( + type='malware', + id=MALWARE_ID, + created="2016-04-06T20:03:00.000Z", + modified="2016-04-06T20:03:00.000Z", labels=['ransomware'], name="Cryptolocker", description="A ransomware related to ..." diff --git a/stix2/test/test_granular_markings.py b/stix2/test/test_granular_markings.py index 2d3aa6a..e910ad3 100644 --- a/stix2/test/test_granular_markings.py +++ b/stix2/test/test_granular_markings.py @@ -3,17 +3,12 @@ import pytest from stix2 import Malware, markings -from .constants import FAKE_TIME, MALWARE_ID, MARKING_IDS from .constants import MALWARE_MORE_KWARGS as MALWARE_KWARGS_CONST +from .constants import MARKING_IDS """Tests for the Data Markings API.""" MALWARE_KWARGS = MALWARE_KWARGS_CONST.copy() -MALWARE_KWARGS.update({ - 'id': MALWARE_ID, - 'created': FAKE_TIME, - 'modified': FAKE_TIME, -}) def test_add_marking_mark_one_selector_multiple_refs(): @@ -39,19 +34,34 @@ def test_add_marking_mark_one_selector_multiple_refs(): assert m in after["granular_markings"] -def test_add_marking_mark_multiple_selector_one_refs(): - before = Malware( - **MALWARE_KWARGS - ) - after = Malware( - granular_markings=[ - { - "selectors": ["description", "name"], - "marking_ref": MARKING_IDS[0] - }, - ], - **MALWARE_KWARGS - ) +@pytest.mark.parametrize("data", [ + ( + Malware(**MALWARE_KWARGS), + Malware( + granular_markings=[ + { + "selectors": ["description", "name"], + "marking_ref": MARKING_IDS[0] + }, + ], + **MALWARE_KWARGS), + ), + ( + MALWARE_KWARGS, + dict( + granular_markings=[ + { + "selectors": ["description", "name"], + "marking_ref": MARKING_IDS[0] + }, + ], + **MALWARE_KWARGS), + ), +]) +def test_add_marking_mark_multiple_selector_one_refs(data): + before = data[0] + after = data[1] + before = markings.add_markings(before, [MARKING_IDS[0]], ["description", "name"]) for m in before["granular_markings"]: @@ -337,8 +347,8 @@ def test_get_markings_positional_arguments_combinations(data): assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"]) -def test_remove_marking_remove_one_selector_with_multiple_refs(): - before = Malware( +@pytest.mark.parametrize("before", [ + Malware( granular_markings=[ { "selectors": ["description"], @@ -347,10 +357,25 @@ def test_remove_marking_remove_one_selector_with_multiple_refs(): { "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 + ), +]) +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 @@ -501,49 +526,65 @@ def test_remove_marking_bad_selector(): markings.remove_markings(before, ["marking-definition--1", "marking-definition--2"], ["title"]) -IS_MARKED_TEST_DATA = { - "title": "test title", - "description": "test description", - "revision": 2, - "type": "test", - "granular_markings": [ - { - "selectors": ["description"], - "marking_ref": "marking-definition--1" - }, - { - "selectors": ["revision", "description"], - "marking_ref": "marking-definition--2" - }, - { - "selectors": ["revision", "description"], - "marking_ref": "marking-definition--3" - }, - ] -} +IS_MARKED_TEST_DATA = [ + Malware( + granular_markings=[ + { + "selectors": ["description"], + "marking_ref": MARKING_IDS[1] + }, + { + "selectors": ["labels", "description"], + "marking_ref": MARKING_IDS[2] + }, + { + "selectors": ["labels", "description"], + "marking_ref": MARKING_IDS[3] + }, + ], + **MALWARE_KWARGS + ), + dict( + granular_markings=[ + { + "selectors": ["description"], + "marking_ref": MARKING_IDS[1] + }, + { + "selectors": ["labels", "description"], + "marking_ref": MARKING_IDS[2] + }, + { + "selectors": ["labels", "description"], + "marking_ref": MARKING_IDS[3] + }, + ], + **MALWARE_KWARGS + ), +] -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_smoke(data): """Smoke test is_marked call does not fail.""" assert markings.is_marked(data, selectors=["description"]) - assert markings.is_marked(data, selectors=["title"]) is False + assert markings.is_marked(data, selectors=["modified"]) is False @pytest.mark.parametrize("data,selector", [ - (IS_MARKED_TEST_DATA, "foo"), - (IS_MARKED_TEST_DATA, ""), - (IS_MARKED_TEST_DATA, []), - (IS_MARKED_TEST_DATA, [""]), - (IS_MARKED_TEST_DATA, "x.z.[-2]"), - (IS_MARKED_TEST_DATA, "c.f"), - (IS_MARKED_TEST_DATA, "c.[2].i"), - (IS_MARKED_TEST_DATA, "c.[3]"), - (IS_MARKED_TEST_DATA, "d"), - (IS_MARKED_TEST_DATA, "x.[0]"), - (IS_MARKED_TEST_DATA, "z.y.w"), - (IS_MARKED_TEST_DATA, "x.z.[1]"), - (IS_MARKED_TEST_DATA, "x.z.foo3") + (IS_MARKED_TEST_DATA[0], "foo"), + (IS_MARKED_TEST_DATA[0], ""), + (IS_MARKED_TEST_DATA[0], []), + (IS_MARKED_TEST_DATA[0], [""]), + (IS_MARKED_TEST_DATA[0], "x.z.[-2]"), + (IS_MARKED_TEST_DATA[0], "c.f"), + (IS_MARKED_TEST_DATA[0], "c.[2].i"), + (IS_MARKED_TEST_DATA[1], "c.[3]"), + (IS_MARKED_TEST_DATA[1], "d"), + (IS_MARKED_TEST_DATA[1], "x.[0]"), + (IS_MARKED_TEST_DATA[1], "z.y.w"), + (IS_MARKED_TEST_DATA[1], "x.z.[1]"), + (IS_MARKED_TEST_DATA[1], "x.z.foo3") ]) def test_is_marked_invalid_selector(data, selector): """Test invalid selector raises an error.""" @@ -551,46 +592,54 @@ def test_is_marked_invalid_selector(data, selector): markings.is_marked(data, selectors=selector) -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_mix_selector(data): """Test valid selector, one marked and one not marked returns True.""" - assert markings.is_marked(data, selectors=["description", "revision"]) + assert markings.is_marked(data, selectors=["description", "labels"]) assert markings.is_marked(data, selectors=["description"]) -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_valid_selector_no_refs(data): """Test that a valid selector return True when it has marking refs and False when not.""" assert markings.is_marked(data, selectors=["description"]) - assert markings.is_marked(data, ["marking-definition--2", "marking-definition--3"], ["description"]) - assert markings.is_marked(data, ["marking-definition--2"], ["description"]) - assert markings.is_marked(data, ["marking-definition--2", "marking-definition--8"], ["description"]) is False + assert markings.is_marked(data, [MARKING_IDS[2], MARKING_IDS[3]], ["description"]) + assert markings.is_marked(data, [MARKING_IDS[2]], ["description"]) + assert markings.is_marked(data, [MARKING_IDS[2], MARKING_IDS[5]], ["description"]) is False -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_valid_selector_and_refs(data): """Test that a valid selector returns True when marking_refs match.""" - assert markings.is_marked(data, ["marking-definition--1"], ["description"]) - assert markings.is_marked(data, ["marking-definition--1"], ["title"]) is False + assert markings.is_marked(data, [MARKING_IDS[1]], ["description"]) + assert markings.is_marked(data, [MARKING_IDS[1]], ["modified"]) is False -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_valid_selector_multiple_refs(data): """Test that a valid selector returns True if aall marking_refs match. Otherwise False.""" - assert markings.is_marked(data, ["marking-definition--2", "marking-definition--3"], ["revision"]) - assert markings.is_marked(data, ["marking-definition--2", "marking-definition--1"], ["revision"]) is False - assert markings.is_marked(data, "marking-definition--2", ["revision"]) - assert markings.is_marked(data, ["marking-definition--1234"], ["revision"]) is False + assert markings.is_marked(data, [MARKING_IDS[2], MARKING_IDS[3]], ["labels"]) + assert markings.is_marked(data, [MARKING_IDS[2], MARKING_IDS[1]], ["labels"]) is False + assert markings.is_marked(data, MARKING_IDS[2], ["labels"]) + assert markings.is_marked(data, ["marking-definition--1234"], ["labels"]) is False -@pytest.mark.parametrize("data", [IS_MARKED_TEST_DATA]) +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) def test_is_marked_no_marking_refs(data): """Test that a valid content selector with no marking_refs returns True if there is a granular_marking that asserts that field, False otherwise.""" assert markings.is_marked(data, selectors=["type"]) is False - assert markings.is_marked(data, selectors=["revision"]) + assert markings.is_marked(data, selectors=["labels"]) + + +@pytest.mark.parametrize("data", IS_MARKED_TEST_DATA) +def test_is_marked_no_selectors(data): + """Test that we're ensuring 'selectors' is provided.""" + with pytest.raises(TypeError) as excinfo: + markings.granular_markings.is_marked(data) + assert "'selectors' must be provided" in str(excinfo.value) def test_is_marked_positional_arguments_combinations(): @@ -899,47 +948,66 @@ def test_set_marking_mark_same_property_same_marking(): assert m in after["granular_markings"] -CLEAR_MARKINGS_TEST_DATA = Malware( - granular_markings=[ - { - "selectors": ["description"], - "marking_ref": MARKING_IDS[0] - }, - { - "selectors": ["modified", "description"], - "marking_ref": MARKING_IDS[1] - }, - { - "selectors": ["modified", "description", "type"], - "marking_ref": MARKING_IDS[2] - }, - ], - **MALWARE_KWARGS -) +CLEAR_MARKINGS_TEST_DATA = [ + Malware( + granular_markings=[ + { + "selectors": ["description"], + "marking_ref": MARKING_IDS[0] + }, + { + "selectors": ["modified", "description"], + "marking_ref": MARKING_IDS[1] + }, + { + "selectors": ["modified", "description", "type"], + "marking_ref": MARKING_IDS[2] + }, + ], + **MALWARE_KWARGS + ), + dict( + granular_markings=[ + { + "selectors": ["description"], + "marking_ref": MARKING_IDS[0] + }, + { + "selectors": ["modified", "description"], + "marking_ref": MARKING_IDS[1] + }, + { + "selectors": ["modified", "description", "type"], + "marking_ref": MARKING_IDS[2] + }, + ], + **MALWARE_KWARGS + ) +] -@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +@pytest.mark.parametrize("data", CLEAR_MARKINGS_TEST_DATA) def test_clear_marking_smoke(data): """Test clear_marking call does not fail.""" data = markings.clear_markings(data, "modified") assert markings.is_marked(data, "modified") is False -@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +@pytest.mark.parametrize("data", CLEAR_MARKINGS_TEST_DATA) def test_clear_marking_multiple_selectors(data): """Test clearing markings for multiple selectors effectively removes associated markings.""" data = markings.clear_markings(data, ["type", "description"]) assert markings.is_marked(data, ["type", "description"]) is False -@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +@pytest.mark.parametrize("data", CLEAR_MARKINGS_TEST_DATA) def test_clear_marking_one_selector(data): """Test markings associated with one selector were removed.""" data = markings.clear_markings(data, "description") assert markings.is_marked(data, "description") is False -@pytest.mark.parametrize("data", [CLEAR_MARKINGS_TEST_DATA]) +@pytest.mark.parametrize("data", CLEAR_MARKINGS_TEST_DATA) def test_clear_marking_all_selectors(data): data = markings.clear_markings(data, ["description", "type", "modified"]) assert markings.is_marked(data, "description") is False @@ -947,10 +1015,10 @@ def test_clear_marking_all_selectors(data): @pytest.mark.parametrize("data,selector", [ - (CLEAR_MARKINGS_TEST_DATA, "foo"), - (CLEAR_MARKINGS_TEST_DATA, ""), - (CLEAR_MARKINGS_TEST_DATA, []), - (CLEAR_MARKINGS_TEST_DATA, [""]), + (CLEAR_MARKINGS_TEST_DATA[0], "foo"), + (CLEAR_MARKINGS_TEST_DATA[0], ""), + (CLEAR_MARKINGS_TEST_DATA[1], []), + (CLEAR_MARKINGS_TEST_DATA[1], [""]), ]) def test_clear_marking_bad_selector(data, selector): """Test bad selector raises exception.""" diff --git a/stix2/test/test_object_markings.py b/stix2/test/test_object_markings.py index f949042..36e8e4d 100644 --- a/stix2/test/test_object_markings.py +++ b/stix2/test/test_object_markings.py @@ -16,15 +16,21 @@ MALWARE_KWARGS.update({ }) -def test_add_markings_one_marking(): - before = Malware( - **MALWARE_KWARGS - ) - - after = Malware( - object_marking_refs=[MARKING_IDS[0]], - **MALWARE_KWARGS - ) +@pytest.mark.parametrize("data", [ + ( + Malware(**MALWARE_KWARGS), + Malware(object_marking_refs=[MARKING_IDS[0]], + **MALWARE_KWARGS), + ), + ( + MALWARE_KWARGS, + dict(object_marking_refs=[MARKING_IDS[0]], + **MALWARE_KWARGS), + ), +]) +def test_add_markings_one_marking(data): + before = data[0] + after = data[1] before = markings.add_markings(before, MARKING_IDS[0], None) @@ -242,34 +248,49 @@ def test_get_markings_object_and_granular_combinations(data): assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"]) -def test_remove_markings_object_level(): - before = Malware( - object_marking_refs=[MARKING_IDS[0]], - **MALWARE_KWARGS - ) - after = Malware( - **MALWARE_KWARGS - ) +@pytest.mark.parametrize("data", [ + ( + Malware(object_marking_refs=[MARKING_IDS[0]], + **MALWARE_KWARGS), + Malware(**MALWARE_KWARGS), + ), + ( + dict(object_marking_refs=[MARKING_IDS[0]], + **MALWARE_KWARGS), + MALWARE_KWARGS, + ), +]) +def test_remove_markings_object_level(data): + before = data[0] + after = data[1] before = markings.remove_markings(before, MARKING_IDS[0], None) assert 'object_marking_refs' not in before assert 'object_marking_refs' not in after - modified = after.modified + modified = after['modified'] after = markings.remove_markings(after, MARKING_IDS[0], None) - modified == after.modified + modified == after['modified'] -def test_remove_markings_multiple(): - before = Malware( - object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], - **MALWARE_KWARGS - ) - after = Malware( - object_marking_refs=[MARKING_IDS[1]], - **MALWARE_KWARGS - ) +@pytest.mark.parametrize("data", [ + ( + Malware(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + Malware(object_marking_refs=[MARKING_IDS[1]], + **MALWARE_KWARGS), + ), + ( + dict(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + dict(object_marking_refs=[MARKING_IDS[1]], + **MALWARE_KWARGS), + ), +]) +def test_remove_markings_multiple(data): + before = data[0] + after = data[1] before = markings.remove_markings(before, [MARKING_IDS[0], MARKING_IDS[2]], None) @@ -286,14 +307,21 @@ def test_remove_markings_bad_markings(): assert str(excinfo.value) == "Marking ['%s'] was not found in Malware!" % MARKING_IDS[4] -def test_clear_markings(): - before = Malware( - object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], - **MALWARE_KWARGS - ) - after = Malware( - **MALWARE_KWARGS - ) +@pytest.mark.parametrize("data", [ + ( + Malware(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + Malware(**MALWARE_KWARGS), + ), + ( + dict(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + MALWARE_KWARGS, + ), +]) +def test_clear_markings(data): + before = data[0] + after = data[1] before = markings.clear_markings(before, None) @@ -444,6 +472,26 @@ def test_is_marked_object_and_granular_combinations(): assert markings.is_marked(test_sdo, ["2"], None, True, True) is False +@pytest.mark.parametrize("data", [ + ( + Malware(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + Malware(**MALWARE_KWARGS), + ), + ( + dict(object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], + **MALWARE_KWARGS), + MALWARE_KWARGS, + ), +]) +def test_is_marked_no_markings(data): + marked = data[0] + nonmarked = data[1] + + assert markings.is_marked(marked) + assert markings.is_marked(nonmarked) is False + + def test_set_marking(): before = Malware( object_marking_refs=[MARKING_IDS[0], MARKING_IDS[1], MARKING_IDS[2]], diff --git a/stix2/utils.py b/stix2/utils.py index 72a9c8e..ca195f6 100644 --- a/stix2/utils.py +++ b/stix2/utils.py @@ -191,7 +191,8 @@ def new_version(data, **kwargs): if new_modified_property < old_modified_property: raise InvalidValueError(cls, 'modified', "The new modified datetime cannot be before the current modified datatime.") new_obj_inner.update(kwargs) - return cls(**new_obj_inner) + # Exclude properties with a value of 'None' in case data is not an instance of a _STIXBase subclass + return cls(**{k: v for k, v in new_obj_inner.items() if v is not None}) def revoke(data): diff --git a/tox.ini b/tox.ini index b1265ec..bca18c3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py33,py34,py35,py36,pycodestyle,isort-check +envlist = py27,py33,py34,py35,py36,pycodestyle,isort-check [testenv] deps = @@ -36,7 +36,6 @@ commands = [travis] python = - 2.6: py26 2.7: py27, pycodestyle 3.3: py33, pycodestyle 3.4: py34, pycodestyle