parent
07ccf9ec03
commit
e01ce132db
|
@ -176,7 +176,7 @@ class _STIXBase(collections.Mapping):
|
||||||
if 'modified' not in kwargs:
|
if 'modified' not in kwargs:
|
||||||
kwargs['modified'] = get_timestamp()
|
kwargs['modified'] = get_timestamp()
|
||||||
else:
|
else:
|
||||||
new_modified_property = parse_into_datetime(kwargs['modified'])
|
new_modified_property = parse_into_datetime(kwargs['modified'], precision='millisecond')
|
||||||
if new_modified_property < self.modified:
|
if new_modified_property < self.modified:
|
||||||
raise InvalidValueError(cls, 'modified', "The new modified datetime cannot be before the current modified datatime.")
|
raise InvalidValueError(cls, 'modified', "The new modified datetime cannot be before the current modified datatime.")
|
||||||
new_obj_inner.update(kwargs)
|
new_obj_inner.update(kwargs)
|
||||||
|
|
|
@ -7,8 +7,8 @@ from .utils import NOW
|
||||||
|
|
||||||
COMMON_PROPERTIES = {
|
COMMON_PROPERTIES = {
|
||||||
# 'type' and 'id' should be defined on each individual type
|
# 'type' and 'id' should be defined on each individual type
|
||||||
'created': TimestampProperty(default=lambda: NOW),
|
'created': TimestampProperty(default=lambda: NOW, precision='millisecond'),
|
||||||
'modified': TimestampProperty(default=lambda: NOW),
|
'modified': TimestampProperty(default=lambda: NOW, precision='millisecond'),
|
||||||
'external_references': ListProperty(ExternalReference),
|
'external_references': ListProperty(ExternalReference),
|
||||||
'revoked': BooleanProperty(),
|
'revoked': BooleanProperty(),
|
||||||
'labels': ListProperty(StringProperty),
|
'labels': ListProperty(StringProperty),
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import collections
|
import collections
|
||||||
import datetime as dt
|
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from dateutil import parser
|
|
||||||
import pytz
|
|
||||||
from six import text_type
|
from six import text_type
|
||||||
|
|
||||||
from .base import _Observable, _STIXBase
|
from .base import _Observable, _STIXBase
|
||||||
from .exceptions import DictionaryKeyError
|
from .exceptions import DictionaryKeyError
|
||||||
from .utils import get_dict
|
from .utils import get_dict, parse_into_datetime
|
||||||
|
|
||||||
|
|
||||||
class Property(object):
|
class Property(object):
|
||||||
|
@ -215,26 +212,15 @@ class BooleanProperty(Property):
|
||||||
|
|
||||||
class TimestampProperty(Property):
|
class TimestampProperty(Property):
|
||||||
|
|
||||||
def clean(self, value):
|
def __init__(self, precision=None, **kwargs):
|
||||||
if isinstance(value, dt.date):
|
self.precision = precision
|
||||||
if hasattr(value, 'hour'):
|
super(TimestampProperty, self).__init__(**kwargs)
|
||||||
return value
|
|
||||||
else:
|
|
||||||
# Add a time component
|
|
||||||
return dt.datetime.combine(value, dt.time(), tzinfo=pytz.utc)
|
|
||||||
|
|
||||||
# value isn't a date or datetime object so assume it's a string
|
def clean(self, value):
|
||||||
try:
|
try:
|
||||||
parsed = parser.parse(value)
|
return parse_into_datetime(value, self.precision)
|
||||||
except TypeError:
|
except ValueError:
|
||||||
# Unknown format
|
raise
|
||||||
raise ValueError("must be a datetime object, date object, or "
|
|
||||||
"timestamp string in a recognizable format.")
|
|
||||||
if parsed.tzinfo:
|
|
||||||
return parsed.astimezone(pytz.utc)
|
|
||||||
else:
|
|
||||||
# Doesn't have timezone info in the string; assume UTC
|
|
||||||
return pytz.utc.localize(parsed)
|
|
||||||
|
|
||||||
|
|
||||||
class ObservableProperty(Property):
|
class ObservableProperty(Property):
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import datetime as dt
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -13,12 +12,12 @@ from .constants import (FAKE_TIME, INDICATOR_KWARGS, MALWARE_KWARGS,
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def clock(monkeypatch):
|
def clock(monkeypatch):
|
||||||
|
|
||||||
class mydatetime(dt.datetime):
|
class mydatetime(stix2.utils.STIXdatetime):
|
||||||
@classmethod
|
@classmethod
|
||||||
def now(cls, tz=None):
|
def now(cls, tz=None):
|
||||||
return FAKE_TIME
|
return FAKE_TIME
|
||||||
|
|
||||||
monkeypatch.setattr(dt, 'datetime', mydatetime)
|
monkeypatch.setattr(stix2.utils, 'STIXdatetime', mydatetime)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .constants import ATTACK_PATTERN_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-05-12T08:17:27Z",
|
"created": "2016-05-12T08:17:27.000Z",
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"external_references": [
|
"external_references": [
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ EXPECTED = """{
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
"id": "attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
||||||
"modified": "2016-05-12T08:17:27Z",
|
"modified": "2016-05-12T08:17:27.000Z",
|
||||||
"name": "Spear Phishing",
|
"name": "Spear Phishing",
|
||||||
"type": "attack-pattern"
|
"type": "attack-pattern"
|
||||||
}"""
|
}"""
|
||||||
|
@ -27,8 +27,8 @@ EXPECTED = """{
|
||||||
def test_attack_pattern_example():
|
def test_attack_pattern_example():
|
||||||
ap = stix2.AttackPattern(
|
ap = stix2.AttackPattern(
|
||||||
id="attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
id="attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
||||||
created="2016-05-12T08:17:27Z",
|
created="2016-05-12T08:17:27.000Z",
|
||||||
modified="2016-05-12T08:17:27Z",
|
modified="2016-05-12T08:17:27.000Z",
|
||||||
name="Spear Phishing",
|
name="Spear Phishing",
|
||||||
external_references=[{
|
external_references=[{
|
||||||
"source_name": "capec",
|
"source_name": "capec",
|
||||||
|
@ -45,8 +45,8 @@ def test_attack_pattern_example():
|
||||||
{
|
{
|
||||||
"type": "attack-pattern",
|
"type": "attack-pattern",
|
||||||
"id": "attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
"id": "attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
||||||
"created": "2016-05-12T08:17:27Z",
|
"created": "2016-05-12T08:17:27.000Z",
|
||||||
"modified": "2016-05-12T08:17:27Z",
|
"modified": "2016-05-12T08:17:27.000Z",
|
||||||
"description": "...",
|
"description": "...",
|
||||||
"external_references": [
|
"external_references": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,30 +7,30 @@ EXPECTED_BUNDLE = """{
|
||||||
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
||||||
"objects": [
|
"objects": [
|
||||||
{
|
{
|
||||||
"created": "2017-01-01T12:34:56Z",
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
"id": "indicator--00000000-0000-0000-0000-000000000001",
|
"id": "indicator--00000000-0000-0000-0000-000000000001",
|
||||||
"labels": [
|
"labels": [
|
||||||
"malicious-activity"
|
"malicious-activity"
|
||||||
],
|
],
|
||||||
"modified": "2017-01-01T12:34:56Z",
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||||
"type": "indicator",
|
"type": "indicator",
|
||||||
"valid_from": "2017-01-01T12:34:56Z"
|
"valid_from": "2017-01-01T12:34:56Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"created": "2017-01-01T12:34:56Z",
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
"id": "malware--00000000-0000-0000-0000-000000000002",
|
"id": "malware--00000000-0000-0000-0000-000000000002",
|
||||||
"labels": [
|
"labels": [
|
||||||
"ransomware"
|
"ransomware"
|
||||||
],
|
],
|
||||||
"modified": "2017-01-01T12:34:56Z",
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
"name": "Cryptolocker",
|
"name": "Cryptolocker",
|
||||||
"type": "malware"
|
"type": "malware"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"created": "2017-01-01T12:34:56Z",
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
"id": "relationship--00000000-0000-0000-0000-000000000003",
|
"id": "relationship--00000000-0000-0000-0000-000000000003",
|
||||||
"modified": "2017-01-01T12:34:56Z",
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
"relationship_type": "indicates",
|
"relationship_type": "indicates",
|
||||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||||
|
|
|
@ -9,11 +9,11 @@ from .constants import CAMPAIGN_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-04-06T20:03:00Z",
|
"created": "2016-04-06T20:03:00.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
||||||
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"modified": "2016-04-06T20:03:00Z",
|
"modified": "2016-04-06T20:03:00.000Z",
|
||||||
"name": "Green Group Attacks Against Finance",
|
"name": "Green Group Attacks Against Finance",
|
||||||
"type": "campaign"
|
"type": "campaign"
|
||||||
}"""
|
}"""
|
||||||
|
@ -23,8 +23,8 @@ def test_campaign_example():
|
||||||
campaign = stix2.Campaign(
|
campaign = stix2.Campaign(
|
||||||
id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T20:03:00Z",
|
created="2016-04-06T20:03:00.000Z",
|
||||||
modified="2016-04-06T20:03:00Z",
|
modified="2016-04-06T20:03:00.000Z",
|
||||||
name="Green Group Attacks Against Finance",
|
name="Green Group Attacks Against Finance",
|
||||||
description="Campaign by Green Group against a series of targets in the financial services sector."
|
description="Campaign by Green Group against a series of targets in the financial services sector."
|
||||||
)
|
)
|
||||||
|
@ -37,8 +37,8 @@ def test_campaign_example():
|
||||||
{
|
{
|
||||||
"type": "campaign",
|
"type": "campaign",
|
||||||
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"created": "2016-04-06T20:03:00Z",
|
"created": "2016-04-06T20:03:00.000Z",
|
||||||
"modified": "2016-04-06T20:03:00Z",
|
"modified": "2016-04-06T20:03:00.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
||||||
"name": "Green Group Attacks Against Finance",
|
"name": "Green Group Attacks Against Finance",
|
||||||
|
|
|
@ -9,11 +9,11 @@ from .constants import COURSE_OF_ACTION_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
|
"description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
|
||||||
"id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
"name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
||||||
"type": "course-of-action"
|
"type": "course-of-action"
|
||||||
}"""
|
}"""
|
||||||
|
@ -23,8 +23,8 @@ def test_course_of_action_example():
|
||||||
coa = stix2.CourseOfAction(
|
coa = stix2.CourseOfAction(
|
||||||
id="course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
id="course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T20:03:48Z",
|
created="2016-04-06T20:03:48.000Z",
|
||||||
modified="2016-04-06T20:03:48Z",
|
modified="2016-04-06T20:03:48.000Z",
|
||||||
name="Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
name="Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
||||||
description="This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..."
|
description="This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..."
|
||||||
)
|
)
|
||||||
|
@ -35,11 +35,11 @@ def test_course_of_action_example():
|
||||||
@pytest.mark.parametrize("data", [
|
@pytest.mark.parametrize("data", [
|
||||||
EXPECTED,
|
EXPECTED,
|
||||||
{
|
{
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
|
"description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
|
||||||
"id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
"name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
|
||||||
"type": "course-of-action"
|
"type": "course-of-action"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import datetime as dt
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from stix2 import utils
|
||||||
|
|
||||||
from .constants import FAKE_TIME
|
from .constants import FAKE_TIME
|
||||||
|
|
||||||
|
|
||||||
def test_clock(clock):
|
def test_clock(clock):
|
||||||
assert dt.datetime.now() == FAKE_TIME
|
assert utils.STIXdatetime.now() == FAKE_TIME
|
||||||
|
|
||||||
|
|
||||||
def test_my_uuid4_fixture(uuid4):
|
def test_my_uuid4_fixture(uuid4):
|
||||||
|
|
|
@ -9,10 +9,10 @@ from .constants import IDENTITY_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2015-12-21T19:59:11Z",
|
"created": "2015-12-21T19:59:11.000Z",
|
||||||
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
||||||
"identity_class": "individual",
|
"identity_class": "individual",
|
||||||
"modified": "2015-12-21T19:59:11Z",
|
"modified": "2015-12-21T19:59:11.000Z",
|
||||||
"name": "John Smith",
|
"name": "John Smith",
|
||||||
"type": "identity"
|
"type": "identity"
|
||||||
}"""
|
}"""
|
||||||
|
@ -21,8 +21,8 @@ EXPECTED = """{
|
||||||
def test_identity_example():
|
def test_identity_example():
|
||||||
identity = stix2.Identity(
|
identity = stix2.Identity(
|
||||||
id="identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
id="identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
||||||
created="2015-12-21T19:59:11Z",
|
created="2015-12-21T19:59:11.000Z",
|
||||||
modified="2015-12-21T19:59:11Z",
|
modified="2015-12-21T19:59:11.000Z",
|
||||||
name="John Smith",
|
name="John Smith",
|
||||||
identity_class="individual",
|
identity_class="individual",
|
||||||
)
|
)
|
||||||
|
@ -33,10 +33,10 @@ def test_identity_example():
|
||||||
@pytest.mark.parametrize("data", [
|
@pytest.mark.parametrize("data", [
|
||||||
EXPECTED,
|
EXPECTED,
|
||||||
{
|
{
|
||||||
"created": "2015-12-21T19:59:11Z",
|
"created": "2015-12-21T19:59:11.000Z",
|
||||||
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
||||||
"identity_class": "individual",
|
"identity_class": "individual",
|
||||||
"modified": "2015-12-21T19:59:11Z",
|
"modified": "2015-12-21T19:59:11.000Z",
|
||||||
"name": "John Smith",
|
"name": "John Smith",
|
||||||
"type": "identity"
|
"type": "identity"
|
||||||
},
|
},
|
||||||
|
@ -56,8 +56,8 @@ def test_parse_no_type():
|
||||||
stix2.parse("""
|
stix2.parse("""
|
||||||
{
|
{
|
||||||
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
|
||||||
"created": "2015-12-21T19:59:11Z",
|
"created": "2015-12-21T19:59:11.000Z",
|
||||||
"modified": "2015-12-21T19:59:11Z",
|
"modified": "2015-12-21T19:59:11.000Z",
|
||||||
"name": "John Smith",
|
"name": "John Smith",
|
||||||
"identity_class": "individual"
|
"identity_class": "individual"
|
||||||
}""")
|
}""")
|
||||||
|
|
|
@ -10,22 +10,22 @@ from .constants import FAKE_TIME, INDICATOR_ID, INDICATOR_KWARGS
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_INDICATOR = """{
|
EXPECTED_INDICATOR = """{
|
||||||
"created": "2017-01-01T00:00:01Z",
|
"created": "2017-01-01T00:00:01.000Z",
|
||||||
"id": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
"id": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
"labels": [
|
"labels": [
|
||||||
"malicious-activity"
|
"malicious-activity"
|
||||||
],
|
],
|
||||||
"modified": "2017-01-01T00:00:01Z",
|
"modified": "2017-01-01T00:00:01.000Z",
|
||||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||||
"type": "indicator",
|
"type": "indicator",
|
||||||
"valid_from": "1970-01-01T00:00:01Z"
|
"valid_from": "1970-01-01T00:00:01Z"
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
|
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
|
||||||
created=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
created=STIXdatetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||||
id='indicator--01234567-89ab-cdef-0123-456789abcdef',
|
id='indicator--01234567-89ab-cdef-0123-456789abcdef',
|
||||||
labels=['malicious-activity'],
|
labels=['malicious-activity'],
|
||||||
modified=datetime.datetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
modified=STIXdatetime(2017, 1, 1, 0, 0, 1, tzinfo=<UTC>),
|
||||||
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||||
type='indicator',
|
type='indicator',
|
||||||
valid_from=datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=<UTC>)
|
valid_from=datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=<UTC>)
|
||||||
|
@ -48,6 +48,8 @@ def test_indicator_with_all_required_properties():
|
||||||
|
|
||||||
assert str(ind) == EXPECTED_INDICATOR
|
assert str(ind) == EXPECTED_INDICATOR
|
||||||
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(ind))
|
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(ind))
|
||||||
|
print(rep)
|
||||||
|
print(EXPECTED_INDICATOR_REPR)
|
||||||
assert rep == EXPECTED_INDICATOR_REPR
|
assert rep == EXPECTED_INDICATOR_REPR
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ EXPECTED = """{
|
||||||
"aliases": [
|
"aliases": [
|
||||||
"Zookeeper"
|
"Zookeeper"
|
||||||
],
|
],
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "Incidents usually feature a shared TTP of a bobcat being released...",
|
"description": "Incidents usually feature a shared TTP of a bobcat being released...",
|
||||||
"goals": [
|
"goals": [
|
||||||
|
@ -21,7 +21,7 @@ EXPECTED = """{
|
||||||
"damage"
|
"damage"
|
||||||
],
|
],
|
||||||
"id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
"id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Bobcat Breakin",
|
"name": "Bobcat Breakin",
|
||||||
"type": "intrusion-set"
|
"type": "intrusion-set"
|
||||||
}"""
|
}"""
|
||||||
|
@ -31,8 +31,8 @@ def test_intrusion_set_example():
|
||||||
intrusion_set = stix2.IntrusionSet(
|
intrusion_set = stix2.IntrusionSet(
|
||||||
id="intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
id="intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T20:03:48Z",
|
created="2016-04-06T20:03:48.000Z",
|
||||||
modified="2016-04-06T20:03:48Z",
|
modified="2016-04-06T20:03:48.000Z",
|
||||||
name="Bobcat Breakin",
|
name="Bobcat Breakin",
|
||||||
description="Incidents usually feature a shared TTP of a bobcat being released...",
|
description="Incidents usually feature a shared TTP of a bobcat being released...",
|
||||||
aliases=["Zookeeper"],
|
aliases=["Zookeeper"],
|
||||||
|
@ -48,7 +48,7 @@ def test_intrusion_set_example():
|
||||||
"aliases": [
|
"aliases": [
|
||||||
"Zookeeper"
|
"Zookeeper"
|
||||||
],
|
],
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "Incidents usually feature a shared TTP of a bobcat being released...",
|
"description": "Incidents usually feature a shared TTP of a bobcat being released...",
|
||||||
"goals": [
|
"goals": [
|
||||||
|
@ -57,7 +57,7 @@ def test_intrusion_set_example():
|
||||||
"damage"
|
"damage"
|
||||||
],
|
],
|
||||||
"id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
"id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Bobcat Breakin",
|
"name": "Bobcat Breakin",
|
||||||
"type": "intrusion-set"
|
"type": "intrusion-set"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,12 +10,12 @@ from .constants import FAKE_TIME, MALWARE_ID, MALWARE_KWARGS
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_MALWARE = """{
|
EXPECTED_MALWARE = """{
|
||||||
"created": "2016-05-12T08:17:27Z",
|
"created": "2016-05-12T08:17:27.000Z",
|
||||||
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||||
"labels": [
|
"labels": [
|
||||||
"ransomware"
|
"ransomware"
|
||||||
],
|
],
|
||||||
"modified": "2016-05-12T08:17:27Z",
|
"modified": "2016-05-12T08:17:27.000Z",
|
||||||
"name": "Cryptolocker",
|
"name": "Cryptolocker",
|
||||||
"type": "malware"
|
"type": "malware"
|
||||||
}"""
|
}"""
|
||||||
|
@ -109,8 +109,8 @@ def test_invalid_kwarg_to_malware():
|
||||||
{
|
{
|
||||||
"type": "malware",
|
"type": "malware",
|
||||||
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
"id": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||||
"created": "2016-05-12T08:17:27Z",
|
"created": "2016-05-12T08:17:27.000Z",
|
||||||
"modified": "2016-05-12T08:17:27Z",
|
"modified": "2016-05-12T08:17:27.000Z",
|
||||||
"labels": ["ransomware"],
|
"labels": ["ransomware"],
|
||||||
"name": "Cryptolocker",
|
"name": "Cryptolocker",
|
||||||
},
|
},
|
||||||
|
|
|
@ -40,7 +40,7 @@ EXPECTED_GRANULAR_MARKING = """{
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
EXPECTED_CAMPAIGN_WITH_GRANULAR_MARKINGS = """{
|
EXPECTED_CAMPAIGN_WITH_GRANULAR_MARKINGS = """{
|
||||||
"created": "2016-04-06T20:03:00Z",
|
"created": "2016-04-06T20:03:00.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
|
||||||
"granular_markings": [
|
"granular_markings": [
|
||||||
|
@ -52,7 +52,7 @@ EXPECTED_CAMPAIGN_WITH_GRANULAR_MARKINGS = """{
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"modified": "2016-04-06T20:03:00Z",
|
"modified": "2016-04-06T20:03:00.000Z",
|
||||||
"name": "Green Group Attacks Against Finance",
|
"name": "Green Group Attacks Against Finance",
|
||||||
"type": "campaign"
|
"type": "campaign"
|
||||||
}"""
|
}"""
|
||||||
|
|
|
@ -10,12 +10,12 @@ from .constants import OBSERVED_DATA_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-04-06T19:58:16Z",
|
"created": "2016-04-06T19:58:16.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"first_observed": "2015-12-21T19:00:00Z",
|
"first_observed": "2015-12-21T19:00:00Z",
|
||||||
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
"last_observed": "2015-12-21T19:00:00Z",
|
"last_observed": "2015-12-21T19:00:00Z",
|
||||||
"modified": "2016-04-06T19:58:16Z",
|
"modified": "2016-04-06T19:58:16.000Z",
|
||||||
"number_observed": 50,
|
"number_observed": 50,
|
||||||
"objects": {
|
"objects": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -31,8 +31,8 @@ def test_observed_data_example():
|
||||||
observed_data = stix2.ObservedData(
|
observed_data = stix2.ObservedData(
|
||||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T19:58:16Z",
|
created="2016-04-06T19:58:16.000Z",
|
||||||
modified="2016-04-06T19:58:16Z",
|
modified="2016-04-06T19:58:16.000Z",
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=50,
|
number_observed=50,
|
||||||
|
@ -48,12 +48,12 @@ def test_observed_data_example():
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_WITH_REF = """{
|
EXPECTED_WITH_REF = """{
|
||||||
"created": "2016-04-06T19:58:16Z",
|
"created": "2016-04-06T19:58:16.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"first_observed": "2015-12-21T19:00:00Z",
|
"first_observed": "2015-12-21T19:00:00Z",
|
||||||
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
"last_observed": "2015-12-21T19:00:00Z",
|
"last_observed": "2015-12-21T19:00:00Z",
|
||||||
"modified": "2016-04-06T19:58:16Z",
|
"modified": "2016-04-06T19:58:16.000Z",
|
||||||
"number_observed": 50,
|
"number_observed": 50,
|
||||||
"objects": {
|
"objects": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -76,8 +76,8 @@ def test_observed_data_example_with_refs():
|
||||||
observed_data = stix2.ObservedData(
|
observed_data = stix2.ObservedData(
|
||||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T19:58:16Z",
|
created="2016-04-06T19:58:16.000Z",
|
||||||
modified="2016-04-06T19:58:16Z",
|
modified="2016-04-06T19:58:16.000Z",
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=50,
|
number_observed=50,
|
||||||
|
@ -102,8 +102,8 @@ def test_observed_data_example_with_bad_refs():
|
||||||
stix2.ObservedData(
|
stix2.ObservedData(
|
||||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T19:58:16Z",
|
created="2016-04-06T19:58:16.000Z",
|
||||||
modified="2016-04-06T19:58:16Z",
|
modified="2016-04-06T19:58:16.000Z",
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=50,
|
number_observed=50,
|
||||||
|
@ -130,11 +130,11 @@ def test_observed_data_example_with_bad_refs():
|
||||||
{
|
{
|
||||||
"type": "observed-data",
|
"type": "observed-data",
|
||||||
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
"created": "2016-04-06T19:58:16Z",
|
"created": "2016-04-06T19:58:16.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"first_observed": "2015-12-21T19:00:00Z",
|
"first_observed": "2015-12-21T19:00:00Z",
|
||||||
"last_observed": "2015-12-21T19:00:00Z",
|
"last_observed": "2015-12-21T19:00:00Z",
|
||||||
"modified": "2016-04-06T19:58:16Z",
|
"modified": "2016-04-06T19:58:16.000Z",
|
||||||
"number_observed": 50,
|
"number_observed": 50,
|
||||||
"objects": {
|
"objects": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -466,12 +466,12 @@ def test_parse_basic_tcp_traffic_with_error(data):
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_PROCESS_OD = """{
|
EXPECTED_PROCESS_OD = """{
|
||||||
"created": "2016-04-06T19:58:16Z",
|
"created": "2016-04-06T19:58:16.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"first_observed": "2015-12-21T19:00:00Z",
|
"first_observed": "2015-12-21T19:00:00Z",
|
||||||
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
"last_observed": "2015-12-21T19:00:00Z",
|
"last_observed": "2015-12-21T19:00:00Z",
|
||||||
"modified": "2016-04-06T19:58:16Z",
|
"modified": "2016-04-06T19:58:16.000Z",
|
||||||
"number_observed": 50,
|
"number_observed": 50,
|
||||||
"objects": {
|
"objects": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -499,8 +499,8 @@ def test_observed_data_with_process_example():
|
||||||
observed_data = stix2.ObservedData(
|
observed_data = stix2.ObservedData(
|
||||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T19:58:16Z",
|
created="2016-04-06T19:58:16.000Z",
|
||||||
modified="2016-04-06T19:58:16Z",
|
modified="2016-04-06T19:58:16.000Z",
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=50,
|
number_observed=50,
|
||||||
|
|
|
@ -10,9 +10,9 @@ from .constants import (FAKE_TIME, INDICATOR_ID, MALWARE_ID, RELATIONSHIP_ID,
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_RELATIONSHIP = """{
|
EXPECTED_RELATIONSHIP = """{
|
||||||
"created": "2016-04-06T20:06:37Z",
|
"created": "2016-04-06T20:06:37.000Z",
|
||||||
"id": "relationship--00000000-1111-2222-3333-444444444444",
|
"id": "relationship--00000000-1111-2222-3333-444444444444",
|
||||||
"modified": "2016-04-06T20:06:37Z",
|
"modified": "2016-04-06T20:06:37.000Z",
|
||||||
"relationship_type": "indicates",
|
"relationship_type": "indicates",
|
||||||
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210",
|
||||||
|
|
|
@ -9,14 +9,14 @@ from .constants import INDICATOR_KWARGS, REPORT_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2015-12-21T19:59:11Z",
|
"created": "2015-12-21T19:59:11.000Z",
|
||||||
"created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
"created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
||||||
"description": "A simple report with an indicator and campaign",
|
"description": "A simple report with an indicator and campaign",
|
||||||
"id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
"id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
||||||
"labels": [
|
"labels": [
|
||||||
"campaign"
|
"campaign"
|
||||||
],
|
],
|
||||||
"modified": "2015-12-21T19:59:11Z",
|
"modified": "2015-12-21T19:59:11.000Z",
|
||||||
"name": "The Black Vine Cyberespionage Group",
|
"name": "The Black Vine Cyberespionage Group",
|
||||||
"object_refs": [
|
"object_refs": [
|
||||||
"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
|
"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
|
||||||
|
@ -32,8 +32,8 @@ def test_report_example():
|
||||||
report = stix2.Report(
|
report = stix2.Report(
|
||||||
id="report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
id="report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
||||||
created_by_ref="identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
created_by_ref="identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
||||||
created="2015-12-21T19:59:11Z",
|
created="2015-12-21T19:59:11.000Z",
|
||||||
modified="2015-12-21T19:59:11Z",
|
modified="2015-12-21T19:59:11.000Z",
|
||||||
name="The Black Vine Cyberespionage Group",
|
name="The Black Vine Cyberespionage Group",
|
||||||
description="A simple report with an indicator and campaign",
|
description="A simple report with an indicator and campaign",
|
||||||
published="2016-01-20T17:00:00Z",
|
published="2016-01-20T17:00:00Z",
|
||||||
|
@ -95,14 +95,14 @@ def test_report_example_objects_in_object_refs_with_bad_id():
|
||||||
@pytest.mark.parametrize("data", [
|
@pytest.mark.parametrize("data", [
|
||||||
EXPECTED,
|
EXPECTED,
|
||||||
{
|
{
|
||||||
"created": "2015-12-21T19:59:11Z",
|
"created": "2015-12-21T19:59:11.000Z",
|
||||||
"created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
"created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
|
||||||
"description": "A simple report with an indicator and campaign",
|
"description": "A simple report with an indicator and campaign",
|
||||||
"id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
"id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
|
||||||
"labels": [
|
"labels": [
|
||||||
"campaign"
|
"campaign"
|
||||||
],
|
],
|
||||||
"modified": "2015-12-21T19:59:11Z",
|
"modified": "2015-12-21T19:59:11.000Z",
|
||||||
"name": "The Black Vine Cyberespionage Group",
|
"name": "The Black Vine Cyberespionage Group",
|
||||||
"object_refs": [
|
"object_refs": [
|
||||||
"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
|
"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
|
||||||
|
|
|
@ -9,9 +9,9 @@ from .constants import INDICATOR_ID, SIGHTING_ID, SIGHTING_KWARGS
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_SIGHTING = """{
|
EXPECTED_SIGHTING = """{
|
||||||
"created": "2016-04-06T20:06:37Z",
|
"created": "2016-04-06T20:06:37.000Z",
|
||||||
"id": "sighting--bfbc19db-ec35-4e45-beed-f8bde2a772fb",
|
"id": "sighting--bfbc19db-ec35-4e45-beed-f8bde2a772fb",
|
||||||
"modified": "2016-04-06T20:06:37Z",
|
"modified": "2016-04-06T20:06:37.000Z",
|
||||||
"sighting_of_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
"sighting_of_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
"type": "sighting",
|
"type": "sighting",
|
||||||
"where_sighted_refs": [
|
"where_sighted_refs": [
|
||||||
|
@ -20,9 +20,9 @@ EXPECTED_SIGHTING = """{
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
BAD_SIGHTING = """{
|
BAD_SIGHTING = """{
|
||||||
"created": "2016-04-06T20:06:37Z",
|
"created": "2016-04-06T20:06:37.000Z",
|
||||||
"id": "sighting--bfbc19db-ec35-4e45-beed-f8bde2a772fb",
|
"id": "sighting--bfbc19db-ec35-4e45-beed-f8bde2a772fb",
|
||||||
"modified": "2016-04-06T20:06:37Z",
|
"modified": "2016-04-06T20:06:37.000Z",
|
||||||
"sighting_of_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
"sighting_of_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
"type": "sighting",
|
"type": "sighting",
|
||||||
"where_sighted_refs": [
|
"where_sighted_refs": [
|
||||||
|
|
|
@ -9,14 +9,14 @@ from .constants import THREAT_ACTOR_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "The Evil Org threat actor group",
|
"description": "The Evil Org threat actor group",
|
||||||
"id": "threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"labels": [
|
"labels": [
|
||||||
"crime-syndicate"
|
"crime-syndicate"
|
||||||
],
|
],
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Evil Org",
|
"name": "Evil Org",
|
||||||
"type": "threat-actor"
|
"type": "threat-actor"
|
||||||
}"""
|
}"""
|
||||||
|
@ -26,8 +26,8 @@ def test_threat_actor_example():
|
||||||
threat_actor = stix2.ThreatActor(
|
threat_actor = stix2.ThreatActor(
|
||||||
id="threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
id="threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T20:03:48Z",
|
created="2016-04-06T20:03:48.000Z",
|
||||||
modified="2016-04-06T20:03:48Z",
|
modified="2016-04-06T20:03:48.000Z",
|
||||||
name="Evil Org",
|
name="Evil Org",
|
||||||
description="The Evil Org threat actor group",
|
description="The Evil Org threat actor group",
|
||||||
labels=["crime-syndicate"],
|
labels=["crime-syndicate"],
|
||||||
|
@ -39,14 +39,14 @@ def test_threat_actor_example():
|
||||||
@pytest.mark.parametrize("data", [
|
@pytest.mark.parametrize("data", [
|
||||||
EXPECTED,
|
EXPECTED,
|
||||||
{
|
{
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"description": "The Evil Org threat actor group",
|
"description": "The Evil Org threat actor group",
|
||||||
"id": "threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"labels": [
|
"labels": [
|
||||||
"crime-syndicate"
|
"crime-syndicate"
|
||||||
],
|
],
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "Evil Org",
|
"name": "Evil Org",
|
||||||
"type": "threat-actor"
|
"type": "threat-actor"
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,13 +9,13 @@ from .constants import TOOL_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-04-06T20:03:48Z",
|
"created": "2016-04-06T20:03:48.000Z",
|
||||||
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
"id": "tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
"id": "tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
"labels": [
|
"labels": [
|
||||||
"remote-access"
|
"remote-access"
|
||||||
],
|
],
|
||||||
"modified": "2016-04-06T20:03:48Z",
|
"modified": "2016-04-06T20:03:48.000Z",
|
||||||
"name": "VNC",
|
"name": "VNC",
|
||||||
"type": "tool"
|
"type": "tool"
|
||||||
}"""
|
}"""
|
||||||
|
@ -25,8 +25,8 @@ def test_tool_example():
|
||||||
tool = stix2.Tool(
|
tool = stix2.Tool(
|
||||||
id="tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
id="tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
|
||||||
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
|
||||||
created="2016-04-06T20:03:48Z",
|
created="2016-04-06T20:03:48.000Z",
|
||||||
modified="2016-04-06T20:03:48Z",
|
modified="2016-04-06T20:03:48.000Z",
|
||||||
name="VNC",
|
name="VNC",
|
||||||
labels=["remote-access"],
|
labels=["remote-access"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .constants import VULNERABILITY_ID
|
||||||
|
|
||||||
|
|
||||||
EXPECTED = """{
|
EXPECTED = """{
|
||||||
"created": "2016-05-12T08:17:27Z",
|
"created": "2016-05-12T08:17:27.000Z",
|
||||||
"external_references": [
|
"external_references": [
|
||||||
{
|
{
|
||||||
"external_id": "CVE-2016-1234",
|
"external_id": "CVE-2016-1234",
|
||||||
|
@ -17,7 +17,7 @@ EXPECTED = """{
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
"id": "vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
||||||
"modified": "2016-05-12T08:17:27Z",
|
"modified": "2016-05-12T08:17:27.000Z",
|
||||||
"name": "CVE-2016-1234",
|
"name": "CVE-2016-1234",
|
||||||
"type": "vulnerability"
|
"type": "vulnerability"
|
||||||
}"""
|
}"""
|
||||||
|
@ -26,8 +26,8 @@ EXPECTED = """{
|
||||||
def test_vulnerability_example():
|
def test_vulnerability_example():
|
||||||
vulnerability = stix2.Vulnerability(
|
vulnerability = stix2.Vulnerability(
|
||||||
id="vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
id="vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
|
||||||
created="2016-05-12T08:17:27Z",
|
created="2016-05-12T08:17:27.000Z",
|
||||||
modified="2016-05-12T08:17:27Z",
|
modified="2016-05-12T08:17:27.000Z",
|
||||||
name="CVE-2016-1234",
|
name="CVE-2016-1234",
|
||||||
external_references=[
|
external_references=[
|
||||||
stix2.ExternalReference(source_name='cve',
|
stix2.ExternalReference(source_name='cve',
|
||||||
|
|
|
@ -12,15 +12,34 @@ import pytz
|
||||||
NOW = object()
|
NOW = object()
|
||||||
|
|
||||||
|
|
||||||
|
class STIXdatetime(dt.datetime):
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
precision = kwargs.pop('precision', None)
|
||||||
|
if isinstance(args[0], dt.datetime): # Allow passing in a datetime object
|
||||||
|
dttm = args[0]
|
||||||
|
args = (dttm.year,)
|
||||||
|
kwargs['month'] = dttm.month
|
||||||
|
kwargs['day'] = dttm.day
|
||||||
|
kwargs['hour'] = dttm.hour
|
||||||
|
kwargs['minute'] = dttm.minute
|
||||||
|
kwargs['second'] = dttm.second
|
||||||
|
kwargs['microsecond'] = dttm.microsecond
|
||||||
|
kwargs['tzinfo'] = dttm.tzinfo
|
||||||
|
self = dt.datetime.__new__(cls, *args, **kwargs)
|
||||||
|
self.precision = precision
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp():
|
def get_timestamp():
|
||||||
return dt.datetime.now(tz=pytz.UTC)
|
return STIXdatetime.now(tz=pytz.UTC)
|
||||||
|
|
||||||
|
|
||||||
def format_datetime(dttm):
|
def format_datetime(dttm):
|
||||||
# 1. Convert to timezone-aware
|
# 1. Convert to timezone-aware
|
||||||
# 2. Convert to UTC
|
# 2. Convert to UTC
|
||||||
# 3. Format in ISO format
|
# 3. Format in ISO format
|
||||||
# 4. Add subsecond value if non-zero
|
# 4. Ensure correct precision
|
||||||
|
# 4a. Add subsecond value if non-zero and precision not defined
|
||||||
# 5. Add "Z"
|
# 5. Add "Z"
|
||||||
|
|
||||||
if dttm.tzinfo is None or dttm.tzinfo.utcoffset(dttm) is None:
|
if dttm.tzinfo is None or dttm.tzinfo.utcoffset(dttm) is None:
|
||||||
|
@ -29,20 +48,24 @@ def format_datetime(dttm):
|
||||||
else:
|
else:
|
||||||
zoned = dttm.astimezone(pytz.utc)
|
zoned = dttm.astimezone(pytz.utc)
|
||||||
ts = zoned.strftime("%Y-%m-%dT%H:%M:%S")
|
ts = zoned.strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
if zoned.microsecond > 0:
|
|
||||||
ms = zoned.strftime("%f")
|
ms = zoned.strftime("%f")
|
||||||
|
precision = getattr(dttm, "precision", None)
|
||||||
|
if precision:
|
||||||
|
if precision == "millisecond":
|
||||||
|
ts = ts + '.' + ms[:3]
|
||||||
|
elif zoned.microsecond > 0:
|
||||||
ts = ts + '.' + ms.rstrip("0")
|
ts = ts + '.' + ms.rstrip("0")
|
||||||
return ts + "Z"
|
return ts + "Z"
|
||||||
|
|
||||||
|
|
||||||
def parse_into_datetime(value):
|
def parse_into_datetime(value, precision=None):
|
||||||
if isinstance(value, dt.date):
|
if isinstance(value, dt.date):
|
||||||
if hasattr(value, 'hour'):
|
if hasattr(value, 'hour'):
|
||||||
return value
|
ts = value
|
||||||
else:
|
else:
|
||||||
# Add a time component
|
# Add a time component
|
||||||
return dt.datetime.combine(value, dt.time(0, 0, tzinfo=pytz.utc))
|
ts = dt.datetime.combine(value, dt.time(0, 0, tzinfo=pytz.utc))
|
||||||
|
else:
|
||||||
# value isn't a date or datetime object so assume it's a string
|
# value isn't a date or datetime object so assume it's a string
|
||||||
try:
|
try:
|
||||||
parsed = parser.parse(value)
|
parsed = parser.parse(value)
|
||||||
|
@ -51,10 +74,21 @@ def parse_into_datetime(value):
|
||||||
raise ValueError("must be a datetime object, date object, or "
|
raise ValueError("must be a datetime object, date object, or "
|
||||||
"timestamp string in a recognizable format.")
|
"timestamp string in a recognizable format.")
|
||||||
if parsed.tzinfo:
|
if parsed.tzinfo:
|
||||||
return parsed.astimezone(pytz.utc)
|
ts = parsed.astimezone(pytz.utc)
|
||||||
else:
|
else:
|
||||||
# Doesn't have timezone info in the string; assume UTC
|
# Doesn't have timezone info in the string; assume UTC
|
||||||
return pytz.utc.localize(parsed)
|
ts = pytz.utc.localize(parsed)
|
||||||
|
|
||||||
|
# Ensure correct precision
|
||||||
|
if not precision:
|
||||||
|
return ts
|
||||||
|
ms = ts.microsecond
|
||||||
|
if precision == 'millisecond':
|
||||||
|
ms_len = len(str(ms))
|
||||||
|
if ms_len > 3:
|
||||||
|
# Truncate to millisecond precision
|
||||||
|
return ts.replace(microsecond=(ts.microsecond // (10 ** (ms_len - 3))))
|
||||||
|
return STIXdatetime(ts, precision=precision)
|
||||||
|
|
||||||
|
|
||||||
def get_dict(data):
|
def get_dict(data):
|
||||||
|
|
Loading…
Reference in New Issue