cti-python-stix2/stix2/test/v21/test_granular_markings.py

1324 lines
43 KiB
Python
Raw Normal View History

import pytest
from stix2 import markings
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
from stix2.exceptions import InvalidSelectorError, MarkingNotFoundError
2018-07-10 22:05:30 +02:00
from stix2.v21 import TLP_RED, Malware
from .constants import MALWARE_MORE_KWARGS as MALWARE_KWARGS_CONST
from .constants import MARKING_IDS, MARKING_LANGS
"""Tests for the Data Markings API."""
MALWARE_KWARGS = MALWARE_KWARGS_CONST.copy()
def test_add_marking_mark_one_selector_multiple_refs():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.add_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
@pytest.mark.parametrize(
"data", [
(
Malware(**MALWARE_KWARGS),
Malware(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
),
MARKING_IDS[0],
),
(
MALWARE_KWARGS,
dict(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
),
MARKING_IDS[0],
),
(
Malware(**MALWARE_KWARGS),
Malware(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": TLP_RED.id,
},
],
**MALWARE_KWARGS
),
TLP_RED,
),
],
)
def test_add_marking_mark_multiple_selector_one_refs(data):
before = data[0]
after = data[1]
before = markings.add_markings(before, data[2], ["description", "name"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_add_marking_mark_multiple_selector_multiple_refs():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.add_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description", "name"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_add_marking_mark_multiple_selector_multiple_refs_mixed():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[1],
},
{
"selectors": ["description", "name"],
"lang": MARKING_LANGS[0],
},
{
"selectors": ["description", "name"],
"lang": MARKING_LANGS[1],
},
],
**MALWARE_KWARGS
)
before = markings.add_markings(before, [MARKING_IDS[0], MARKING_IDS[1], MARKING_LANGS[0], MARKING_LANGS[1]], ["description", "name"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_add_marking_mark_another_property_same_marking():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "name"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.add_markings(before, [MARKING_IDS[0]], ["name"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_add_marking_mark_same_property_same_marking():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.add_markings(before, [MARKING_IDS[0]], ["description"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
@pytest.mark.parametrize(
"data,marking", [
(
{"description": "test description"},
[
["title"], ["marking-definition--1", "marking-definition--2"],
"", ["marking-definition--1", "marking-definition--2"],
[], ["marking-definition--1", "marking-definition--2"],
[""], ["marking-definition--1", "marking-definition--2"],
["description"], [""],
["description"], [],
["description"], ["marking-definition--1", 456],
],
),
],
)
def test_add_marking_bad_selector(data, marking):
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
markings.add_markings(data, marking[0], marking[1])
GET_MARKINGS_TEST_DATA = {
"a": 333,
"b": "value",
"c": [
17,
"list value",
{
"g": "nested",
"h": 45,
},
],
"x": {
"y": [
"hello",
88,
],
"z": {
"foo1": "bar",
"foo2": 65,
},
},
"granular_markings": [
{
"marking_ref": "1",
"selectors": ["a"],
},
{
"marking_ref": "2",
"selectors": ["c"],
},
{
"marking_ref": "3",
"selectors": ["c.[1]"],
},
{
"marking_ref": "4",
"selectors": ["c.[2]"],
},
{
"marking_ref": "5",
"selectors": ["c.[2].g"],
},
{
"marking_ref": "6",
"selectors": ["x"],
},
{
"marking_ref": "7",
"selectors": ["x.y"],
},
{
"marking_ref": "8",
"selectors": ["x.y.[1]"],
},
{
"marking_ref": "9",
"selectors": ["x.z"],
},
{
"marking_ref": "10",
"selectors": ["x.z.foo2"],
},
],
}
@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA])
def test_get_markings_smoke(data):
"""Test get_markings does not fail."""
assert len(markings.get_markings(data, "a")) >= 1
assert markings.get_markings(data, "a") == ["1"]
@pytest.mark.parametrize(
"data", [
GET_MARKINGS_TEST_DATA,
{"b": 1234},
],
)
def test_get_markings_not_marked(data):
"""Test selector that is not marked returns empty list."""
results = markings.get_markings(data, "b")
assert len(results) == 0
@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA])
def test_get_markings_multiple_selectors(data):
"""Test multiple selectors return combination of markings."""
total = markings.get_markings(data, ["x.y", "x.z"])
xy_markings = markings.get_markings(data, ["x.y"])
xz_markings = markings.get_markings(data, ["x.z"])
assert set(xy_markings).issubset(total)
assert set(xz_markings).issubset(total)
assert set(xy_markings).union(xz_markings).issuperset(total)
@pytest.mark.parametrize(
"data,selector", [
(GET_MARKINGS_TEST_DATA, "foo"),
(GET_MARKINGS_TEST_DATA, ""),
(GET_MARKINGS_TEST_DATA, []),
(GET_MARKINGS_TEST_DATA, [""]),
(GET_MARKINGS_TEST_DATA, "x.z.[-2]"),
(GET_MARKINGS_TEST_DATA, "c.f"),
(GET_MARKINGS_TEST_DATA, "c.[2].i"),
(GET_MARKINGS_TEST_DATA, "c.[3]"),
(GET_MARKINGS_TEST_DATA, "d"),
(GET_MARKINGS_TEST_DATA, "x.[0]"),
(GET_MARKINGS_TEST_DATA, "z.y.w"),
(GET_MARKINGS_TEST_DATA, "x.z.[1]"),
(GET_MARKINGS_TEST_DATA, "x.z.foo3"),
],
)
def test_get_markings_bad_selector(data, selector):
"""Test bad selectors raise exception"""
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
markings.get_markings(data, selector)
@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA])
def test_get_markings_positional_arguments_combinations(data):
"""Test multiple combinations for inherited and descendant markings."""
assert set(markings.get_markings(data, "a", False, False)) == set(["1"])
assert set(markings.get_markings(data, "a", True, False)) == set(["1"])
assert set(markings.get_markings(data, "a", True, True)) == set(["1"])
assert set(markings.get_markings(data, "a", False, True)) == set(["1"])
assert set(markings.get_markings(data, "b", False, False)) == set([])
assert set(markings.get_markings(data, "b", True, False)) == set([])
assert set(markings.get_markings(data, "b", True, True)) == set([])
assert set(markings.get_markings(data, "b", False, True)) == set([])
assert set(markings.get_markings(data, "c", False, False)) == set(["2"])
assert set(markings.get_markings(data, "c", True, False)) == set(["2"])
assert set(markings.get_markings(data, "c", True, True)) == set(["2", "3", "4", "5"])
assert set(markings.get_markings(data, "c", False, True)) == set(["2", "3", "4", "5"])
assert set(markings.get_markings(data, "c.[0]", False, False)) == set([])
assert set(markings.get_markings(data, "c.[0]", True, False)) == set(["2"])
assert set(markings.get_markings(data, "c.[0]", True, True)) == set(["2"])
assert set(markings.get_markings(data, "c.[0]", False, True)) == set([])
assert set(markings.get_markings(data, "c.[1]", False, False)) == set(["3"])
assert set(markings.get_markings(data, "c.[1]", True, False)) == set(["2", "3"])
assert set(markings.get_markings(data, "c.[1]", True, True)) == set(["2", "3"])
assert set(markings.get_markings(data, "c.[1]", False, True)) == set(["3"])
assert set(markings.get_markings(data, "c.[2]", False, False)) == set(["4"])
assert set(markings.get_markings(data, "c.[2]", True, False)) == set(["2", "4"])
assert set(markings.get_markings(data, "c.[2]", True, True)) == set(["2", "4", "5"])
assert set(markings.get_markings(data, "c.[2]", False, True)) == set(["4", "5"])
assert set(markings.get_markings(data, "c.[2].g", False, False)) == set(["5"])
assert set(markings.get_markings(data, "c.[2].g", True, False)) == set(["2", "4", "5"])
assert set(markings.get_markings(data, "c.[2].g", True, True)) == set(["2", "4", "5"])
assert set(markings.get_markings(data, "c.[2].g", False, True)) == set(["5"])
assert set(markings.get_markings(data, "x", False, False)) == set(["6"])
assert set(markings.get_markings(data, "x", True, False)) == set(["6"])
assert set(markings.get_markings(data, "x", True, True)) == set(["6", "7", "8", "9", "10"])
assert set(markings.get_markings(data, "x", False, True)) == set(["6", "7", "8", "9", "10"])
assert set(markings.get_markings(data, "x.y", False, False)) == set(["7"])
assert set(markings.get_markings(data, "x.y", True, False)) == set(["6", "7"])
assert set(markings.get_markings(data, "x.y", True, True)) == set(["6", "7", "8"])
assert set(markings.get_markings(data, "x.y", False, True)) == set(["7", "8"])
assert set(markings.get_markings(data, "x.y.[0]", False, False)) == set([])
assert set(markings.get_markings(data, "x.y.[0]", True, False)) == set(["6", "7"])
assert set(markings.get_markings(data, "x.y.[0]", True, True)) == set(["6", "7"])
assert set(markings.get_markings(data, "x.y.[0]", False, True)) == set([])
assert set(markings.get_markings(data, "x.y.[1]", False, False)) == set(["8"])
assert set(markings.get_markings(data, "x.y.[1]", True, False)) == set(["6", "7", "8"])
assert set(markings.get_markings(data, "x.y.[1]", True, True)) == set(["6", "7", "8"])
assert set(markings.get_markings(data, "x.y.[1]", False, True)) == set(["8"])
assert set(markings.get_markings(data, "x.z", False, False)) == set(["9"])
assert set(markings.get_markings(data, "x.z", True, False)) == set(["6", "9"])
assert set(markings.get_markings(data, "x.z", True, True)) == set(["6", "9", "10"])
assert set(markings.get_markings(data, "x.z", False, True)) == set(["9", "10"])
assert set(markings.get_markings(data, "x.z.foo1", False, False)) == set([])
assert set(markings.get_markings(data, "x.z.foo1", True, False)) == set(["6", "9"])
assert set(markings.get_markings(data, "x.z.foo1", True, True)) == set(["6", "9"])
assert set(markings.get_markings(data, "x.z.foo1", False, True)) == set([])
assert set(markings.get_markings(data, "x.z.foo2", False, False)) == set(["10"])
assert set(markings.get_markings(data, "x.z.foo2", True, False)) == set(["6", "9", "10"])
assert set(markings.get_markings(data, "x.z.foo2", True, True)) == set(["6", "9", "10"])
assert set(markings.get_markings(data, "x.z.foo2", False, True)) == set(["10"])
GET_MARKINGS_TEST_DATA_LANGS = {
"a": 333,
"b": "value",
"c": [
17,
"list value",
{
"g": "nested",
"h": 45,
},
],
"x": {
"y": [
"hello",
88,
],
"z": {
"foo1": "bar",
"foo2": 65,
},
},
"granular_markings": [
{
"marking_ref": "m1",
"selectors": ["a"],
},
{
"marking_ref": "m2",
"selectors": ["c"],
},
{
"marking_ref": "m3",
"selectors": ["c.[1]"],
},
{
"marking_ref": "m4",
"selectors": ["c.[2]"],
},
{
"marking_ref": "m5",
"selectors": ["c.[2].g"],
},
{
"marking_ref": "m6",
"selectors": ["x"],
},
{
"lang": "l7",
"selectors": ["x.y"],
},
{
"marking_ref": "m8",
"selectors": ["x.y.[1]"],
},
{
"lang": "l9",
"selectors": ["x.z"],
},
{
"marking_ref": "m9",
"selectors": ["x.z"],
},
{
"marking_ref": "m10",
"selectors": ["x.z.foo2"],
},
],
}
@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA_LANGS])
2019-04-23 13:43:56 +02:00
def test_get_markings_multiple_selectors_langs(data):
"""Test multiple selectors return combination of markings."""
total = markings.get_markings(data, ["x.y", "x.z"])
xy_markings = markings.get_markings(data, ["x.y"])
xz_markings = markings.get_markings(data, ["x.z"])
assert set(xy_markings).issubset(total)
assert set(xz_markings).issubset(total)
assert set(xy_markings).union(xz_markings).issuperset(total)
@pytest.mark.parametrize("data", [GET_MARKINGS_TEST_DATA_LANGS])
def test_get_markings_multiple_selectors_with_options(data):
"""Test multiple selectors return combination of markings."""
total = markings.get_markings(data, ["x.y", "x.z"], lang=False)
xz_markings = markings.get_markings(data, ["x.z"], marking_ref=False)
assert len(total) == 1
assert len(xz_markings) == 1
@pytest.mark.parametrize(
"data", [
(
Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
),
[MARKING_IDS[0], MARKING_IDS[1]],
),
(
dict(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
),
[MARKING_IDS[0], MARKING_IDS[1]],
),
],
)
def test_remove_marking_remove_one_selector_with_multiple_refs(data):
before = markings.remove_markings(data[0], data[1], ["description"])
assert "granular_markings" not in before
def test_remove_marking_remove_multiple_selector_one_ref():
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, MARKING_IDS[0], ["description", "modified"])
assert "granular_markings" not in before
def test_remove_marking_mark_one_selector_from_multiple_ones():
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0]], ["modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_remove_marking_mark_one_selector_from_multiple_ones_mixed():
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description"],
"lang": MARKING_LANGS[0],
},
],
**MALWARE_KWARGS
)
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "modified"],
"lang": MARKING_LANGS[0],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0], MARKING_LANGS[0]], ["modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_remove_marking_mark_one_selector_markings_from_multiple_ones():
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0]], ["modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_remove_marking_mark_mutilple_selector_multiple_refs():
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description", "modified"])
assert "granular_markings" not in before
def test_remove_marking_mark_another_property_same_marking():
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["modified"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0]], ["modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_remove_marking_mark_same_property_same_marking():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.remove_markings(before, [MARKING_IDS[0]], ["description"])
assert "granular_markings" not in before
def test_remove_no_markings():
before = {
"description": "test description",
}
after = markings.remove_markings(before, ["marking-definition--1"], ["description"])
assert before == after
def test_remove_marking_bad_selector():
before = {
"description": "test description",
}
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
markings.remove_markings(before, ["marking-definition--1", "marking-definition--2"], ["title"])
def test_remove_marking_not_present():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
with pytest.raises(MarkingNotFoundError):
markings.remove_markings(before, [MARKING_IDS[1]], ["description"])
IS_MARKED_TEST_DATA = [
Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
{
"selectors": ["malware_types", "description"],
"marking_ref": MARKING_IDS[2],
},
{
"selectors": ["malware_types", "description"],
"marking_ref": MARKING_IDS[3],
},
{
"selectors": ["name"],
"lang": MARKING_LANGS[1],
},
],
**MALWARE_KWARGS
),
dict(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
{
"selectors": ["malware_types", "description"],
"marking_ref": MARKING_IDS[2],
},
{
"selectors": ["malware_types", "description"],
"marking_ref": MARKING_IDS[3],
},
{
"selectors": ["name"],
"lang": MARKING_LANGS[1],
},
],
**MALWARE_KWARGS
),
]
@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=["modified"]) is False
assert markings.is_marked(data, selectors=["name"])
@pytest.mark.parametrize(
"data,selector", [
(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."""
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
markings.is_marked(data, selectors=selector)
@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", "malware_types"])
assert markings.is_marked(data, selectors=["description"])
@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_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)
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_IDS[1]], ["description"])
assert markings.is_marked(data, [MARKING_IDS[1]], ["modified"]) is False
assert markings.is_marked(data, [MARKING_LANGS[1]], ["name"])
@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_IDS[2], MARKING_IDS[3]], ["malware_types"])
assert markings.is_marked(data, [MARKING_IDS[2], MARKING_IDS[1]], ["malware_types"]) is False
assert markings.is_marked(data, MARKING_IDS[2], ["malware_types"])
assert markings.is_marked(data, ["marking-definition--1234"], ["malware_types"]) is False
@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=["malware_types"])
@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():
"""Test multiple combinations for inherited and descendant markings."""
test_sdo = \
{
"a": 333,
"b": "value",
"c": [
17,
"list value",
{
"g": "nested",
"h": 45,
},
],
"x": {
"y": [
"hello",
88,
],
"z": {
"foo1": "bar",
"foo2": 65,
},
},
"granular_markings": [
{
"marking_ref": "1",
"selectors": ["a"],
},
{
"marking_ref": "2",
"selectors": ["c"],
},
{
"marking_ref": "3",
"selectors": ["c.[1]"],
},
{
"marking_ref": "4",
"selectors": ["c.[2]"],
},
{
"marking_ref": "5",
"selectors": ["c.[2].g"],
},
{
"marking_ref": "6",
"selectors": ["x"],
},
{
"marking_ref": "7",
"selectors": ["x.y"],
},
{
"marking_ref": "8",
"selectors": ["x.y.[1]"],
},
{
"marking_ref": "9",
"selectors": ["x.z"],
},
{
"marking_ref": "10",
"selectors": ["x.z.foo2"],
},
],
}
assert markings.is_marked(test_sdo, ["1"], "a", False, False)
assert markings.is_marked(test_sdo, ["1"], "a", True, False)
assert markings.is_marked(test_sdo, ["1"], "a", True, True)
assert markings.is_marked(test_sdo, ["1"], "a", False, True)
assert markings.is_marked(test_sdo, "b", inherited=False, descendants=False) is False
assert markings.is_marked(test_sdo, "b", inherited=True, descendants=False) is False
assert markings.is_marked(test_sdo, "b", inherited=True, descendants=True) is False
assert markings.is_marked(test_sdo, "b", inherited=False, descendants=True) is False
assert markings.is_marked(test_sdo, ["2"], "c", False, False)
assert markings.is_marked(test_sdo, ["2"], "c", True, False)
assert markings.is_marked(test_sdo, ["2", "3", "4", "5"], "c", True, True)
assert markings.is_marked(test_sdo, ["2", "3", "4", "5"], "c", False, True)
assert markings.is_marked(test_sdo, "c.[0]", inherited=False, descendants=False) is False
assert markings.is_marked(test_sdo, ["2"], "c.[0]", True, False)
assert markings.is_marked(test_sdo, ["2"], "c.[0]", True, True)
assert markings.is_marked(test_sdo, "c.[0]", inherited=False, descendants=True) is False
assert markings.is_marked(test_sdo, ["3"], "c.[1]", False, False)
assert markings.is_marked(test_sdo, ["2", "3"], "c.[1]", True, False)
assert markings.is_marked(test_sdo, ["2", "3"], "c.[1]", True, True)
assert markings.is_marked(test_sdo, ["3"], "c.[1]", False, True)
assert markings.is_marked(test_sdo, ["4"], "c.[2]", False, False)
assert markings.is_marked(test_sdo, ["2", "4"], "c.[2]", True, False)
assert markings.is_marked(test_sdo, ["2", "4", "5"], "c.[2]", True, True)
assert markings.is_marked(test_sdo, ["4", "5"], "c.[2]", False, True)
assert markings.is_marked(test_sdo, ["5"], "c.[2].g", False, False)
assert markings.is_marked(test_sdo, ["2", "4", "5"], "c.[2].g", True, False)
assert markings.is_marked(test_sdo, ["2", "4", "5"], "c.[2].g", True, True)
assert markings.is_marked(test_sdo, ["5"], "c.[2].g", False, True)
assert markings.is_marked(test_sdo, ["6"], "x", False, False)
assert markings.is_marked(test_sdo, ["6"], "x", True, False)
assert markings.is_marked(test_sdo, ["6", "7", "8", "9", "10"], "x", True, True)
assert markings.is_marked(test_sdo, ["6", "7", "8", "9", "10"], "x", False, True)
assert markings.is_marked(test_sdo, ["7"], "x.y", False, False)
assert markings.is_marked(test_sdo, ["6", "7"], "x.y", True, False)
assert markings.is_marked(test_sdo, ["6", "7", "8"], "x.y", True, True)
assert markings.is_marked(test_sdo, ["7", "8"], "x.y", False, True)
assert markings.is_marked(test_sdo, "x.y.[0]", inherited=False, descendants=False) is False
assert markings.is_marked(test_sdo, ["6", "7"], "x.y.[0]", True, False)
assert markings.is_marked(test_sdo, ["6", "7"], "x.y.[0]", True, True)
assert markings.is_marked(test_sdo, "x.y.[0]", inherited=False, descendants=True) is False
assert markings.is_marked(test_sdo, ["8"], "x.y.[1]", False, False)
assert markings.is_marked(test_sdo, ["6", "7", "8"], "x.y.[1]", True, False)
assert markings.is_marked(test_sdo, ["6", "7", "8"], "x.y.[1]", True, True)
assert markings.is_marked(test_sdo, ["8"], "x.y.[1]", False, True)
assert markings.is_marked(test_sdo, ["9"], "x.z", False, False)
assert markings.is_marked(test_sdo, ["6", "9"], "x.z", True, False)
assert markings.is_marked(test_sdo, ["6", "9", "10"], "x.z", True, True)
assert markings.is_marked(test_sdo, ["9", "10"], "x.z", False, True)
assert markings.is_marked(test_sdo, "x.z.foo1", inherited=False, descendants=False) is False
assert markings.is_marked(test_sdo, ["6", "9"], "x.z.foo1", True, False)
assert markings.is_marked(test_sdo, ["6", "9"], "x.z.foo1", True, True)
assert markings.is_marked(test_sdo, "x.z.foo1", inherited=False, descendants=True) is False
assert markings.is_marked(test_sdo, ["10"], "x.z.foo2", False, False)
assert markings.is_marked(test_sdo, ["6", "9", "10"], "x.z.foo2", True, False)
assert markings.is_marked(test_sdo, ["6", "9", "10"], "x.z.foo2", True, True)
assert markings.is_marked(test_sdo, ["10"], "x.z.foo2", False, True)
def test_create_sdo_with_invalid_marking():
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError) as excinfo:
Malware(
granular_markings=[
{
"selectors": ["foo"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
assert str(excinfo.value) == "Selector foo in Malware is not valid!"
def test_set_marking_mark_one_selector_multiple_refs():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_set_marking_mark_one_selector_multiple_lang_refs():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"lang": MARKING_LANGS[0],
},
{
"selectors": ["description"],
"lang": MARKING_LANGS[1],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_LANGS[0], MARKING_LANGS[1]], ["description"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_set_marking_mark_multiple_selector_one_refs():
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[0]], ["description", "modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_set_marking_mark_multiple_mixed_markings():
before = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
{
"selectors": ["description", "modified"],
"lang": MARKING_LANGS[2],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[2],
},
{
"selectors": ["description", "modified"],
"lang": MARKING_LANGS[3],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[2], MARKING_LANGS[3]], ["description", "modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_set_marking_mark_multiple_selector_multiple_refs_from_none():
before = Malware(
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[0],
},
{
"selectors": ["description", "modified"],
"marking_ref": MARKING_IDS[1],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[0], MARKING_IDS[1]], ["description", "modified"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
def test_set_marking_mark_another_property_same_marking():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[1],
},
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[2],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[1], MARKING_IDS[2]], ["description"])
for m in before["granular_markings"]:
assert m in after["granular_markings"]
@pytest.mark.parametrize(
"marking", [
([MARKING_IDS[4], MARKING_IDS[5]], ["foo"]),
([MARKING_IDS[4], MARKING_IDS[5]], ""),
([MARKING_IDS[4], MARKING_IDS[5]], []),
([MARKING_IDS[4], MARKING_IDS[5]], [""]),
],
)
def test_set_marking_bad_selector(marking):
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
before = markings.set_markings(before, marking[0], marking[1])
assert before == after
def test_set_marking_mark_same_property_same_marking():
before = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
after = Malware(
granular_markings=[
{
"selectors": ["description"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
before = markings.set_markings(before, [MARKING_IDS[0]], ["description"])
for m in before["granular_markings"]:
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
),
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)
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)
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)
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)
def test_clear_marking_all_selectors(data):
data = markings.clear_markings(data, ["description", "type", "modified"])
assert markings.is_marked(data, "description") is False
assert "granular_markings" not in data
@pytest.mark.parametrize(
"data,selector", [
(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."""
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(InvalidSelectorError):
markings.clear_markings(data, selector)
@pytest.mark.parametrize("data", CLEAR_MARKINGS_TEST_DATA)
def test_clear_marking_not_present(data):
"""Test clearing markings for a selector that has no associated markings."""
with pytest.raises(MarkingNotFoundError):
markings.clear_markings(data, ["malware_types"])
def test_set_marking_on_id_property():
malware = Malware(
granular_markings=[
{
"selectors": ["id"],
"marking_ref": MARKING_IDS[0],
},
],
**MALWARE_KWARGS
)
assert "id" in malware["granular_markings"][0]["selectors"]