cti-python-stix2/stix2/test/v20/test_observed_data.py

1360 lines
42 KiB
Python
Raw Normal View History

import datetime as dt
2017-05-03 23:35:33 +02:00
import re
2017-04-19 15:22:08 +02:00
import pytest
import pytz
2017-02-24 18:56:55 +01:00
import stix2
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 ...exceptions import InvalidValueError
from .constants import IDENTITY_ID, OBSERVED_DATA_ID
OBJECTS_REGEX = re.compile('\"objects\": {(?:.*?)(?:(?:[^{]*?)|(?:{[^{]*?}))*}', re.DOTALL)
2017-04-19 15:22:08 +02:00
2017-02-24 18:56:55 +01:00
EXPECTED = """{
"type": "observed-data",
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
"created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
"created": "2016-04-06T19:58:16.000Z",
"modified": "2016-04-06T19:58:16.000Z",
2017-02-24 18:56:55 +01:00
"first_observed": "2015-12-21T19:00:00Z",
"last_observed": "2015-12-21T19:00:00Z",
"number_observed": 50,
"objects": {
"0": {
"type": "file",
"name": "foo.exe"
2017-02-24 18:56:55 +01:00
}
}
2017-02-24 18:56:55 +01:00
}"""
def test_observed_data_example():
observed_data = stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
2017-02-24 18:56:55 +01:00
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects={
"0": {
"name": "foo.exe",
"type": "file",
2017-02-24 18:56:55 +01:00
},
},
)
assert str(observed_data) == EXPECTED
2017-04-19 15:22:08 +02:00
EXPECTED_WITH_REF = """{
"type": "observed-data",
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
"created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
"created": "2016-04-06T19:58:16.000Z",
"modified": "2016-04-06T19:58:16.000Z",
"first_observed": "2015-12-21T19:00:00Z",
"last_observed": "2015-12-21T19:00:00Z",
"number_observed": 50,
"objects": {
"0": {
"type": "file",
"name": "foo.exe"
},
"1": {
"type": "directory",
"path": "/usr/home",
"contains_refs": [
"0"
]
}
}
}"""
def test_observed_data_example_with_refs():
observed_data = stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects={
"0": {
"name": "foo.exe",
"type": "file",
},
"1": {
"type": "directory",
"path": "/usr/home",
"contains_refs": ["0"],
},
},
)
assert str(observed_data) == EXPECTED_WITH_REF
def test_observed_data_example_with_bad_refs():
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects={
"0": {
"type": "file",
"name": "foo.exe",
},
"1": {
"type": "directory",
"path": "/usr/home",
"contains_refs": ["2"],
},
},
)
assert excinfo.value.cls == stix2.v20.ObservedData
assert excinfo.value.prop_name == "objects"
assert excinfo.value.reason == "Invalid object reference for 'Directory:contains_refs': '2' is not a valid object in local scope"
2017-08-24 23:53:43 +02:00
def test_observed_data_example_with_non_dictionary():
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
2017-08-24 23:53:43 +02:00
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects="file: foo.exe",
)
assert excinfo.value.cls == stix2.v20.ObservedData
2017-08-24 23:53:43 +02:00
assert excinfo.value.prop_name == "objects"
assert 'must contain a dictionary' in excinfo.value.reason
def test_observed_data_example_with_empty_dictionary():
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
2017-08-24 23:53:43 +02:00
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects={},
)
assert excinfo.value.cls == stix2.v20.ObservedData
2017-08-24 23:53:43 +02:00
assert excinfo.value.prop_name == "objects"
assert 'must contain a non-empty dictionary' in excinfo.value.reason
@pytest.mark.parametrize(
"data", [
EXPECTED,
{
"type": "observed-data",
2019-01-23 05:07:20 +01:00
"id": OBSERVED_DATA_ID,
"created": "2016-04-06T19:58:16.000Z",
"created_by_ref": IDENTITY_ID,
"first_observed": "2015-12-21T19:00:00Z",
"last_observed": "2015-12-21T19:00:00Z",
"modified": "2016-04-06T19:58:16.000Z",
"number_observed": 50,
"objects": {
"0": {
"name": "foo.exe",
"type": "file",
},
},
},
],
)
2017-04-19 15:22:08 +02:00
def test_parse_observed_data(data):
odata = stix2.parse(data, version="2.0")
2017-04-19 15:22:08 +02:00
assert odata.type == 'observed-data'
assert odata.id == OBSERVED_DATA_ID
assert odata.created == dt.datetime(2016, 4, 6, 19, 58, 16, tzinfo=pytz.utc)
assert odata.modified == dt.datetime(2016, 4, 6, 19, 58, 16, tzinfo=pytz.utc)
assert odata.first_observed == dt.datetime(2015, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
assert odata.last_observed == dt.datetime(2015, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
assert odata.created_by_ref == IDENTITY_ID
2017-05-03 23:35:33 +02:00
assert odata.objects["0"].type == "file"
@pytest.mark.parametrize(
"data", [
""""0": {
2017-05-03 23:35:33 +02:00
"type": "artifact",
"mime_type": "image/jpeg",
"payload_bin": "VBORw0KGgoAAAANSUhEUgAAADI=="
}""",
""""0": {
2017-05-03 23:35:33 +02:00
"type": "artifact",
"mime_type": "image/jpeg",
"url": "https://upload.wikimedia.org/wikipedia/commons/b/b4/JPEG_example_JPG_RIP_100.jpg",
"hashes": {
"MD5": "6826f9a05da08134006557758bb3afbb"
}
}""",
],
)
2017-05-03 23:35:33 +02:00
def test_parse_artifact_valid(data):
odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
odata = stix2.parse(odata_str, version="2.0")
2017-05-03 23:35:33 +02:00
assert odata.objects["0"].type == "artifact"
@pytest.mark.parametrize(
"data", [
""""0": {
2017-05-03 23:35:33 +02:00
"type": "artifact",
"mime_type": "image/jpeg",
"payload_bin": "abcVBORw0KGgoAAAANSUhEUgAAADI=="
}""",
""""0": {
2017-05-03 23:35:33 +02:00
"type": "artifact",
"mime_type": "image/jpeg",
"url": "https://upload.wikimedia.org/wikipedia/commons/b/b4/JPEG_example_JPG_RIP_100.jpg",
"hashes": {
"MD5": "a"
}
}""",
],
)
2017-05-03 23:35:33 +02:00
def test_parse_artifact_invalid(data):
odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
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(InvalidValueError):
stix2.parse(odata_str, version="2.0")
2017-04-19 15:22:08 +02:00
2017-05-04 00:19:30 +02:00
def test_artifact_example_dependency_error():
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.v20.Artifact(url="http://example.com/sirvizio.exe")
assert excinfo.value.dependencies == [("hashes", "url")]
assert str(excinfo.value) == "The property dependencies for Artifact: (hashes, url) are not met."
@pytest.mark.parametrize(
"data", [
""""0": {
2017-05-04 00:19:30 +02:00
"type": "autonomous-system",
"number": 15139,
"name": "Slime Industries",
"rir": "ARIN"
}""",
],
)
2017-05-04 00:19:30 +02:00
def test_parse_autonomous_system_valid(data):
odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
odata = stix2.parse(odata_str, version="2.0")
2017-05-04 00:19:30 +02:00
assert odata.objects["0"].type == "autonomous-system"
assert odata.objects["0"].number == 15139
assert odata.objects["0"].name == "Slime Industries"
assert odata.objects["0"].rir == "ARIN"
@pytest.mark.parametrize(
"data", [
"""{
"type": "email-addr",
"value": "john@example.com",
"display_name": "John Doe",
"belongs_to_ref": "0"
}""",
],
)
def test_parse_email_address(data):
odata = stix2.parse_observable(data, {"0": "user-account"}, version='2.0')
assert odata.type == "email-addr"
odata_str = re.compile('"belongs_to_ref": "0"', re.DOTALL).sub('"belongs_to_ref": "3"', data)
with pytest.raises(stix2.exceptions.InvalidObjRefError):
stix2.parse_observable(odata_str, {"0": "user-account"}, version='2.0')
@pytest.mark.parametrize(
"data", [
"""
{
"type": "email-message",
"is_multipart": true,
"content_type": "multipart/mixed",
"date": "2016-06-19T14:20:40.000Z",
"from_ref": "1",
"to_refs": [
"2"
],
"cc_refs": [
"3"
],
"subject": "Check out this picture of a cat!",
"additional_header_fields": {
"Content-Disposition": "inline",
"X-Mailer": "Mutt/1.5.23",
"X-Originating-IP": "198.51.100.3"
},
"body_multipart": [
{
"content_type": "text/plain; charset=utf-8",
"content_disposition": "inline",
"body": "Cats are funny!"
},
{
"content_type": "image/png",
"content_disposition": "attachment; filename=\\"tabby.png\\"",
"body_raw_ref": "4"
},
{
"content_type": "application/zip",
"content_disposition": "attachment; filename=\\"tabby_pics.zip\\"",
"body_raw_ref": "5"
}
]
}
""",
],
)
def test_parse_email_message(data):
valid_refs = {
"0": "email-message",
"1": "email-addr",
"2": "email-addr",
"3": "email-addr",
"4": "artifact",
"5": "file",
}
odata = stix2.parse_observable(data, valid_refs, version='2.0')
assert odata.type == "email-message"
assert odata.body_multipart[0].content_disposition == "inline"
@pytest.mark.parametrize(
"data", [
"""
{
"type": "email-message",
"from_ref": "0",
"to_refs": ["1"],
"is_multipart": true,
"date": "1997-11-21T15:55:06.000Z",
"subject": "Saying Hello",
"body": "Cats are funny!"
}
""",
],
)
def test_parse_email_message_not_multipart(data):
valid_refs = {
"0": "email-addr",
"1": "email-addr",
}
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.parse_observable(data, valid_refs, version='2.0')
assert excinfo.value.cls == stix2.v20.EmailMessage
assert excinfo.value.dependencies == [("is_multipart", "body")]
@pytest.mark.parametrize(
"data", [
""""0": {
2017-05-12 17:22:23 +02:00
"type": "file",
"hashes": {
"SHA-256": "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a"
}
},
"1": {
"type": "file",
"hashes": {
"SHA-256": "19c549ec2628b989382f6b280cbd7bb836a0b461332c0fe53511ce7d584b89d3"
}
},
"2": {
"type": "file",
"hashes": {
"SHA-256": "0969de02ecf8a5f003e3f6d063d848c8a193aada092623f8ce408c15bcb5f038"
}
},
"3": {
"type": "file",
"name": "foo.zip",
"hashes": {
"SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
},
"mime_type": "application/zip",
"extensions": {
"archive-ext": {
"contains_refs": [
"0",
"1",
"2"
],
"version": "5.0"
}
}
}""",
],
)
def test_parse_file_archive(data):
odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
odata = stix2.parse(odata_str, version="2.0")
2017-05-12 17:22:23 +02:00
assert odata.objects["3"].extensions['archive-ext'].version == "5.0"
@pytest.mark.parametrize(
"data", [
"""
{
"type": "email-message",
"is_multipart": true,
"content_type": "multipart/mixed",
"date": "2016-06-19T14:20:40.000Z",
"from_ref": "1",
"to_refs": [
"2"
],
"cc_refs": [
"3"
],
"subject": "Check out this picture of a cat!",
"additional_header_fields": {
"Content-Disposition": "inline",
"X-Mailer": "Mutt/1.5.23",
"X-Originating-IP": "198.51.100.3"
},
"body_multipart": [
{
"content_type": "text/plain; charset=utf-8",
"content_disposition": "inline",
"body": "Cats are funny!"
},
{
"content_type": "image/png",
"content_disposition": "attachment; filename=\\"tabby.png\\""
},
{
"content_type": "application/zip",
"content_disposition": "attachment; filename=\\"tabby_pics.zip\\"",
"body_raw_ref": "5"
}
]
}
""",
],
)
def test_parse_email_message_with_at_least_one_error(data):
valid_refs = {
"0": "email-message",
"1": "email-addr",
"2": "email-addr",
"3": "email-addr",
"4": "artifact",
"5": "file",
}
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(InvalidValueError) as excinfo:
stix2.parse_observable(data, valid_refs, version='2.0')
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
assert excinfo.value.cls == stix2.v20.EmailMessage
2017-08-24 23:53:43 +02:00
assert "At least one of the" in str(excinfo.value)
assert "must be populated" in str(excinfo.value)
@pytest.mark.parametrize(
"data", [
"""
{
"type": "network-traffic",
"src_ref": "0",
"dst_ref": "1",
"protocols": [
"tcp"
]
}
""",
],
)
def test_parse_basic_tcp_traffic(data):
odata = stix2.parse_observable(
data, {"0": "ipv4-addr", "1": "ipv4-addr"},
version='2.0',
)
assert odata.type == "network-traffic"
assert odata.src_ref == "0"
assert odata.dst_ref == "1"
assert odata.protocols == ["tcp"]
@pytest.mark.parametrize(
"data", [
"""
{
"type": "network-traffic",
"src_port": 2487,
"dst_port": 1723,
"protocols": [
"ipv4",
"pptp"
],
"src_byte_count": 35779,
"dst_byte_count": 935750,
"encapsulates_refs": [
"4"
]
}
""",
],
)
def test_parse_basic_tcp_traffic_with_error(data):
with pytest.raises(stix2.exceptions.AtLeastOnePropertyError) as excinfo:
stix2.parse_observable(data, {"4": "network-traffic"}, version='2.0')
assert excinfo.value.cls == stix2.v20.NetworkTraffic
assert excinfo.value.properties == ["dst_ref", "src_ref"]
EXPECTED_PROCESS_OD = """{
"created": "2016-04-06T19:58:16.000Z",
"created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
"first_observed": "2015-12-21T19:00:00Z",
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
"last_observed": "2015-12-21T19:00:00Z",
"modified": "2016-04-06T19:58:16.000Z",
"number_observed": 50,
"objects": {
"0": {
"type": "file",
"hashes": {
"SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100fSHA"
},
},
"1": {
"type": "process",
"pid": 1221,
"name": "gedit-bin",
"created": "2016-01-20T14:11:25.55Z",
"arguments" :[
"--new-window"
],
"binary_ref": "0"
}
},
"type": "observed-data"
}"""
def test_observed_data_with_process_example():
observed_data = stix2.v20.ObservedData(
2019-01-23 05:07:20 +01:00
id=OBSERVED_DATA_ID,
created_by_ref=IDENTITY_ID,
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,
objects={
"0": {
"type": "file",
"hashes": {
"SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f",
},
},
"1": {
"type": "process",
"pid": 1221,
"name": "gedit-bin",
"created": "2016-01-20T14:11:25.55Z",
"arguments": [
"--new-window",
],
"binary_ref": "0",
},
},
)
assert observed_data.objects["0"].type == "file"
assert observed_data.objects["0"].hashes["SHA-256"] == "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
assert observed_data.objects["1"].type == "process"
assert observed_data.objects["1"].pid == 1221
assert observed_data.objects["1"].name == "gedit-bin"
assert observed_data.objects["1"].arguments[0] == "--new-window"
# creating cyber observables directly
def test_artifact_example():
art = stix2.v20.Artifact(
mime_type="image/jpeg",
url="https://upload.wikimedia.org/wikipedia/commons/b/b4/JPEG_example_JPG_RIP_100.jpg",
hashes={
"MD5": "6826f9a05da08134006557758bb3afbb",
},
)
assert art.mime_type == "image/jpeg"
assert art.url == "https://upload.wikimedia.org/wikipedia/commons/b/b4/JPEG_example_JPG_RIP_100.jpg"
assert art.hashes["MD5"] == "6826f9a05da08134006557758bb3afbb"
def test_artifact_mutual_exclusion_error():
with pytest.raises(stix2.exceptions.MutuallyExclusivePropertiesError) as excinfo:
stix2.v20.Artifact(
mime_type="image/jpeg",
url="https://upload.wikimedia.org/wikipedia/commons/b/b4/JPEG_example_JPG_RIP_100.jpg",
hashes={
"MD5": "6826f9a05da08134006557758bb3afbb",
},
payload_bin="VBORw0KGgoAAAANSUhEUgAAADI==",
)
assert excinfo.value.cls == stix2.v20.Artifact
assert excinfo.value.properties == ["payload_bin", "url"]
2017-08-24 23:53:43 +02:00
assert 'are mutually exclusive' in str(excinfo.value)
def test_directory_example():
dir = stix2.v20.Directory(
_valid_refs={"1": "file"},
path='/usr/lib',
created="2015-12-21T19:00:00Z",
modified="2015-12-24T19:00:00Z",
accessed="2015-12-21T20:00:00Z",
contains_refs=["1"],
)
assert dir.path == '/usr/lib'
assert dir.created == dt.datetime(2015, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
assert dir.modified == dt.datetime(2015, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
assert dir.accessed == dt.datetime(2015, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
assert dir.contains_refs == ["1"]
def test_directory_example_ref_error():
with pytest.raises(stix2.exceptions.InvalidObjRefError) as excinfo:
stix2.v20.Directory(
_valid_refs=[],
path='/usr/lib',
created="2015-12-21T19:00:00Z",
modified="2015-12-24T19:00:00Z",
accessed="2015-12-21T20:00:00Z",
contains_refs=["1"],
)
assert excinfo.value.cls == stix2.v20.Directory
assert excinfo.value.prop_name == "contains_refs"
def test_domain_name_example():
dn = stix2.v20.DomainName(
_valid_refs={"1": 'domain-name'},
value="example.com",
resolves_to_refs=["1"],
)
assert dn.value == "example.com"
assert dn.resolves_to_refs == ["1"]
def test_domain_name_example_invalid_ref_type():
with pytest.raises(stix2.exceptions.InvalidObjRefError) as excinfo:
stix2.v20.DomainName(
_valid_refs={"1": "file"},
value="example.com",
resolves_to_refs=["1"],
)
assert excinfo.value.cls == stix2.v20.DomainName
assert excinfo.value.prop_name == "resolves_to_refs"
def test_file_example():
f = stix2.v20.File(
name="qwerty.dll",
hashes={
"SHA-256": "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a",
2018-07-10 20:46:46 +02:00
},
size=100,
magic_number_hex="1C",
mime_type="application/msword",
created="2016-12-21T19:00:00Z",
modified="2016-12-24T19:00:00Z",
accessed="2016-12-21T20:00:00Z",
is_encrypted=True,
encryption_algorithm="AES128-CBC",
decryption_key="fred",
)
assert f.name == "qwerty.dll"
assert f.size == 100
assert f.magic_number_hex == "1C"
assert f.hashes["SHA-256"] == "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a"
assert f.mime_type == "application/msword"
assert f.created == dt.datetime(2016, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
assert f.modified == dt.datetime(2016, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
assert f.accessed == dt.datetime(2016, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
assert f.is_encrypted
assert f.encryption_algorithm == "AES128-CBC"
assert f.decryption_key == "fred" # does the key have a format we can test for?
def test_file_ssdeep_example():
f = stix2.v20.File(
name="example.dll",
hashes={
"SHA-256": "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a",
"ssdeep": "96:gS/mFkCpXTWLr/PbKQHbr/S/mFkCpXTWLr/PbKQHbrB:Tu6SXTWGQHbeu6SXTWGQHbV",
},
size=1024,
)
assert f.name == "example.dll"
assert f.size == 1024
assert f.hashes["SHA-256"] == "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a"
assert f.hashes["ssdeep"] == "96:gS/mFkCpXTWLr/PbKQHbr/S/mFkCpXTWLr/PbKQHbrB:Tu6SXTWGQHbeu6SXTWGQHbV"
def test_file_example_with_NTFSExt():
f = stix2.v20.File(
name="abc.txt",
extensions={
"ntfs-ext": {
"alternate_data_streams": [
{
"name": "second.stream",
"size": 25536,
},
],
},
},
)
assert f.name == "abc.txt"
assert f.extensions["ntfs-ext"].alternate_data_streams[0].size == 25536
def test_file_example_with_empty_NTFSExt():
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(InvalidValueError) as excinfo:
stix2.v20.File(
name="abc.txt",
extensions={
"ntfs-ext": {},
},
)
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
assert excinfo.value.cls == stix2.v20.File
def test_file_example_with_PDFExt():
f = stix2.v20.File(
name="qwerty.dll",
extensions={
"pdf-ext": {
"version": "1.7",
"document_info_dict": {
"Title": "Sample document",
"Author": "Adobe Systems Incorporated",
"Creator": "Adobe FrameMaker 5.5.3 for Power Macintosh",
"Producer": "Acrobat Distiller 3.01 for Power Macintosh",
"CreationDate": "20070412090123-02",
},
"pdfid0": "DFCE52BD827ECF765649852119D",
"pdfid1": "57A1E0F9ED2AE523E313C",
},
},
)
assert f.name == "qwerty.dll"
assert f.extensions["pdf-ext"].version == "1.7"
assert f.extensions["pdf-ext"].document_info_dict["Title"] == "Sample document"
def test_file_example_with_PDFExt_Object():
f = stix2.v20.File(
name="qwerty.dll",
extensions={
"pdf-ext": stix2.v20.PDFExt(
version="1.7",
document_info_dict={
"Title": "Sample document",
"Author": "Adobe Systems Incorporated",
"Creator": "Adobe FrameMaker 5.5.3 for Power Macintosh",
"Producer": "Acrobat Distiller 3.01 for Power Macintosh",
"CreationDate": "20070412090123-02",
},
pdfid0="DFCE52BD827ECF765649852119D",
pdfid1="57A1E0F9ED2AE523E313C",
),
},
)
assert f.name == "qwerty.dll"
assert f.extensions["pdf-ext"].version == "1.7"
assert f.extensions["pdf-ext"].document_info_dict["Title"] == "Sample document"
def test_file_example_with_RasterImageExt_Object():
f = stix2.v20.File(
name="qwerty.jpeg",
extensions={
"raster-image-ext": {
"bits_per_pixel": 123,
"exif_tags": {
"Make": "Nikon",
"Model": "D7000",
"XResolution": 4928,
"YResolution": 3264,
},
},
},
)
assert f.name == "qwerty.jpeg"
assert f.extensions["raster-image-ext"].bits_per_pixel == 123
assert f.extensions["raster-image-ext"].exif_tags["XResolution"] == 4928
RASTER_IMAGE_EXT = """{
"type": "observed-data",
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
"created": "2016-04-06T19:58:16.000Z",
"modified": "2016-04-06T19:58:16.000Z",
"first_observed": "2015-12-21T19:00:00Z",
"last_observed": "2015-12-21T19:00:00Z",
"number_observed": 1,
"objects": {
"0": {
"type": "file",
"name": "picture.jpg",
"hashes": {
"SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
},
"extensions": {
"raster-image-ext": {
"image_height": 768,
"image_width": 1024,
"bits_per_pixel": 72,
"image_compression_algorithm": "JPEG",
"exif_tags": {
"Make": "Nikon",
"Model": "D7000",
"XResolution": 4928,
"YResolution": 3264
}
}
}
}
}
}
"""
def test_raster_image_ext_parse():
obj = stix2.parse(RASTER_IMAGE_EXT, version="2.0")
assert obj.objects["0"].extensions['raster-image-ext'].image_width == 1024
def test_raster_images_ext_create():
ext = stix2.v20.RasterImageExt(image_width=1024)
assert "image_width" in str(ext)
def test_file_example_with_WindowsPEBinaryExt():
f = stix2.v20.File(
name="qwerty.dll",
extensions={
"windows-pebinary-ext": {
"pe_type": "exe",
"machine_hex": "014c",
"number_of_sections": 4,
"time_date_stamp": "2016-01-22T12:31:12Z",
"pointer_to_symbol_table_hex": "74726144",
"number_of_symbols": 4542568,
"size_of_optional_header": 224,
"characteristics_hex": "818f",
"optional_header": {
"magic_hex": "010b",
"major_linker_version": 2,
"minor_linker_version": 25,
"size_of_code": 512,
"size_of_initialized_data": 283648,
"size_of_uninitialized_data": 0,
"address_of_entry_point": 4096,
"base_of_code": 4096,
"base_of_data": 8192,
"image_base": 14548992,
"section_alignment": 4096,
"file_alignment": 4096,
"major_os_version": 1,
"minor_os_version": 0,
"major_image_version": 0,
"minor_image_version": 0,
"major_subsystem_version": 4,
"minor_subsystem_version": 0,
"win32_version_value_hex": "00",
"size_of_image": 299008,
"size_of_headers": 4096,
"checksum_hex": "00",
"subsystem_hex": "03",
"dll_characteristics_hex": "00",
"size_of_stack_reserve": 100000,
"size_of_stack_commit": 8192,
"size_of_heap_reserve": 100000,
"size_of_heap_commit": 4096,
"loader_flags_hex": "abdbffde",
"number_of_rva_and_sizes": 3758087646,
},
"sections": [
{
"name": "CODE",
"entropy": 0.061089,
},
{
"name": "DATA",
"entropy": 7.980693,
},
{
"name": "NicolasB",
"entropy": 0.607433,
},
{
"name": ".idata",
"entropy": 0.607433,
},
],
},
},
)
assert f.name == "qwerty.dll"
assert f.extensions["windows-pebinary-ext"].sections[2].entropy == 0.607433
def test_file_example_encryption_error():
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.v20.File(
name="qwerty.dll",
is_encrypted=False,
encryption_algorithm="AES128-CBC",
)
assert excinfo.value.cls == stix2.v20.File
assert excinfo.value.dependencies == [("is_encrypted", "encryption_algorithm")]
2017-08-24 23:53:43 +02:00
assert "property dependencies" in str(excinfo.value)
assert "are not met" in str(excinfo.value)
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.v20.File(
name="qwerty.dll",
encryption_algorithm="AES128-CBC",
)
def test_ip4_address_example():
ip4 = stix2.v20.IPv4Address(
_valid_refs={"4": "mac-addr", "5": "mac-addr"},
value="198.51.100.3",
resolves_to_refs=["4", "5"],
)
assert ip4.value == "198.51.100.3"
assert ip4.resolves_to_refs == ["4", "5"]
def test_ip4_address_valid_refs():
mac1 = stix2.v20.MACAddress(
value="a1:b2:c3:d4:e5:f6",
)
mac2 = stix2.v20.MACAddress(
value="a7:b8:c9:d0:e1:f2",
)
ip4 = stix2.v20.IPv4Address(
_valid_refs={"1": mac1, "2": mac2},
value="177.60.40.7",
resolves_to_refs=["1", "2"],
)
assert ip4.value == "177.60.40.7"
assert ip4.resolves_to_refs == ["1", "2"]
def test_ip4_address_example_cidr():
ip4 = stix2.v20.IPv4Address(value="198.51.100.0/24")
assert ip4.value == "198.51.100.0/24"
def test_ip6_address_example():
ip6 = stix2.v20.IPv6Address(value="2001:0db8:85a3:0000:0000:8a2e:0370:7334")
assert ip6.value == "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
def test_mac_address_example():
ip6 = stix2.v20.MACAddress(value="d2:fb:49:24:37:18")
assert ip6.value == "d2:fb:49:24:37:18"
def test_network_traffic_example():
nt = stix2.v20.NetworkTraffic(
_valid_refs={"0": "ipv4-addr", "1": "ipv4-addr"},
protocols="tcp",
src_ref="0",
dst_ref="1",
)
assert nt.protocols == ["tcp"]
assert nt.src_ref == "0"
assert nt.dst_ref == "1"
def test_network_traffic_http_request_example():
h = stix2.v20.HTTPRequestExt(
request_method="get",
request_value="/download.html",
request_version="http/1.1",
request_header={
"Accept-Encoding": "gzip,deflate",
"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113",
"Host": "www.example.com",
},
)
nt = stix2.v20.NetworkTraffic(
_valid_refs={"0": "ipv4-addr"},
protocols="tcp",
src_ref="0",
extensions={'http-request-ext': h},
)
assert nt.extensions['http-request-ext'].request_method == "get"
assert nt.extensions['http-request-ext'].request_value == "/download.html"
assert nt.extensions['http-request-ext'].request_version == "http/1.1"
assert nt.extensions['http-request-ext'].request_header['Accept-Encoding'] == "gzip,deflate"
assert nt.extensions['http-request-ext'].request_header['User-Agent'] == "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113"
assert nt.extensions['http-request-ext'].request_header['Host'] == "www.example.com"
def test_network_traffic_icmp_example():
h = stix2.v20.ICMPExt(icmp_type_hex="08", icmp_code_hex="00")
nt = stix2.v20.NetworkTraffic(
_valid_refs={"0": "ipv4-addr"},
protocols="tcp",
src_ref="0",
extensions={'icmp-ext': h},
)
assert nt.extensions['icmp-ext'].icmp_type_hex == "08"
assert nt.extensions['icmp-ext'].icmp_code_hex == "00"
def test_network_traffic_socket_example():
h = stix2.v20.SocketExt(
is_listening=True,
address_family="AF_INET",
protocol_family="PF_INET",
socket_type="SOCK_STREAM",
)
nt = stix2.v20.NetworkTraffic(
_valid_refs={"0": "ipv4-addr"},
protocols="tcp",
src_ref="0",
extensions={'socket-ext': h},
)
assert nt.extensions['socket-ext'].is_listening
assert nt.extensions['socket-ext'].address_family == "AF_INET"
assert nt.extensions['socket-ext'].protocol_family == "PF_INET"
assert nt.extensions['socket-ext'].socket_type == "SOCK_STREAM"
def test_network_traffic_tcp_example():
h = stix2.v20.TCPExt(src_flags_hex="00000002")
nt = stix2.v20.NetworkTraffic(
_valid_refs={"0": "ipv4-addr"},
protocols="tcp",
src_ref="0",
extensions={'tcp-ext': h},
)
assert nt.extensions['tcp-ext'].src_flags_hex == "00000002"
def test_mutex_example():
m = stix2.v20.Mutex(name="barney")
assert m.name == "barney"
def test_process_example():
p = stix2.v20.Process(
_valid_refs={"0": "file"},
pid=1221,
name="gedit-bin",
created="2016-01-20T14:11:25.55Z",
arguments=["--new-window"],
binary_ref="0",
)
assert p.name == "gedit-bin"
assert p.arguments == ["--new-window"]
def test_process_example_empty_error():
with pytest.raises(stix2.exceptions.AtLeastOnePropertyError) as excinfo:
stix2.v20.Process()
assert excinfo.value.cls == stix2.v20.Process
properties_of_process = list(stix2.v20.Process._properties.keys())
properties_of_process.remove("type")
2017-05-18 17:24:43 +02:00
assert excinfo.value.properties == sorted(properties_of_process)
msg = "At least one of the ({1}) properties for {0} must be populated."
msg = msg.format(
stix2.v20.Process.__name__,
", ".join(sorted(properties_of_process)),
)
assert str(excinfo.value) == msg
def test_process_example_empty_with_extensions():
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(InvalidValueError) as excinfo:
2019-02-06 21:23:00 +01:00
stix2.v20.Process(
extensions={
"windows-process-ext": {},
},
)
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
assert excinfo.value.cls == stix2.v20.Process
def test_process_example_windows_process_ext():
proc = stix2.v20.Process(
pid=314,
name="foobar.exe",
extensions={
"windows-process-ext": {
"aslr_enabled": True,
"dep_enabled": True,
"priority": "HIGH_PRIORITY_CLASS",
"owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309",
},
},
)
assert proc.extensions["windows-process-ext"].aslr_enabled
assert proc.extensions["windows-process-ext"].dep_enabled
assert proc.extensions["windows-process-ext"].priority == "HIGH_PRIORITY_CLASS"
assert proc.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309"
def test_process_example_windows_process_ext_empty():
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(InvalidValueError) as excinfo:
2019-02-06 21:23:00 +01:00
stix2.v20.Process(
pid=1221,
name="gedit-bin",
extensions={
"windows-process-ext": {},
},
)
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
assert excinfo.value.cls == stix2.v20.Process
def test_process_example_extensions_empty():
proc = stix2.v20.Process(
pid=314,
name="foobar.exe",
extensions={},
)
assert '{}' in str(proc)
def test_process_example_with_WindowsProcessExt_Object():
p = stix2.v20.Process(extensions={
"windows-process-ext": stix2.v20.WindowsProcessExt(
aslr_enabled=True,
dep_enabled=True,
priority="HIGH_PRIORITY_CLASS",
owner_sid="S-1-5-21-186985262-1144665072-74031268-1309",
), # noqa
})
assert p.extensions["windows-process-ext"].dep_enabled
assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309"
def test_process_example_with_WindowsServiceExt():
p = stix2.v20.Process(extensions={
"windows-service-ext": {
"service_name": "sirvizio",
"display_name": "Sirvizio",
"start_type": "SERVICE_AUTO_START",
"service_type": "SERVICE_WIN32_OWN_PROCESS",
"service_status": "SERVICE_RUNNING",
},
})
assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"
def test_process_example_with_WindowsProcessServiceExt():
p = stix2.v20.Process(extensions={
"windows-service-ext": {
"service_name": "sirvizio",
"display_name": "Sirvizio",
"start_type": "SERVICE_AUTO_START",
"service_type": "SERVICE_WIN32_OWN_PROCESS",
"service_status": "SERVICE_RUNNING",
},
"windows-process-ext": {
"aslr_enabled": True,
"dep_enabled": True,
"priority": "HIGH_PRIORITY_CLASS",
"owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309",
},
})
assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"
assert p.extensions["windows-process-ext"].dep_enabled
assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309"
def test_software_example():
s = stix2.v20.Software(
name="Word",
cpe="cpe:2.3:a:microsoft:word:2000:*:*:*:*:*:*:*",
version="2002",
vendor="Microsoft",
)
assert s.name == "Word"
assert s.cpe == "cpe:2.3:a:microsoft:word:2000:*:*:*:*:*:*:*"
assert s.version == "2002"
assert s.vendor == "Microsoft"
def test_url_example():
s = stix2.v20.URL(value="https://example.com/research/index.html")
assert s.type == "url"
assert s.value == "https://example.com/research/index.html"
def test_user_account_example():
a = stix2.v20.UserAccount(
user_id="1001",
account_login="jdoe",
account_type="unix",
display_name="John Doe",
is_service_account=False,
is_privileged=False,
can_escalate_privs=True,
account_created="2016-01-20T12:31:12Z",
password_last_changed="2016-01-20T14:27:43Z",
account_first_login="2016-01-20T14:26:07Z",
account_last_login="2016-07-22T16:08:28Z",
)
assert a.user_id == "1001"
assert a.account_login == "jdoe"
assert a.account_type == "unix"
assert a.display_name == "John Doe"
assert not a.is_service_account
assert not a.is_privileged
assert a.can_escalate_privs
assert a.account_created == dt.datetime(2016, 1, 20, 12, 31, 12, tzinfo=pytz.utc)
assert a.password_last_changed == dt.datetime(2016, 1, 20, 14, 27, 43, tzinfo=pytz.utc)
assert a.account_first_login == dt.datetime(2016, 1, 20, 14, 26, 7, tzinfo=pytz.utc)
assert a.account_last_login == dt.datetime(2016, 7, 22, 16, 8, 28, tzinfo=pytz.utc)
2017-05-11 21:42:56 +02:00
def test_user_account_unix_account_ext_example():
u = stix2.v20.UNIXAccountExt(
gid=1001,
groups=["wheel"],
home_dir="/home/jdoe",
shell="/bin/bash",
)
a = stix2.v20.UserAccount(
user_id="1001",
account_login="jdoe",
account_type="unix",
extensions={'unix-account-ext': u},
)
assert a.extensions['unix-account-ext'].gid == 1001
assert a.extensions['unix-account-ext'].groups == ["wheel"]
assert a.extensions['unix-account-ext'].home_dir == "/home/jdoe"
assert a.extensions['unix-account-ext'].shell == "/bin/bash"
def test_windows_registry_key_example():
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(InvalidValueError):
stix2.v20.WindowsRegistryValueType(
name="Foo",
data="qwerty",
data_type="string",
)
v = stix2.v20.WindowsRegistryValueType(
name="Foo",
data="qwerty",
data_type="REG_SZ",
)
w = stix2.v20.WindowsRegistryKey(
key="hkey_local_machine\\system\\bar\\foo",
values=[v],
)
assert w.key == "hkey_local_machine\\system\\bar\\foo"
assert w["values"][0].name == "Foo"
assert w["values"][0].data == "qwerty"
assert w["values"][0].data_type == "REG_SZ"
# ensure no errors in serialization because of 'values'
assert "Foo" in str(w)
def test_x509_certificate_example():
x509 = stix2.v20.X509Certificate(
issuer="C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com", # noqa
validity_not_before="2016-03-12T12:00:00Z",
validity_not_after="2016-08-21T12:00:00Z",
subject="C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala@freesoft.org",
) # noqa
assert x509.type == "x509-certificate"
assert x509.issuer == "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com" # noqa
assert x509.subject == "C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala@freesoft.org" # noqa
def test_new_version_with_related_objects():
data = stix2.v20.ObservedData(
first_observed="2016-03-12T12:00:00Z",
last_observed="2016-03-12T12:00:00Z",
number_observed=1,
objects={
'src_ip': {
'type': 'ipv4-addr',
'value': '127.0.0.1/32',
},
2018-02-19 21:03:20 +01:00
'domain': {
'type': 'domain-name',
'value': 'example.com',
'resolves_to_refs': ['src_ip'],
},
},
)
new_version = data.new_version(last_observed="2017-12-12T12:00:00Z")
assert new_version.last_observed.year == 2017
assert new_version.objects['domain'].resolves_to_refs[0] == 'src_ip'