2018-07-03 13:00:18 +02:00
|
|
|
import datetime as dt
|
|
|
|
import re
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
import pytz
|
|
|
|
|
|
|
|
import stix2
|
|
|
|
|
|
|
|
from .constants import FAKE_TIME, MALWARE_ID, MALWARE_KWARGS
|
|
|
|
|
|
|
|
EXPECTED_MALWARE = """{
|
|
|
|
"type": "malware",
|
|
|
|
"spec_version": "2.1",
|
2018-07-11 15:43:37 +02:00
|
|
|
"id": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
|
2018-07-03 13:00:18 +02:00
|
|
|
"created": "2016-05-12T08:17:27.000Z",
|
|
|
|
"modified": "2016-05-12T08:17:27.000Z",
|
|
|
|
"name": "Cryptolocker",
|
2018-07-12 20:33:00 +02:00
|
|
|
"malware_types": [
|
2018-07-03 13:00:18 +02:00
|
|
|
"ransomware"
|
2018-07-12 20:33:00 +02:00
|
|
|
]
|
2018-07-03 13:00:18 +02:00
|
|
|
}"""
|
|
|
|
|
|
|
|
|
|
|
|
def test_malware_with_all_required_properties():
|
|
|
|
now = dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
|
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
mal = stix2.v21.Malware(
|
2018-07-03 13:00:18 +02:00
|
|
|
type="malware",
|
|
|
|
id=MALWARE_ID,
|
|
|
|
created=now,
|
|
|
|
modified=now,
|
2018-07-12 20:33:00 +02:00
|
|
|
malware_types=["ransomware"],
|
2018-07-03 13:00:18 +02:00
|
|
|
name="Cryptolocker",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert str(mal) == EXPECTED_MALWARE
|
|
|
|
|
|
|
|
|
|
|
|
def test_malware_autogenerated_properties(malware):
|
|
|
|
assert malware.type == 'malware'
|
2018-07-11 15:43:37 +02:00
|
|
|
assert malware.id == 'malware--00000000-0000-4000-8000-000000000001'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert malware.created == FAKE_TIME
|
|
|
|
assert malware.modified == FAKE_TIME
|
2018-07-12 20:33:00 +02:00
|
|
|
assert malware.malware_types == ['ransomware']
|
2018-07-03 13:00:18 +02:00
|
|
|
assert malware.name == "Cryptolocker"
|
|
|
|
|
|
|
|
assert malware['type'] == 'malware'
|
2018-07-11 15:43:37 +02:00
|
|
|
assert malware['id'] == 'malware--00000000-0000-4000-8000-000000000001'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert malware['created'] == FAKE_TIME
|
|
|
|
assert malware['modified'] == FAKE_TIME
|
2018-07-12 20:33:00 +02:00
|
|
|
assert malware['malware_types'] == ['ransomware']
|
2018-07-03 13:00:18 +02:00
|
|
|
assert malware['name'] == "Cryptolocker"
|
|
|
|
|
|
|
|
|
|
|
|
def test_malware_type_must_be_malware():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Malware(type='xxx', **MALWARE_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Malware
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "type"
|
|
|
|
assert excinfo.value.reason == "must equal 'malware'."
|
|
|
|
assert str(excinfo.value) == "Invalid value for Malware 'type': must equal 'malware'."
|
|
|
|
|
|
|
|
|
|
|
|
def test_malware_id_must_start_with_malware():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Malware(id='my-prefix--', **MALWARE_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Malware
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "id"
|
|
|
|
assert excinfo.value.reason == "must start with 'malware--'."
|
|
|
|
assert str(excinfo.value) == "Invalid value for Malware 'id': must start with 'malware--'."
|
|
|
|
|
|
|
|
|
|
|
|
def test_malware_required_properties():
|
|
|
|
with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Malware()
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Malware
|
2018-07-25 19:34:56 +02:00
|
|
|
assert excinfo.value.properties == ["malware_types", "name"]
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_malware_required_property_name():
|
|
|
|
with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo:
|
2018-07-25 19:34:56 +02:00
|
|
|
stix2.v21.Malware(malware_types=['ransomware'])
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Malware
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.properties == ["name"]
|
|
|
|
|
|
|
|
|
|
|
|
def test_cannot_assign_to_malware_attributes(malware):
|
|
|
|
with pytest.raises(stix2.exceptions.ImmutableError) as excinfo:
|
|
|
|
malware.name = "Cryptolocker II"
|
|
|
|
|
|
|
|
assert str(excinfo.value) == "Cannot modify 'name' property in 'Malware' after creation."
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_kwarg_to_malware():
|
|
|
|
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Malware(my_custom_property="foo", **MALWARE_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Malware
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.properties == ['my_custom_property']
|
|
|
|
assert str(excinfo.value) == "Unexpected properties for Malware: (my_custom_property)."
|
|
|
|
|
|
|
|
|
2018-07-13 17:10:05 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"data", [
|
|
|
|
EXPECTED_MALWARE,
|
|
|
|
{
|
|
|
|
"type": "malware",
|
|
|
|
"spec_version": "2.1",
|
2019-01-23 16:56:20 +01:00
|
|
|
"id": MALWARE_ID,
|
2018-07-13 17:10:05 +02:00
|
|
|
"created": "2016-05-12T08:17:27.000Z",
|
|
|
|
"modified": "2016-05-12T08:17:27.000Z",
|
|
|
|
"malware_types": ["ransomware"],
|
|
|
|
"name": "Cryptolocker",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
def test_parse_malware(data):
|
2018-07-12 20:33:00 +02:00
|
|
|
mal = stix2.parse(data)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert mal.type == 'malware'
|
2018-07-03 15:40:51 +02:00
|
|
|
assert mal.spec_version == '2.1'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert mal.id == MALWARE_ID
|
|
|
|
assert mal.created == dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
|
|
|
|
assert mal.modified == dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
|
2018-07-12 20:33:00 +02:00
|
|
|
assert mal.malware_types == ['ransomware']
|
|
|
|
assert mal.name == 'Cryptolocker'
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_malware_invalid_labels():
|
|
|
|
data = re.compile('\\[.+\\]', re.DOTALL).sub('1', EXPECTED_MALWARE)
|
|
|
|
with pytest.raises(ValueError) as excinfo:
|
2018-07-12 20:33:00 +02:00
|
|
|
stix2.parse(data)
|
|
|
|
assert "Invalid value for Malware 'malware_types'" in str(excinfo.value)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_malware_kill_chain_phases():
|
|
|
|
kill_chain = """
|
|
|
|
"kill_chain_phases": [
|
|
|
|
{
|
|
|
|
"kill_chain_name": "lockheed-martin-cyber-kill-chain",
|
|
|
|
"phase_name": "reconnaissance"
|
|
|
|
}
|
|
|
|
]"""
|
|
|
|
data = EXPECTED_MALWARE.replace('malware"', 'malware",%s' % kill_chain)
|
2018-07-03 15:40:51 +02:00
|
|
|
mal = stix2.parse(data, version="2.1")
|
2018-07-03 13:00:18 +02:00
|
|
|
assert mal.kill_chain_phases[0].kill_chain_name == "lockheed-martin-cyber-kill-chain"
|
|
|
|
assert mal.kill_chain_phases[0].phase_name == "reconnaissance"
|
|
|
|
assert mal['kill_chain_phases'][0]['kill_chain_name'] == "lockheed-martin-cyber-kill-chain"
|
|
|
|
assert mal['kill_chain_phases'][0]['phase_name'] == "reconnaissance"
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_malware_clean_kill_chain_phases():
|
|
|
|
kill_chain = """
|
|
|
|
"kill_chain_phases": [
|
|
|
|
{
|
|
|
|
"kill_chain_name": "lockheed-martin-cyber-kill-chain",
|
|
|
|
"phase_name": 1
|
|
|
|
}
|
|
|
|
]"""
|
|
|
|
data = EXPECTED_MALWARE.replace('2.1"', '2.1",%s' % kill_chain)
|
2018-07-03 15:40:51 +02:00
|
|
|
mal = stix2.parse(data, version="2.1")
|
2018-07-03 13:00:18 +02:00
|
|
|
assert mal['kill_chain_phases'][0]['phase_name'] == "1"
|