215 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
| import datetime as dt
 | |
| import json
 | |
| 
 | |
| import pytest
 | |
| import pytz
 | |
| 
 | |
| import stix2
 | |
| 
 | |
| from ...exceptions import InvalidValueError, PropertyPresenceError
 | |
| from .constants import FAKE_TIME, MALWARE_ID, MALWARE_KWARGS
 | |
| 
 | |
| EXPECTED_MALWARE = """{
 | |
|     "type": "malware",
 | |
|     "spec_version": "2.1",
 | |
|     "id": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
 | |
|     "created": "2016-05-12T08:17:27.000Z",
 | |
|     "modified": "2016-05-12T08:17:27.000Z",
 | |
|     "name": "Cryptolocker",
 | |
|     "is_family": false
 | |
| }"""
 | |
| 
 | |
| 
 | |
| def test_malware_with_all_required_properties():
 | |
|     now = dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc)
 | |
| 
 | |
|     mal = stix2.v21.Malware(
 | |
|         type="malware",
 | |
|         id=MALWARE_ID,
 | |
|         created=now,
 | |
|         modified=now,
 | |
|         name="Cryptolocker",
 | |
|         is_family=False,
 | |
|     )
 | |
| 
 | |
|     assert str(mal) == EXPECTED_MALWARE
 | |
| 
 | |
| 
 | |
| def test_malware_autogenerated_properties(malware):
 | |
|     assert malware.type == 'malware'
 | |
|     assert malware.id == 'malware--00000000-0000-4000-8000-000000000001'
 | |
|     assert malware.created == FAKE_TIME
 | |
|     assert malware.modified == FAKE_TIME
 | |
|     assert malware.malware_types == ['ransomware']
 | |
|     assert malware.name == "Cryptolocker"
 | |
| 
 | |
|     assert malware['type'] == 'malware'
 | |
|     assert malware['id'] == 'malware--00000000-0000-4000-8000-000000000001'
 | |
|     assert malware['created'] == FAKE_TIME
 | |
|     assert malware['modified'] == FAKE_TIME
 | |
|     assert malware['malware_types'] == ['ransomware']
 | |
|     assert malware['name'] == "Cryptolocker"
 | |
| 
 | |
| 
 | |
| def test_malware_type_must_be_malware():
 | |
|     with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
 | |
|         stix2.v21.Malware(type='xxx', **MALWARE_KWARGS)
 | |
| 
 | |
|     assert excinfo.value.cls == stix2.v21.Malware
 | |
|     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:
 | |
|         stix2.v21.Malware(id='my-prefix--', **MALWARE_KWARGS)
 | |
| 
 | |
|     assert excinfo.value.cls == stix2.v21.Malware
 | |
|     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:
 | |
|         stix2.v21.Malware()
 | |
| 
 | |
|     assert excinfo.value.cls == stix2.v21.Malware
 | |
|     assert excinfo.value.properties == ["is_family"]
 | |
| 
 | |
| 
 | |
| def test_malware_required_property_name():
 | |
|     with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo:
 | |
|         stix2.v21.Malware(malware_types=['ransomware'])
 | |
| 
 | |
|     assert excinfo.value.cls == stix2.v21.Malware
 | |
|     assert excinfo.value.properties == ["is_family"]
 | |
| 
 | |
| 
 | |
| 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:
 | |
|         stix2.v21.Malware(my_custom_property="foo", **MALWARE_KWARGS)
 | |
| 
 | |
|     assert excinfo.value.cls == stix2.v21.Malware
 | |
|     assert excinfo.value.properties == ['my_custom_property']
 | |
|     assert str(excinfo.value) == "Unexpected properties for Malware: (my_custom_property)."
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "data", [
 | |
|         EXPECTED_MALWARE,
 | |
|         {
 | |
|             "type": "malware",
 | |
|             "spec_version": "2.1",
 | |
|             "id": MALWARE_ID,
 | |
|             "created": "2016-05-12T08:17:27.000Z",
 | |
|             "modified": "2016-05-12T08:17:27.000Z",
 | |
|             "name": "Cryptolocker",
 | |
|             "is_family": False,
 | |
|         },
 | |
|     ],
 | |
| )
 | |
| def test_parse_malware(data):
 | |
|     mal = stix2.parse(data)
 | |
| 
 | |
|     assert mal.type == 'malware'
 | |
|     assert mal.spec_version == '2.1'
 | |
|     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)
 | |
|     assert mal.name == 'Cryptolocker'
 | |
|     assert not mal.is_family
 | |
| 
 | |
| 
 | |
| def test_parse_malware_invalid_types():
 | |
|     data = json.loads(EXPECTED_MALWARE)
 | |
|     data["malware_types"] = 1  # Oops, not a list
 | |
|     data = json.dumps(data)
 | |
|     with pytest.raises(InvalidValueError) as excinfo:
 | |
|         stix2.parse(data)
 | |
|     assert "Invalid value for Malware 'malware_types'" in str(excinfo.value)
 | |
| 
 | |
| 
 | |
| 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)
 | |
|     mal = stix2.parse(data, version="2.1")
 | |
|     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)
 | |
|     mal = stix2.parse(data, version="2.1")
 | |
|     assert mal['kill_chain_phases'][0]['phase_name'] == "1"
 | |
| 
 | |
| 
 | |
| def test_malware_invalid_last_before_first():
 | |
|     with pytest.raises(ValueError) as excinfo:
 | |
|         stix2.v21.Malware(first_seen="2017-01-01T12:34:56.000Z", last_seen="2017-01-01T12:33:56.000Z", **MALWARE_KWARGS)
 | |
| 
 | |
|     assert "'last_seen' must be greater than or equal to 'first_seen'" in str(excinfo.value)
 | |
| 
 | |
| 
 | |
| def test_malware_family_no_name():
 | |
|     with pytest.raises(PropertyPresenceError):
 | |
|         stix2.parse({
 | |
|             "type": "malware",
 | |
|             "id": MALWARE_ID,
 | |
|             "spec_version": "2.1",
 | |
|             "is_family": True,
 | |
|             "malware_types": ["a type"],
 | |
|         })
 | |
| 
 | |
| 
 | |
| def test_malware_non_family_no_name():
 | |
|     stix2.parse({
 | |
|         "type": "malware",
 | |
|         "id": MALWARE_ID,
 | |
|         "spec_version": "2.1",
 | |
|         "is_family": False,
 | |
|         "malware_types": ["something"],
 | |
|     })
 | |
| 
 | |
| 
 | |
| def test_malware_with_os_refs():
 | |
|     software = stix2.parse({
 | |
|         "type": "software",
 | |
|         "name": "SuperOS",
 | |
|         "spec_version": "2.1",
 | |
|     })
 | |
| 
 | |
|     malware = stix2.parse({
 | |
|         "type": "malware",
 | |
|         "id": MALWARE_ID,
 | |
|         "spec_version": "2.1",
 | |
|         "is_family": False,
 | |
|         "malware_types": ["something"],
 | |
|         "operating_system_refs": [software],
 | |
|     })
 | |
| 
 | |
|     assert malware["operating_system_refs"][0] == software["id"]
 |