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, INDICATOR_ID, INDICATOR_KWARGS
|
|
|
|
|
|
|
|
EXPECTED_INDICATOR = """{
|
|
|
|
"type": "indicator",
|
|
|
|
"spec_version": "2.1",
|
2018-07-11 15:43:37 +02:00
|
|
|
"id": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
2018-07-03 13:00:18 +02:00
|
|
|
"created": "2017-01-01T00:00:01.000Z",
|
|
|
|
"modified": "2017-01-01T00:00:01.000Z",
|
2018-07-12 20:33:00 +02:00
|
|
|
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
2019-07-16 22:10:25 +02:00
|
|
|
"pattern_type": "stix",
|
2019-11-22 19:24:09 +01:00
|
|
|
"pattern_version": "2.1",
|
2018-07-12 20:33:00 +02:00
|
|
|
"valid_from": "1970-01-01T00:00:01Z"
|
2018-07-03 13:00:18 +02:00
|
|
|
}"""
|
|
|
|
|
|
|
|
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
|
|
|
|
type='indicator',
|
|
|
|
spec_version='2.1',
|
2018-07-11 15:43:37 +02:00
|
|
|
id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7',
|
2018-07-03 13:00:18 +02:00
|
|
|
created='2017-01-01T00:00:01.000Z',
|
|
|
|
modified='2017-01-01T00:00:01.000Z',
|
|
|
|
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
2019-07-16 22:10:25 +02:00
|
|
|
pattern_type='stix',
|
2019-11-22 19:24:09 +01:00
|
|
|
pattern_version='2.1',
|
2018-07-12 20:33:00 +02:00
|
|
|
valid_from='1970-01-01T00:00:01Z'
|
2018-07-03 13:00:18 +02:00
|
|
|
""".split()) + ")"
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_with_all_required_properties():
|
|
|
|
now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
ind = stix2.v21.Indicator(
|
2018-07-03 13:00:18 +02:00
|
|
|
type="indicator",
|
|
|
|
id=INDICATOR_ID,
|
|
|
|
created=now,
|
|
|
|
modified=now,
|
|
|
|
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
2019-07-16 22:10:25 +02:00
|
|
|
pattern_type="stix",
|
2018-07-03 13:00:18 +02:00
|
|
|
valid_from=epoch,
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ind.revoked is False
|
|
|
|
assert str(ind) == EXPECTED_INDICATOR
|
|
|
|
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(ind))
|
|
|
|
assert rep == EXPECTED_INDICATOR_REPR
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_autogenerated_properties(indicator):
|
|
|
|
assert indicator.type == 'indicator'
|
2018-07-03 15:40:51 +02:00
|
|
|
assert indicator.spec_version == '2.1'
|
2018-07-11 15:43:37 +02:00
|
|
|
assert indicator.id == 'indicator--00000000-0000-4000-8000-000000000001'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert indicator.created == FAKE_TIME
|
|
|
|
assert indicator.modified == FAKE_TIME
|
2018-07-12 20:33:00 +02:00
|
|
|
assert indicator.indicator_types == ['malicious-activity']
|
2018-07-03 13:00:18 +02:00
|
|
|
assert indicator.pattern == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
|
|
|
assert indicator.valid_from == FAKE_TIME
|
|
|
|
|
|
|
|
assert indicator['type'] == 'indicator'
|
2018-07-03 15:40:51 +02:00
|
|
|
assert indicator['spec_version'] == '2.1'
|
2018-07-11 15:43:37 +02:00
|
|
|
assert indicator['id'] == 'indicator--00000000-0000-4000-8000-000000000001'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert indicator['created'] == FAKE_TIME
|
|
|
|
assert indicator['modified'] == FAKE_TIME
|
2018-07-12 20:33:00 +02:00
|
|
|
assert indicator['indicator_types'] == ['malicious-activity']
|
2018-07-03 13:00:18 +02:00
|
|
|
assert indicator['pattern'] == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
|
|
|
assert indicator['valid_from'] == FAKE_TIME
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_type_must_be_indicator():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(type='xxx', **INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "type"
|
|
|
|
assert excinfo.value.reason == "must equal 'indicator'."
|
|
|
|
assert str(excinfo.value) == "Invalid value for Indicator 'type': must equal 'indicator'."
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_id_must_start_with_indicator():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(id='my-prefix--', **INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "id"
|
|
|
|
assert excinfo.value.reason == "must start with 'indicator--'."
|
|
|
|
assert str(excinfo.value) == "Invalid value for Indicator 'id': must start with 'indicator--'."
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_required_properties():
|
|
|
|
with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator()
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2020-06-12 20:31:01 +02:00
|
|
|
assert excinfo.value.properties == ["pattern", "pattern_type"]
|
|
|
|
assert str(excinfo.value) == "No values for required properties for Indicator: (pattern, pattern_type)."
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_required_property_pattern():
|
|
|
|
with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo:
|
2018-07-12 20:33:00 +02:00
|
|
|
stix2.v21.Indicator(indicator_types=['malicious-activity'])
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2020-06-12 20:31:01 +02:00
|
|
|
assert excinfo.value.properties == ["pattern", "pattern_type"]
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_created_ref_invalid_format():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(created_by_ref='myprefix--12345678', **INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "created_by_ref"
|
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_revoked_invalid():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(revoked='no', **INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == "revoked"
|
|
|
|
assert excinfo.value.reason == "must be a boolean value."
|
|
|
|
|
|
|
|
|
|
|
|
def test_cannot_assign_to_indicator_attributes(indicator):
|
|
|
|
with pytest.raises(stix2.exceptions.ImmutableError) as excinfo:
|
|
|
|
indicator.valid_from = dt.datetime.now()
|
|
|
|
|
|
|
|
assert str(excinfo.value) == "Cannot modify 'valid_from' property in 'Indicator' after creation."
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_kwarg_to_indicator():
|
|
|
|
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(my_custom_property="foo", **INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.properties == ['my_custom_property']
|
|
|
|
assert str(excinfo.value) == "Unexpected properties for Indicator: (my_custom_property)."
|
|
|
|
|
|
|
|
|
|
|
|
def test_created_modified_time_are_identical_by_default():
|
|
|
|
"""By default, the created and modified times should be the same."""
|
2018-07-03 15:40:51 +02:00
|
|
|
ind = stix2.v21.Indicator(**INDICATOR_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert ind.created == ind.modified
|
|
|
|
|
|
|
|
|
2018-07-13 17:10:05 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"data", [
|
|
|
|
EXPECTED_INDICATOR,
|
|
|
|
{
|
|
|
|
"type": "indicator",
|
2019-01-23 16:56:20 +01:00
|
|
|
"id": INDICATOR_ID,
|
2018-07-13 17:10:05 +02:00
|
|
|
"created": "2017-01-01T00:00:01Z",
|
|
|
|
"modified": "2017-01-01T00:00:01Z",
|
|
|
|
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
2019-07-16 22:10:25 +02:00
|
|
|
"pattern_type": "stix",
|
2018-07-13 17:10:05 +02:00
|
|
|
"valid_from": "1970-01-01T00:00:01Z",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
def test_parse_indicator(data):
|
2018-07-03 15:40:51 +02:00
|
|
|
idctr = stix2.parse(data, version="2.1")
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert idctr.type == 'indicator'
|
2018-07-03 15:40:51 +02:00
|
|
|
assert idctr.spec_version == '2.1'
|
2018-07-03 13:00:18 +02:00
|
|
|
assert idctr.id == INDICATOR_ID
|
|
|
|
assert idctr.created == dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
assert idctr.modified == dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
assert idctr.valid_from == dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
assert idctr.pattern == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']"
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_indicator_pattern():
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(
|
2018-07-12 20:33:00 +02:00
|
|
|
indicator_types=['malicious-activity'],
|
2018-07-03 13:00:18 +02:00
|
|
|
pattern="file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e'",
|
2019-07-16 22:10:25 +02:00
|
|
|
pattern_type="stix",
|
2019-07-01 21:26:30 +02:00
|
|
|
valid_from="2017-01-01T12:34:56Z",
|
2018-07-03 13:00:18 +02:00
|
|
|
)
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == 'pattern'
|
|
|
|
assert 'input is missing square brackets' in excinfo.value.reason
|
|
|
|
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
2018-07-03 15:40:51 +02:00
|
|
|
stix2.v21.Indicator(
|
2018-07-12 20:33:00 +02:00
|
|
|
indicator_types=['malicious-activity'],
|
2018-07-03 13:00:18 +02:00
|
|
|
pattern='[file:hashes.MD5 = "d41d8cd98f00b204e9800998ecf8427e"]',
|
2019-07-16 22:10:25 +02:00
|
|
|
pattern_type="stix",
|
2019-07-01 21:41:44 +02:00
|
|
|
valid_from="2017-01-01T12:34:56Z",
|
2018-07-03 13:00:18 +02:00
|
|
|
)
|
2018-07-03 15:40:51 +02:00
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2018-07-03 13:00:18 +02:00
|
|
|
assert excinfo.value.prop_name == 'pattern'
|
|
|
|
assert 'mismatched input' in excinfo.value.reason
|
2019-12-16 22:32:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_with_custom_embedded_objs():
|
|
|
|
now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
|
|
|
|
ext_ref = stix2.v21.ExternalReference(
|
|
|
|
source_name="Test",
|
|
|
|
description="Example Custom Ext Ref",
|
|
|
|
random_custom_prop="This is a custom property",
|
|
|
|
allow_custom=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
ind = stix2.v21.Indicator(
|
|
|
|
type="indicator",
|
|
|
|
id=INDICATOR_ID,
|
|
|
|
created=now,
|
|
|
|
modified=now,
|
|
|
|
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
|
|
|
pattern_type="stix",
|
|
|
|
valid_from=epoch,
|
|
|
|
indicator_types=['malicious-activity'],
|
|
|
|
external_references=[ext_ref],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ind.indicator_types == ['malicious-activity']
|
|
|
|
assert len(ind.external_references) == 1
|
|
|
|
assert ind.external_references[0] == ext_ref
|
2019-12-17 17:57:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_with_custom_embed_objs_extra_props_error():
|
|
|
|
ext_ref = stix2.v21.ExternalReference(
|
|
|
|
source_name="Test",
|
|
|
|
description="Example Custom Ext Ref",
|
|
|
|
random_custom_prop="This is a custom property",
|
|
|
|
allow_custom=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
|
|
|
|
stix2.v21.Indicator(external_references=[ext_ref], bad_custom_prop="shouldn't be here", **INDICATOR_KWARGS)
|
|
|
|
|
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
|
|
|
assert excinfo.value.properties == ['bad_custom_prop']
|
|
|
|
assert str(excinfo.value) == "Unexpected properties for Indicator: (bad_custom_prop)."
|
2019-12-20 20:34:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_indicator_stix20_invalid_pattern():
|
|
|
|
now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
|
|
|
epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
|
2020-01-03 20:34:59 +01:00
|
|
|
patrn = "[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 SECONDS WITHIN 6 SECONDS"
|
2019-12-20 20:34:49 +01:00
|
|
|
|
|
|
|
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
|
|
|
stix2.v21.Indicator(
|
|
|
|
type="indicator",
|
|
|
|
id=INDICATOR_ID,
|
|
|
|
created=now,
|
|
|
|
modified=now,
|
2020-01-03 20:34:59 +01:00
|
|
|
pattern=patrn,
|
2019-12-20 20:34:49 +01:00
|
|
|
pattern_type="stix",
|
|
|
|
valid_from=epoch,
|
|
|
|
indicator_types=['malicious-activity'],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert excinfo.value.cls == stix2.v21.Indicator
|
2020-02-25 02:02:26 +01:00
|
|
|
assert "FAIL: Duplicate qualifier type encountered: WITHIN" in str(excinfo.value)
|
2020-01-03 20:34:59 +01:00
|
|
|
|
|
|
|
ind = stix2.v21.Indicator(
|
|
|
|
type="indicator",
|
|
|
|
id=INDICATOR_ID,
|
|
|
|
created=now,
|
|
|
|
modified=now,
|
|
|
|
pattern=patrn,
|
|
|
|
pattern_type="stix",
|
|
|
|
pattern_version="2.0",
|
|
|
|
valid_from=epoch,
|
|
|
|
indicator_types=['malicious-activity'],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert ind.id == INDICATOR_ID
|
|
|
|
assert ind.indicator_types == ['malicious-activity']
|
|
|
|
assert ind.pattern == patrn
|
|
|
|
assert ind.pattern_type == "stix"
|
|
|
|
assert ind.pattern_version == "2.0"
|