Add TimestampProperty

stix2.1
clenk 2017-04-11 12:10:55 -04:00
parent 168105603b
commit 2e3dfe5d84
15 changed files with 93 additions and 46 deletions

View File

@ -4,6 +4,7 @@ from setuptools import setup, find_packages
install_requires = [
'pytz',
'six',
'python-dateutil',
]
setup(

View File

@ -2,13 +2,13 @@
from .base import _STIXBase
from .properties import (Property, BooleanProperty, ReferenceProperty,
StringProperty)
StringProperty, TimestampProperty)
from .utils import NOW
COMMON_PROPERTIES = {
# 'type' and 'id' should be defined on each individual type
'created': Property(default=lambda: NOW),
'modified': Property(default=lambda: NOW),
'created': TimestampProperty(default=lambda: NOW),
'modified': TimestampProperty(default=lambda: NOW),
'external_references': Property(),
'revoked': BooleanProperty(),
'created_by_ref': ReferenceProperty(),

View File

@ -1,6 +1,9 @@
import re
import uuid
from six import PY2
import datetime as dt
import pytz
from dateutil import parser
class Property(object):
@ -194,6 +197,30 @@ class BooleanProperty(Property):
raise ValueError("must be a boolean value.")
class TimestampProperty(Property):
def validate(self, value):
if isinstance(value, dt.datetime):
return value
elif isinstance(value, dt.date):
return dt.datetime.combine(value, dt.time())
try:
return parser.parse(value).astimezone(pytz.utc)
except ValueError:
# Doesn't have timezone info in the string
try:
return pytz.utc.localize(parser.parse(value))
except TypeError:
# Unknown format
raise ValueError("must be a datetime object, date object, or "
"timestamp string in a recognizable format.")
except TypeError:
# Isn't a string
raise ValueError("must be a datetime object, date object, or "
"timestamp string.")
REF_REGEX = re.compile("^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}"
"-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")

View File

@ -1,7 +1,7 @@
import stix2
EXPECTED = """{
"created": "2016-05-12T08:17:27.000Z",
"created": "2016-05-12T08:17:27Z",
"description": "...",
"external_references": [
{
@ -10,7 +10,7 @@ EXPECTED = """{
}
],
"id": "attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
"modified": "2016-05-12T08:17:27.000Z",
"modified": "2016-05-12T08:17:27Z",
"name": "Spear Phishing",
"type": "attack-pattern"
}"""
@ -19,8 +19,8 @@ EXPECTED = """{
def test_attack_pattern_example():
ap = stix2.AttackPattern(
id="attack-pattern--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
created="2016-05-12T08:17:27.000Z",
modified="2016-05-12T08:17:27.000Z",
created="2016-05-12T08:17:27Z",
modified="2016-05-12T08:17:27Z",
name="Spear Phishing",
external_references=[{
"source_name": "capec",

View File

@ -1,11 +1,11 @@
import stix2
EXPECTED = """{
"created": "2016-04-06T20:03:00.000Z",
"created": "2016-04-06T20:03:00Z",
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
"description": "Campaign by Green Group against a series of targets in the financial services sector.",
"id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"modified": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00Z",
"name": "Green Group Attacks Against Finance",
"type": "campaign"
}"""
@ -15,8 +15,8 @@ def test_campaign_example():
campaign = stix2.Campaign(
id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T20:03:00.000Z",
modified="2016-04-06T20:03:00.000Z",
created="2016-04-06T20:03:00Z",
modified="2016-04-06T20:03:00Z",
name="Green Group Attacks Against Finance",
description="Campaign by Green Group against a series of targets in the financial services sector."
)

View File

@ -1,11 +1,11 @@
import stix2
EXPECTED = """{
"created": "2016-04-06T20:03:48.000Z",
"created": "2016-04-06T20:03:48Z",
"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 ...",
"id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"modified": "2016-04-06T20:03:48.000Z",
"modified": "2016-04-06T20:03:48Z",
"name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
"type": "course-of-action"
}"""
@ -15,8 +15,8 @@ def test_course_of_action_example():
coa = stix2.CourseOfAction(
id="course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T20:03:48.000Z",
modified="2016-04-06T20:03:48.000Z",
created="2016-04-06T20:03:48Z",
modified="2016-04-06T20:03:48Z",
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 ..."
)

View File

@ -1,10 +1,10 @@
import stix2
EXPECTED = """{
"created": "2015-12-21T19:59:11.000Z",
"created": "2015-12-21T19:59:11Z",
"id": "identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
"identity_class": "individual",
"modified": "2015-12-21T19:59:11.000Z",
"modified": "2015-12-21T19:59:11Z",
"name": "John Smith",
"type": "identity"
}"""
@ -13,8 +13,8 @@ EXPECTED = """{
def test_identity_example():
report = stix2.Identity(
id="identity--311b2d2d-f010-5473-83ec-1edf84858f4c",
created="2015-12-21T19:59:11.000Z",
modified="2015-12-21T19:59:11.000Z",
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
)

View File

@ -4,7 +4,7 @@ EXPECTED = """{
"aliases": [
"Zookeeper"
],
"created": "2016-04-06T20:03:48.000Z",
"created": "2016-04-06T20:03:48Z",
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
"description": "Incidents usually feature a shared TTP of a bobcat being released...",
"goals": [
@ -13,7 +13,7 @@ EXPECTED = """{
"damage"
],
"id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
"modified": "2016-04-06T20:03:48.000Z",
"modified": "2016-04-06T20:03:48Z",
"name": "Bobcat Breakin",
"type": "intrusion-set"
}"""
@ -23,8 +23,8 @@ def test_intrusion_set_example():
intrusion_set = stix2.IntrusionSet(
id="intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T20:03:48.000Z",
modified="2016-04-06T20:03:48.000Z",
created="2016-04-06T20:03:48Z",
modified="2016-04-06T20:03:48Z",
name="Bobcat Breakin",
description="Incidents usually feature a shared TTP of a bobcat being released...",
aliases=["Zookeeper"],

View File

@ -107,8 +107,8 @@ def test_parse_malware(data):
assert mal.type == 'malware'
assert mal.id == MALWARE_ID
assert mal.created == "2016-05-12T08:17:27Z"
assert mal.modified == "2016-05-12T08:17:27Z"
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.labels == ['ransomware']
assert mal.name == "Cryptolocker"

View File

@ -1,12 +1,12 @@
import stix2
EXPECTED = """{
"created": "2016-04-06T19:58:16.000Z",
"created": "2016-04-06T19:58:16Z",
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
"first_observed": "2015-12-21T19:00:00Z",
"id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
"last_observed": "2015-12-21T19:00:00Z",
"modified": "2016-04-06T19:58:16.000Z",
"modified": "2016-04-06T19:58:16Z",
"number_observed": 50,
"objects": {
"0": {
@ -21,8 +21,8 @@ def test_observed_data_example():
observed_data = stix2.ObservedData(
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T19:58:16.000Z",
modified="2016-04-06T19:58:16.000Z",
created="2016-04-06T19:58:16Z",
modified="2016-04-06T19:58:16Z",
first_observed="2015-12-21T19:00:00Z",
last_observed="2015-12-21T19:00:00Z",
number_observed=50,

View File

@ -2,7 +2,8 @@ import pytest
from stix2.properties import (Property, BooleanProperty, ListProperty,
StringProperty, TypeProperty, IDProperty,
ReferenceProperty)
ReferenceProperty, TimestampProperty)
from .constants import FAKE_TIME
def test_property():
@ -131,3 +132,21 @@ def test_reference_property():
assert ref_prop.validate("my-type--3a331bfe-0566-55e1-a4a0-9a2cd355a300")
with pytest.raises(ValueError):
ref_prop.validate("foo")
@pytest.mark.parametrize("value", [
'2017-01-01T12:34:56Z',
'2017-01-01 12:34:56',
'Jan 1 2017 12:34:56',
])
def test_timestamp_property_valid(value):
ts_prop = TimestampProperty()
assert ts_prop.validate(value) == FAKE_TIME
def test_timestamp_property_invalid():
ts_prop = TimestampProperty()
with pytest.raises(ValueError):
ts_prop.validate(1)
with pytest.raises(ValueError):
ts_prop.validate("someday sometime")

View File

@ -1,14 +1,14 @@
import stix2
EXPECTED = """{
"created": "2015-12-21T19:59:11.000Z",
"created": "2015-12-21T19:59:11Z",
"created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
"description": "A simple report with an indicator and campaign",
"id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
"labels": [
"campaign"
],
"modified": "2015-12-21T19:59:11.000Z",
"modified": "2015-12-21T19:59:11Z",
"name": "The Black Vine Cyberespionage Group",
"object_refs": [
"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
@ -24,8 +24,8 @@ def test_report_example():
report = stix2.Report(
id="report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
created_by_ref="identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
created="2015-12-21T19:59:11.000Z",
modified="2015-12-21T19:59:11.000Z",
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="The Black Vine Cyberespionage Group",
description="A simple report with an indicator and campaign",
published="2016-01-201T17:00:00Z",

View File

@ -1,14 +1,14 @@
import stix2
EXPECTED = """{
"created": "2016-04-06T20:03:48.000Z",
"created": "2016-04-06T20:03:48Z",
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
"description": "The Evil Org threat actor group",
"id": "threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"labels": [
"crime-syndicate"
],
"modified": "2016-04-06T20:03:48.000Z",
"modified": "2016-04-06T20:03:48Z",
"name": "Evil Org",
"type": "threat-actor"
}"""
@ -18,8 +18,8 @@ def test_threat_actor_example():
threat_actor = stix2.ThreatActor(
id="threat-actor--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T20:03:48.000Z",
modified="2016-04-06T20:03:48.000Z",
created="2016-04-06T20:03:48Z",
modified="2016-04-06T20:03:48Z",
name="Evil Org",
description="The Evil Org threat actor group",
labels=["crime-syndicate"],

View File

@ -1,13 +1,13 @@
import stix2
EXPECTED = """{
"created": "2016-04-06T20:03:48.000Z",
"created": "2016-04-06T20:03:48Z",
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
"id": "tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"labels": [
"remote-access"
],
"modified": "2016-04-06T20:03:48.000Z",
"modified": "2016-04-06T20:03:48Z",
"name": "VNC",
"type": "tool"
}"""
@ -17,8 +17,8 @@ def test_tool_example():
tool = stix2.Tool(
id="tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff",
created="2016-04-06T20:03:48.000Z",
modified="2016-04-06T20:03:48.000Z",
created="2016-04-06T20:03:48Z",
modified="2016-04-06T20:03:48Z",
name="VNC",
labels=["remote-access"],
)

View File

@ -1,7 +1,7 @@
import stix2
EXPECTED = """{
"created": "2016-05-12T08:17:27.000Z",
"created": "2016-05-12T08:17:27Z",
"external_references": [
{
"external_id": "CVE-2016-1234",
@ -9,7 +9,7 @@ EXPECTED = """{
}
],
"id": "vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
"modified": "2016-05-12T08:17:27.000Z",
"modified": "2016-05-12T08:17:27Z",
"name": "CVE-2016-1234",
"type": "vulnerability"
}"""
@ -18,8 +18,8 @@ EXPECTED = """{
def test_vulnerability_example():
vulnerability = stix2.Vulnerability(
id="vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061",
created="2016-05-12T08:17:27.000Z",
modified="2016-05-12T08:17:27.000Z",
created="2016-05-12T08:17:27Z",
modified="2016-05-12T08:17:27Z",
name="CVE-2016-1234",
external_references=[
stix2.ExternalReference(source_name='cve',