Split up test functions
parent
c1a59265ae
commit
26c65e3bd0
|
@ -0,0 +1,29 @@
|
|||
import datetime as dt
|
||||
|
||||
import pytz
|
||||
|
||||
FAKE_TIME = dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc)
|
||||
|
||||
INDICATOR_ID = "indicator--01234567-89ab-cdef-0123-456789abcdef"
|
||||
MALWARE_ID = "malware--fedcba98-7654-3210-fedc-ba9876543210"
|
||||
RELATIONSHIP_ID = "relationship--00000000-1111-2222-3333-444444444444"
|
||||
IDENTITY_ID = "identity--d4d765ce-cff7-40e8-b7a6-e205d005ac2c"
|
||||
|
||||
# Minimum required args for an Indicator instance
|
||||
INDICATOR_KWARGS = dict(
|
||||
labels=['malicious-activity'],
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
)
|
||||
|
||||
# Minimum required args for a Malware instance
|
||||
MALWARE_KWARGS = dict(
|
||||
labels=['ransomware'],
|
||||
name="Cryptolocker",
|
||||
)
|
||||
|
||||
# Minimum required args for a Relationship instance
|
||||
RELATIONSHIP_KWARGS = dict(
|
||||
relationship_type="indicates",
|
||||
source_ref=INDICATOR_ID,
|
||||
target_ref=MALWARE_ID,
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
|
||||
from .constants import FAKE_TIME, INDICATOR_ID, MALWARE_ID, RELATIONSHIP_ID
|
||||
from .constants import INDICATOR_KWARGS, MALWARE_KWARGS, RELATIONSHIP_KWARGS
|
||||
|
||||
|
||||
# Inspired by: http://stackoverflow.com/a/24006251
|
||||
@pytest.fixture
|
||||
def clock(monkeypatch):
|
||||
|
||||
class mydatetime(dt.datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return FAKE_TIME
|
||||
|
||||
monkeypatch.setattr(dt, 'datetime', mydatetime)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def uuid4(monkeypatch):
|
||||
def wrapper():
|
||||
data = [0]
|
||||
|
||||
def wrapped():
|
||||
data[0] += 1
|
||||
return "00000000-0000-0000-0000-00000000%04x" % data[0]
|
||||
|
||||
return wrapped
|
||||
monkeypatch.setattr(uuid, "uuid4", wrapper())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def indicator(uuid4, clock):
|
||||
return stix2.Indicator(**INDICATOR_KWARGS)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def malware(uuid4, clock):
|
||||
return stix2.Malware(**MALWARE_KWARGS)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def relationship(uuid4, clock):
|
||||
return stix2.Relationship(**RELATIONSHIP_KWARGS)
|
|
@ -0,0 +1,85 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
|
||||
from .fixtures import clock, uuid4, indicator, malware, relationship
|
||||
|
||||
EXPECTED_BUNDLE = """{
|
||||
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
||||
"objects": [
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "indicator--00000000-0000-0000-0000-000000000001",
|
||||
"labels": [
|
||||
"malicious-activity"
|
||||
],
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"type": "indicator",
|
||||
"valid_from": "2017-01-01T12:34:56Z"
|
||||
},
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "malware--00000000-0000-0000-0000-000000000002",
|
||||
"labels": [
|
||||
"ransomware"
|
||||
],
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"name": "Cryptolocker",
|
||||
"type": "malware"
|
||||
},
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "relationship--00000000-0000-0000-0000-000000000003",
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"type": "relationship"
|
||||
}
|
||||
],
|
||||
"spec_version": "2.0",
|
||||
"type": "bundle"
|
||||
}"""
|
||||
|
||||
|
||||
def test_empty_bundle():
|
||||
bundle = stix2.Bundle()
|
||||
|
||||
assert bundle.type == "bundle"
|
||||
assert bundle.id.startswith("bundle--")
|
||||
assert bundle.spec_version == "2.0"
|
||||
assert bundle.objects is None
|
||||
|
||||
|
||||
def test_bundle_with_wrong_type():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(type="not-a-bundle")
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'type': must equal 'bundle'."
|
||||
|
||||
|
||||
def test_bundle_id_must_start_with_bundle():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(id='my-prefix--')
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'id': must start with 'bundle--'."
|
||||
|
||||
|
||||
def test_bundle_with_wrong_spec_version():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(spec_version="1.2")
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'spec_version': must equal '2.0'."
|
||||
|
||||
|
||||
def test_create_bundle(indicator, malware, relationship):
|
||||
bundle = stix2.Bundle(objects=[indicator, malware, relationship])
|
||||
|
||||
assert str(bundle) == EXPECTED_BUNDLE
|
||||
|
||||
|
||||
def test_create_bundle_with_positional_args(indicator, malware, relationship):
|
||||
bundle = stix2.Bundle(indicator, malware, relationship)
|
||||
|
||||
assert str(bundle) == EXPECTED_BUNDLE
|
|
@ -0,0 +1,18 @@
|
|||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
from .constants import FAKE_TIME
|
||||
from .fixtures import clock, uuid4
|
||||
|
||||
|
||||
def test_clock(clock):
|
||||
assert dt.datetime.now() == FAKE_TIME
|
||||
|
||||
|
||||
def test_my_uuid4_fixture(uuid4):
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000001"
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000002"
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000003"
|
||||
for _ in range(256):
|
||||
uuid.uuid4()
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000104"
|
|
@ -0,0 +1,125 @@
|
|||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
|
||||
from .constants import FAKE_TIME, INDICATOR_ID, INDICATOR_KWARGS
|
||||
from .fixtures import clock, uuid4, indicator
|
||||
|
||||
EXPECTED_INDICATOR = """{
|
||||
"created": "2017-01-01T00:00:01Z",
|
||||
"id": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"labels": [
|
||||
"malicious-activity"
|
||||
],
|
||||
"modified": "2017-01-01T00:00:01Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"type": "indicator",
|
||||
"valid_from": "1970-01-01T00:00:01Z"
|
||||
}"""
|
||||
|
||||
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
|
||||
created=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||
id='indicator--01234567-89ab-cdef-0123-456789abcdef',
|
||||
labels=['malicious-activity'],
|
||||
modified=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
type='indicator',
|
||||
valid_from=datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=<UTC>)
|
||||
""".split()) + ")"
|
||||
|
||||
|
||||
def test_indicator_with_all_required_fields():
|
||||
now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
||||
epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
||||
|
||||
indicator = stix2.Indicator(
|
||||
type="indicator",
|
||||
id=INDICATOR_ID,
|
||||
labels=['malicious-activity'],
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
created=now,
|
||||
modified=now,
|
||||
valid_from=epoch,
|
||||
)
|
||||
|
||||
assert str(indicator) == EXPECTED_INDICATOR
|
||||
assert repr(indicator) == EXPECTED_INDICATOR_REPR
|
||||
|
||||
|
||||
def test_indicator_autogenerated_fields(indicator):
|
||||
assert indicator.type == 'indicator'
|
||||
assert indicator.id == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert indicator.created == FAKE_TIME
|
||||
assert indicator.modified == FAKE_TIME
|
||||
assert indicator.labels == ['malicious-activity']
|
||||
assert indicator.pattern == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
||||
assert indicator.valid_from == FAKE_TIME
|
||||
|
||||
assert indicator['type'] == 'indicator'
|
||||
assert indicator['id'] == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert indicator['created'] == FAKE_TIME
|
||||
assert indicator['modified'] == FAKE_TIME
|
||||
assert indicator['labels'] == ['malicious-activity']
|
||||
assert indicator['pattern'] == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
||||
assert indicator['valid_from'] == FAKE_TIME
|
||||
|
||||
|
||||
def test_indicator_type_must_be_indicator():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'type': must equal 'indicator'."
|
||||
|
||||
|
||||
def test_indicator_id_must_start_with_indicator():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(id='my-prefix--', **INDICATOR_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'id': must start with 'indicator--'."
|
||||
|
||||
|
||||
def test_indicator_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Indicator: (labels, pattern)."
|
||||
|
||||
|
||||
def test_indicator_required_field_pattern():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(labels=['malicious-activity'])
|
||||
assert str(excinfo.value) == "Missing required field(s) for Indicator: (pattern)."
|
||||
|
||||
|
||||
def test_indicator_created_ref_invalid_format():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(created_by_ref='myprefix--12345678', **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'created_by_ref': must match <object-type>--<guid>."
|
||||
|
||||
|
||||
def test_indicator_revoked_invalid():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(revoked='false', **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'revoked': must be a boolean value."
|
||||
|
||||
|
||||
def test_cannot_assign_to_indicator_attributes(indicator):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator.valid_from = dt.datetime.now()
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_indicator():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
indicator = stix2.Indicator(my_custom_property="foo", **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']"
|
||||
|
||||
|
||||
def test_created_modified_time_are_identical_by_default():
|
||||
"""By default, the created and modified times should be the same."""
|
||||
indicator = stix2.Indicator(**INDICATOR_KWARGS)
|
||||
|
||||
assert indicator.created == indicator.modified
|
|
@ -0,0 +1,90 @@
|
|||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
|
||||
from .constants import FAKE_TIME, MALWARE_ID, MALWARE_KWARGS
|
||||
from .fixtures import clock, uuid4, malware
|
||||
|
||||
EXPECTED_MALWARE = """{
|
||||
"created": "2016-05-12T08:17:27Z",
|
||||
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"labels": [
|
||||
"ransomware"
|
||||
],
|
||||
"modified": "2016-05-12T08:17:27Z",
|
||||
"name": "Cryptolocker",
|
||||
"type": "malware"
|
||||
}"""
|
||||
|
||||
|
||||
def test_malware_with_all_required_fields():
|
||||
now = dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
|
||||
|
||||
malware = stix2.Malware(
|
||||
type="malware",
|
||||
id=MALWARE_ID,
|
||||
created=now,
|
||||
modified=now,
|
||||
labels=["ransomware"],
|
||||
name="Cryptolocker",
|
||||
)
|
||||
|
||||
assert str(malware) == EXPECTED_MALWARE
|
||||
|
||||
|
||||
def test_malware_autogenerated_fields(malware):
|
||||
assert malware.type == 'malware'
|
||||
assert malware.id == 'malware--00000000-0000-0000-0000-000000000001'
|
||||
assert malware.created == FAKE_TIME
|
||||
assert malware.modified == FAKE_TIME
|
||||
assert malware.labels == ['ransomware']
|
||||
assert malware.name == "Cryptolocker"
|
||||
|
||||
assert malware['type'] == 'malware'
|
||||
assert malware['id'] == 'malware--00000000-0000-0000-0000-000000000001'
|
||||
assert malware['created'] == FAKE_TIME
|
||||
assert malware['modified'] == FAKE_TIME
|
||||
assert malware['labels'] == ['ransomware']
|
||||
assert malware['name'] == "Cryptolocker"
|
||||
|
||||
|
||||
def test_malware_type_must_be_malware():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(type='xxx', **MALWARE_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Malware 'type': must equal 'malware'."
|
||||
|
||||
|
||||
def test_malware_id_must_start_with_malware():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(id='my-prefix--', **MALWARE_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Malware 'id': must start with 'malware--'."
|
||||
|
||||
|
||||
def test_malware_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Malware: (labels, name)."
|
||||
|
||||
|
||||
def test_malware_required_field_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(labels=['ransomware'])
|
||||
assert str(excinfo.value) == "Missing required field(s) for Malware: (name)."
|
||||
|
||||
|
||||
def test_cannot_assign_to_malware_attributes(malware):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware.name = "Cryptolocker II"
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_malware():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
malware = stix2.Malware(my_custom_property="foo", **MALWARE_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']"
|
|
@ -0,0 +1,125 @@
|
|||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
|
||||
from .constants import FAKE_TIME, INDICATOR_ID, MALWARE_ID, RELATIONSHIP_ID
|
||||
from .constants import RELATIONSHIP_KWARGS
|
||||
from .fixtures import clock, uuid4, indicator, malware, relationship
|
||||
|
||||
|
||||
EXPECTED_RELATIONSHIP = """{
|
||||
"created": "2016-04-06T20:06:37Z",
|
||||
"id": "relationship--00000000-1111-2222-3333-444444444444",
|
||||
"modified": "2016-04-06T20:06:37Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"type": "relationship"
|
||||
}"""
|
||||
|
||||
|
||||
def test_relationship_all_required_fields():
|
||||
now = dt.datetime(2016, 4, 6, 20, 6, 37, tzinfo=pytz.utc)
|
||||
|
||||
relationship = stix2.Relationship(
|
||||
type='relationship',
|
||||
id=RELATIONSHIP_ID,
|
||||
created=now,
|
||||
modified=now,
|
||||
relationship_type='indicates',
|
||||
source_ref=INDICATOR_ID,
|
||||
target_ref=MALWARE_ID,
|
||||
)
|
||||
assert str(relationship) == EXPECTED_RELATIONSHIP
|
||||
|
||||
|
||||
def test_relationship_autogenerated_fields(relationship):
|
||||
assert relationship.type == 'relationship'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.created == FAKE_TIME
|
||||
assert relationship.modified == FAKE_TIME
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == INDICATOR_ID
|
||||
assert relationship.target_ref == MALWARE_ID
|
||||
|
||||
assert relationship['type'] == 'relationship'
|
||||
assert relationship['id'] == 'relationship--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship['created'] == FAKE_TIME
|
||||
assert relationship['modified'] == FAKE_TIME
|
||||
assert relationship['relationship_type'] == 'indicates'
|
||||
assert relationship['source_ref'] == INDICATOR_ID
|
||||
assert relationship['target_ref'] == MALWARE_ID
|
||||
|
||||
|
||||
def test_relationship_type_must_be_relationship():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Relationship 'type': must equal 'relationship'."
|
||||
|
||||
|
||||
def test_relationship_id_must_start_with_relationship():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(id='my-prefix--', **RELATIONSHIP_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Relationship 'id': must start with 'relationship--'."
|
||||
|
||||
|
||||
def test_relationship_required_field_relationship_type():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)."
|
||||
|
||||
|
||||
def test_relationship_missing_some_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
# relationship_type is checked first, so make sure that is provided
|
||||
relationship = stix2.Relationship(relationship_type='indicates')
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (source_ref, target_ref)."
|
||||
|
||||
|
||||
def test_relationship_required_field_target_ref():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(
|
||||
relationship_type='indicates',
|
||||
source_ref=INDICATOR_ID
|
||||
)
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (target_ref)."
|
||||
|
||||
|
||||
def test_cannot_assign_to_relationship_attributes(relationship):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship.relationship_type = "derived-from"
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_relationship():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
relationship = stix2.Relationship(my_custom_property="foo", **RELATIONSHIP_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']" in str(excinfo)
|
||||
|
||||
|
||||
def test_create_relationship_from_objects_rather_than_ids(indicator, malware):
|
||||
relationship = stix2.Relationship(
|
||||
relationship_type="indicates",
|
||||
source_ref=indicator,
|
||||
target_ref=malware,
|
||||
)
|
||||
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.target_ref == 'malware--00000000-0000-0000-0000-000000000002'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000003'
|
||||
|
||||
|
||||
def test_create_relationship_with_positional_args(indicator, malware):
|
||||
relationship = stix2.Relationship(indicator, 'indicates', malware)
|
||||
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.target_ref == 'malware--00000000-0000-0000-0000-000000000002'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000003'
|
|
@ -1,486 +0,0 @@
|
|||
"""Tests for the stix2 library"""
|
||||
|
||||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
import stix2.utils
|
||||
|
||||
|
||||
FAKE_TIME = dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc)
|
||||
|
||||
|
||||
# Inspired by: http://stackoverflow.com/a/24006251
|
||||
@pytest.fixture
|
||||
def clock(monkeypatch):
|
||||
|
||||
class mydatetime(dt.datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return FAKE_TIME
|
||||
|
||||
monkeypatch.setattr(dt, 'datetime', mydatetime)
|
||||
|
||||
|
||||
def test_clock(clock):
|
||||
assert dt.datetime.now() == FAKE_TIME
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def uuid4(monkeypatch):
|
||||
def wrapper():
|
||||
data = [0]
|
||||
|
||||
def wrapped():
|
||||
data[0] += 1
|
||||
return "00000000-0000-0000-0000-00000000%04x" % data[0]
|
||||
|
||||
return wrapped
|
||||
monkeypatch.setattr(uuid, "uuid4", wrapper())
|
||||
|
||||
|
||||
def test_my_uuid4_fixture(uuid4):
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000001"
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000002"
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000003"
|
||||
for _ in range(256):
|
||||
uuid.uuid4()
|
||||
assert uuid.uuid4() == "00000000-0000-0000-0000-000000000104"
|
||||
|
||||
|
||||
INDICATOR_ID = "indicator--01234567-89ab-cdef-0123-456789abcdef"
|
||||
MALWARE_ID = "malware--fedcba98-7654-3210-fedc-ba9876543210"
|
||||
RELATIONSHIP_ID = "relationship--00000000-1111-2222-3333-444444444444"
|
||||
IDENTITY_ID = "identity--d4d765ce-cff7-40e8-b7a6-e205d005ac2c"
|
||||
|
||||
# Minimum required args for an Indicator instance
|
||||
INDICATOR_KWARGS = dict(
|
||||
labels=['malicious-activity'],
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
)
|
||||
|
||||
# Minimum required args for a Malware instance
|
||||
MALWARE_KWARGS = dict(
|
||||
labels=['ransomware'],
|
||||
name="Cryptolocker",
|
||||
)
|
||||
|
||||
# Minimum required args for a Relationship instance
|
||||
RELATIONSHIP_KWARGS = dict(
|
||||
relationship_type="indicates",
|
||||
source_ref=INDICATOR_ID,
|
||||
target_ref=MALWARE_ID,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def indicator(uuid4, clock):
|
||||
return stix2.Indicator(**INDICATOR_KWARGS)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def malware(uuid4, clock):
|
||||
return stix2.Malware(**MALWARE_KWARGS)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def relationship(uuid4, clock):
|
||||
return stix2.Relationship(**RELATIONSHIP_KWARGS)
|
||||
|
||||
|
||||
EXPECTED_INDICATOR = """{
|
||||
"created": "2017-01-01T00:00:01Z",
|
||||
"id": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"labels": [
|
||||
"malicious-activity"
|
||||
],
|
||||
"modified": "2017-01-01T00:00:01Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"type": "indicator",
|
||||
"valid_from": "1970-01-01T00:00:01Z"
|
||||
}"""
|
||||
|
||||
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
|
||||
created=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||
id='indicator--01234567-89ab-cdef-0123-456789abcdef',
|
||||
labels=['malicious-activity'],
|
||||
modified=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
type='indicator',
|
||||
valid_from=datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=<UTC>)
|
||||
""".split()) + ")"
|
||||
|
||||
|
||||
def test_indicator_with_all_required_fields():
|
||||
now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
||||
epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
||||
|
||||
indicator = stix2.Indicator(
|
||||
type="indicator",
|
||||
id=INDICATOR_ID,
|
||||
labels=['malicious-activity'],
|
||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
created=now,
|
||||
modified=now,
|
||||
valid_from=epoch,
|
||||
)
|
||||
|
||||
assert str(indicator) == EXPECTED_INDICATOR
|
||||
assert repr(indicator) == EXPECTED_INDICATOR_REPR
|
||||
|
||||
|
||||
def test_indicator_autogenerated_fields(indicator):
|
||||
assert indicator.type == 'indicator'
|
||||
assert indicator.id == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert indicator.created == FAKE_TIME
|
||||
assert indicator.modified == FAKE_TIME
|
||||
assert indicator.labels == ['malicious-activity']
|
||||
assert indicator.pattern == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
||||
assert indicator.valid_from == FAKE_TIME
|
||||
|
||||
assert indicator['type'] == 'indicator'
|
||||
assert indicator['id'] == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert indicator['created'] == FAKE_TIME
|
||||
assert indicator['modified'] == FAKE_TIME
|
||||
assert indicator['labels'] == ['malicious-activity']
|
||||
assert indicator['pattern'] == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
||||
assert indicator['valid_from'] == FAKE_TIME
|
||||
|
||||
|
||||
def test_indicator_type_must_be_indicator():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'type': must equal 'indicator'."
|
||||
|
||||
|
||||
def test_indicator_id_must_start_with_indicator():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(id='my-prefix--', **INDICATOR_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'id': must start with 'indicator--'."
|
||||
|
||||
|
||||
def test_indicator_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Indicator: (labels, pattern)."
|
||||
|
||||
|
||||
def test_indicator_required_field_pattern():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(labels=['malicious-activity'])
|
||||
assert str(excinfo.value) == "Missing required field(s) for Indicator: (pattern)."
|
||||
|
||||
|
||||
def test_indicator_created_ref_invalid_format():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(created_by_ref='myprefix--12345678', **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'created_by_ref': must match <object-type>--<guid>."
|
||||
|
||||
|
||||
def test_indicator_revoked_invalid():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator = stix2.Indicator(revoked='false', **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "Invalid value for Indicator 'revoked': must be a boolean value."
|
||||
|
||||
|
||||
def test_cannot_assign_to_indicator_attributes(indicator):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
indicator.valid_from = dt.datetime.now()
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_indicator():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
indicator = stix2.Indicator(my_custom_property="foo", **INDICATOR_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']"
|
||||
|
||||
|
||||
def test_created_modified_time_are_identical_by_default():
|
||||
"""By default, the created and modified times should be the same."""
|
||||
indicator = stix2.Indicator(**INDICATOR_KWARGS)
|
||||
|
||||
assert indicator.created == indicator.modified
|
||||
|
||||
|
||||
EXPECTED_MALWARE = """{
|
||||
"created": "2016-05-12T08:17:27Z",
|
||||
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"labels": [
|
||||
"ransomware"
|
||||
],
|
||||
"modified": "2016-05-12T08:17:27Z",
|
||||
"name": "Cryptolocker",
|
||||
"type": "malware"
|
||||
}"""
|
||||
|
||||
|
||||
def test_malware_with_all_required_fields():
|
||||
now = dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
|
||||
|
||||
malware = stix2.Malware(
|
||||
type="malware",
|
||||
id=MALWARE_ID,
|
||||
created=now,
|
||||
modified=now,
|
||||
labels=["ransomware"],
|
||||
name="Cryptolocker",
|
||||
)
|
||||
|
||||
assert str(malware) == EXPECTED_MALWARE
|
||||
|
||||
|
||||
def test_malware_autogenerated_fields(malware):
|
||||
assert malware.type == 'malware'
|
||||
assert malware.id == 'malware--00000000-0000-0000-0000-000000000001'
|
||||
assert malware.created == FAKE_TIME
|
||||
assert malware.modified == FAKE_TIME
|
||||
assert malware.labels == ['ransomware']
|
||||
assert malware.name == "Cryptolocker"
|
||||
|
||||
assert malware['type'] == 'malware'
|
||||
assert malware['id'] == 'malware--00000000-0000-0000-0000-000000000001'
|
||||
assert malware['created'] == FAKE_TIME
|
||||
assert malware['modified'] == FAKE_TIME
|
||||
assert malware['labels'] == ['ransomware']
|
||||
assert malware['name'] == "Cryptolocker"
|
||||
|
||||
|
||||
def test_malware_type_must_be_malware():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(type='xxx', **MALWARE_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Malware 'type': must equal 'malware'."
|
||||
|
||||
|
||||
def test_malware_id_must_start_with_malware():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(id='my-prefix--', **MALWARE_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Malware 'id': must start with 'malware--'."
|
||||
|
||||
|
||||
def test_malware_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Malware: (labels, name)."
|
||||
|
||||
|
||||
def test_malware_required_field_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware = stix2.Malware(labels=['ransomware'])
|
||||
assert str(excinfo.value) == "Missing required field(s) for Malware: (name)."
|
||||
|
||||
|
||||
def test_cannot_assign_to_malware_attributes(malware):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
malware.name = "Cryptolocker II"
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_malware():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
malware = stix2.Malware(my_custom_property="foo", **MALWARE_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']"
|
||||
|
||||
|
||||
EXPECTED_RELATIONSHIP = """{
|
||||
"created": "2016-04-06T20:06:37Z",
|
||||
"id": "relationship--00000000-1111-2222-3333-444444444444",
|
||||
"modified": "2016-04-06T20:06:37Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"type": "relationship"
|
||||
}"""
|
||||
|
||||
|
||||
def test_relationship_all_required_fields():
|
||||
now = dt.datetime(2016, 4, 6, 20, 6, 37, tzinfo=pytz.utc)
|
||||
|
||||
relationship = stix2.Relationship(
|
||||
type='relationship',
|
||||
id=RELATIONSHIP_ID,
|
||||
created=now,
|
||||
modified=now,
|
||||
relationship_type='indicates',
|
||||
source_ref=INDICATOR_ID,
|
||||
target_ref=MALWARE_ID,
|
||||
)
|
||||
assert str(relationship) == EXPECTED_RELATIONSHIP
|
||||
|
||||
|
||||
def test_relationship_autogenerated_fields(relationship):
|
||||
assert relationship.type == 'relationship'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.created == FAKE_TIME
|
||||
assert relationship.modified == FAKE_TIME
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == INDICATOR_ID
|
||||
assert relationship.target_ref == MALWARE_ID
|
||||
|
||||
assert relationship['type'] == 'relationship'
|
||||
assert relationship['id'] == 'relationship--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship['created'] == FAKE_TIME
|
||||
assert relationship['modified'] == FAKE_TIME
|
||||
assert relationship['relationship_type'] == 'indicates'
|
||||
assert relationship['source_ref'] == INDICATOR_ID
|
||||
assert relationship['target_ref'] == MALWARE_ID
|
||||
|
||||
|
||||
def test_relationship_type_must_be_relationship():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Relationship 'type': must equal 'relationship'."
|
||||
|
||||
|
||||
def test_relationship_id_must_start_with_relationship():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(id='my-prefix--', **RELATIONSHIP_KWARGS)
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Relationship 'id': must start with 'relationship--'."
|
||||
|
||||
|
||||
def test_relationship_required_field_relationship_type():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship()
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)."
|
||||
|
||||
|
||||
def test_relationship_missing_some_required_fields():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
# relationship_type is checked first, so make sure that is provided
|
||||
relationship = stix2.Relationship(relationship_type='indicates')
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (source_ref, target_ref)."
|
||||
|
||||
|
||||
def test_relationship_required_field_target_ref():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship = stix2.Relationship(
|
||||
relationship_type='indicates',
|
||||
source_ref=INDICATOR_ID
|
||||
)
|
||||
assert str(excinfo.value) == "Missing required field(s) for Relationship: (target_ref)."
|
||||
|
||||
|
||||
def test_cannot_assign_to_relationship_attributes(relationship):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
relationship.relationship_type = "derived-from"
|
||||
|
||||
assert str(excinfo.value) == "Cannot modify properties after creation."
|
||||
|
||||
|
||||
def test_invalid_kwarg_to_relationship():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
relationship = stix2.Relationship(my_custom_property="foo", **RELATIONSHIP_KWARGS)
|
||||
assert str(excinfo.value) == "unexpected keyword arguments: ['my_custom_property']" in str(excinfo)
|
||||
|
||||
|
||||
def test_create_relationship_from_objects_rather_than_ids(indicator, malware):
|
||||
relationship = stix2.Relationship(
|
||||
relationship_type="indicates",
|
||||
source_ref=indicator,
|
||||
target_ref=malware,
|
||||
)
|
||||
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.target_ref == 'malware--00000000-0000-0000-0000-000000000002'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000003'
|
||||
|
||||
|
||||
def test_create_relationship_with_positional_args(indicator, malware):
|
||||
relationship = stix2.Relationship(indicator, 'indicates', malware)
|
||||
|
||||
assert relationship.relationship_type == 'indicates'
|
||||
assert relationship.source_ref == 'indicator--00000000-0000-0000-0000-000000000001'
|
||||
assert relationship.target_ref == 'malware--00000000-0000-0000-0000-000000000002'
|
||||
assert relationship.id == 'relationship--00000000-0000-0000-0000-000000000003'
|
||||
|
||||
|
||||
EXPECTED_BUNDLE = """{
|
||||
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
||||
"objects": [
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "indicator--00000000-0000-0000-0000-000000000001",
|
||||
"labels": [
|
||||
"malicious-activity"
|
||||
],
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"type": "indicator",
|
||||
"valid_from": "2017-01-01T12:34:56Z"
|
||||
},
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "malware--00000000-0000-0000-0000-000000000002",
|
||||
"labels": [
|
||||
"ransomware"
|
||||
],
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"name": "Cryptolocker",
|
||||
"type": "malware"
|
||||
},
|
||||
{
|
||||
"created": "2017-01-01T12:34:56Z",
|
||||
"id": "relationship--00000000-0000-0000-0000-000000000003",
|
||||
"modified": "2017-01-01T12:34:56Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||
"type": "relationship"
|
||||
}
|
||||
],
|
||||
"spec_version": "2.0",
|
||||
"type": "bundle"
|
||||
}"""
|
||||
|
||||
|
||||
def test_empty_bundle():
|
||||
bundle = stix2.Bundle()
|
||||
|
||||
assert bundle.type == "bundle"
|
||||
assert bundle.id.startswith("bundle--")
|
||||
assert bundle.spec_version == "2.0"
|
||||
assert bundle.objects is None
|
||||
|
||||
|
||||
def test_bundle_with_wrong_type():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(type="not-a-bundle")
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'type': must equal 'bundle'."
|
||||
|
||||
|
||||
def test_bundle_id_must_start_with_bundle():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(id='my-prefix--')
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'id': must start with 'bundle--'."
|
||||
|
||||
|
||||
def test_bundle_with_wrong_spec_version():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bundle = stix2.Bundle(spec_version="1.2")
|
||||
|
||||
assert str(excinfo.value) == "Invalid value for Bundle 'spec_version': must equal '2.0'."
|
||||
|
||||
|
||||
def test_create_bundle(indicator, malware, relationship):
|
||||
bundle = stix2.Bundle(objects=[indicator, malware, relationship])
|
||||
|
||||
assert str(bundle) == EXPECTED_BUNDLE
|
||||
|
||||
|
||||
def test_create_bundle_with_positional_args(indicator, malware, relationship):
|
||||
bundle = stix2.Bundle(indicator, malware, relationship)
|
||||
|
||||
assert str(bundle) == EXPECTED_BUNDLE
|
Loading…
Reference in New Issue