2019-07-24 21:35:59 +02:00
|
|
|
import importlib
|
2018-07-03 13:00:18 +02:00
|
|
|
import os
|
|
|
|
|
|
|
|
import stix2
|
2018-07-13 17:10:05 +02:00
|
|
|
from stix2.workbench import (
|
2019-07-24 23:39:00 +02:00
|
|
|
_STIX_VID, AttackPattern, Bundle, Campaign, CourseOfAction,
|
|
|
|
ExternalReference, File, FileSystemSource, Filter, Identity, Indicator,
|
|
|
|
IntrusionSet, Malware, MarkingDefinition, NTFSExt, ObservedData,
|
|
|
|
Relationship, Report, StatementMarking, ThreatActor, Tool, Vulnerability,
|
|
|
|
add_data_source, all_versions, attack_patterns, campaigns,
|
|
|
|
courses_of_action, create, get, identities, indicators, intrusion_sets,
|
|
|
|
malware, observed_data, query, reports, save, set_default_created,
|
|
|
|
set_default_creator, set_default_external_refs,
|
2018-07-13 17:10:05 +02:00
|
|
|
set_default_object_marking_refs, threat_actors, tools, vulnerabilities,
|
|
|
|
)
|
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
# Auto-detect some settings based on the current default STIX version
|
|
|
|
_STIX_DATA_PATH = os.path.join(
|
|
|
|
os.path.dirname(os.path.realpath(__file__)),
|
|
|
|
_STIX_VID,
|
2019-07-24 22:30:18 +02:00
|
|
|
"stix2_data",
|
2018-07-13 17:10:05 +02:00
|
|
|
)
|
2019-07-24 21:35:59 +02:00
|
|
|
_STIX_CONSTANTS_MODULE = "stix2.test." + _STIX_VID + ".constants"
|
|
|
|
|
|
|
|
|
|
|
|
constants = importlib.import_module(_STIX_CONSTANTS_MODULE)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_environment():
|
|
|
|
|
|
|
|
# Create a STIX object
|
2019-07-24 21:35:59 +02:00
|
|
|
ind = create(
|
|
|
|
Indicator, id=constants.INDICATOR_ID, **constants.INDICATOR_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(ind)
|
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
resp = get(constants.INDICATOR_ID)
|
2019-07-22 22:55:22 +02:00
|
|
|
assert resp['labels'][0] == 'malicious-activity'
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
resp = all_versions(constants.INDICATOR_ID)
|
2018-07-03 13:00:18 +02:00
|
|
|
assert len(resp) == 1
|
|
|
|
|
|
|
|
# Search on something other than id
|
|
|
|
q = [Filter('type', '=', 'vulnerability')]
|
|
|
|
resp = query(q)
|
|
|
|
assert len(resp) == 0
|
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_attack_patterns():
|
2019-07-24 21:35:59 +02:00
|
|
|
mal = AttackPattern(
|
|
|
|
id=constants.ATTACK_PATTERN_ID, **constants.ATTACK_PATTERN_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(mal)
|
|
|
|
|
|
|
|
resp = attack_patterns()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.ATTACK_PATTERN_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_campaigns():
|
2019-07-24 21:35:59 +02:00
|
|
|
cam = Campaign(id=constants.CAMPAIGN_ID, **constants.CAMPAIGN_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(cam)
|
|
|
|
|
|
|
|
resp = campaigns()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.CAMPAIGN_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_courses_of_action():
|
2019-07-24 21:35:59 +02:00
|
|
|
coa = CourseOfAction(
|
|
|
|
id=constants.COURSE_OF_ACTION_ID, **constants.COURSE_OF_ACTION_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(coa)
|
|
|
|
|
|
|
|
resp = courses_of_action()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.COURSE_OF_ACTION_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_identities():
|
2019-07-24 21:35:59 +02:00
|
|
|
idty = Identity(id=constants.IDENTITY_ID, **constants.IDENTITY_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(idty)
|
|
|
|
|
|
|
|
resp = identities()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.IDENTITY_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_indicators():
|
|
|
|
resp = indicators()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.INDICATOR_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_intrusion_sets():
|
2019-07-24 21:35:59 +02:00
|
|
|
ins = IntrusionSet(
|
|
|
|
id=constants.INTRUSION_SET_ID, **constants.INTRUSION_SET_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(ins)
|
|
|
|
|
|
|
|
resp = intrusion_sets()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.INTRUSION_SET_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_malware():
|
2019-07-24 21:35:59 +02:00
|
|
|
mal = Malware(id=constants.MALWARE_ID, **constants.MALWARE_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(mal)
|
|
|
|
|
|
|
|
resp = malware()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.MALWARE_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_observed_data():
|
2019-07-24 21:35:59 +02:00
|
|
|
od = ObservedData(
|
|
|
|
id=constants.OBSERVED_DATA_ID, **constants.OBSERVED_DATA_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(od)
|
|
|
|
|
|
|
|
resp = observed_data()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.OBSERVED_DATA_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_reports():
|
2019-07-24 21:35:59 +02:00
|
|
|
rep = Report(id=constants.REPORT_ID, **constants.REPORT_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(rep)
|
|
|
|
|
|
|
|
resp = reports()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.REPORT_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_threat_actors():
|
2019-07-24 21:35:59 +02:00
|
|
|
thr = ThreatActor(
|
|
|
|
id=constants.THREAT_ACTOR_ID, **constants.THREAT_ACTOR_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(thr)
|
|
|
|
|
|
|
|
resp = threat_actors()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.THREAT_ACTOR_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_tools():
|
2019-07-24 21:35:59 +02:00
|
|
|
tool = Tool(id=constants.TOOL_ID, **constants.TOOL_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(tool)
|
|
|
|
|
|
|
|
resp = tools()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.TOOL_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_get_all_vulnerabilities():
|
2019-07-24 21:35:59 +02:00
|
|
|
vuln = Vulnerability(
|
|
|
|
id=constants.VULNERABILITY_ID, **constants.VULNERABILITY_KWARGS
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(vuln)
|
|
|
|
|
|
|
|
resp = vulnerabilities()
|
|
|
|
assert len(resp) == 1
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].id == constants.VULNERABILITY_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_add_to_bundle():
|
2019-07-24 21:35:59 +02:00
|
|
|
vuln = Vulnerability(**constants.VULNERABILITY_KWARGS)
|
2019-07-24 23:39:00 +02:00
|
|
|
bundle = Bundle(vuln)
|
2018-07-03 13:00:18 +02:00
|
|
|
assert bundle.objects[0].name == 'Heartbleed'
|
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_relationships():
|
2019-07-24 21:35:59 +02:00
|
|
|
rel = Relationship(
|
2019-07-24 22:30:18 +02:00
|
|
|
constants.INDICATOR_ID, 'indicates', constants.MALWARE_ID,
|
2019-07-24 21:35:59 +02:00
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(rel)
|
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
ind = get(constants.INDICATOR_ID)
|
2018-07-03 13:00:18 +02:00
|
|
|
resp = ind.relationships()
|
|
|
|
assert len(resp) == 1
|
|
|
|
assert resp[0].relationship_type == 'indicates'
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].source_ref == constants.INDICATOR_ID
|
|
|
|
assert resp[0].target_ref == constants.MALWARE_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_created_by():
|
2019-07-24 21:35:59 +02:00
|
|
|
intset = IntrusionSet(
|
2019-07-24 22:30:18 +02:00
|
|
|
name="Breach 123", created_by_ref=constants.IDENTITY_ID,
|
2019-07-24 21:35:59 +02:00
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
save(intset)
|
|
|
|
creator = intset.created_by()
|
2019-07-24 21:35:59 +02:00
|
|
|
assert creator.id == constants.IDENTITY_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_related():
|
2019-07-24 21:35:59 +02:00
|
|
|
rel1 = Relationship(constants.MALWARE_ID, 'targets', constants.IDENTITY_ID)
|
|
|
|
rel2 = Relationship(constants.CAMPAIGN_ID, 'uses', constants.MALWARE_ID)
|
2018-07-03 13:00:18 +02:00
|
|
|
save([rel1, rel2])
|
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
resp = get(constants.MALWARE_ID).related()
|
2018-07-03 13:00:18 +02:00
|
|
|
assert len(resp) == 3
|
2019-07-24 21:35:59 +02:00
|
|
|
assert any(x['id'] == constants.CAMPAIGN_ID for x in resp)
|
|
|
|
assert any(x['id'] == constants.INDICATOR_ID for x in resp)
|
|
|
|
assert any(x['id'] == constants.IDENTITY_ID for x in resp)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
resp = get(constants.MALWARE_ID).related(relationship_type='indicates')
|
2018-07-03 13:00:18 +02:00
|
|
|
assert len(resp) == 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_related_with_filters():
|
2019-07-24 21:35:59 +02:00
|
|
|
malware = Malware(
|
2019-07-24 22:30:18 +02:00
|
|
|
labels=["ransomware"], name="CryptorBit", created_by_ref=constants.IDENTITY_ID,
|
2019-07-24 21:35:59 +02:00
|
|
|
)
|
|
|
|
rel = Relationship(malware.id, 'variant-of', constants.MALWARE_ID)
|
2018-07-03 13:00:18 +02:00
|
|
|
save([malware, rel])
|
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
filters = [Filter('created_by_ref', '=', constants.IDENTITY_ID)]
|
|
|
|
resp = get(constants.MALWARE_ID).related(filters=filters)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert len(resp) == 1
|
|
|
|
assert resp[0].name == malware.name
|
2019-07-24 21:35:59 +02:00
|
|
|
assert resp[0].created_by_ref == constants.IDENTITY_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
# filters arg can also be single filter
|
2019-07-24 21:35:59 +02:00
|
|
|
resp = get(constants.MALWARE_ID).related(filters=filters[0])
|
2018-07-03 13:00:18 +02:00
|
|
|
assert len(resp) == 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_data_source():
|
2019-07-24 21:35:59 +02:00
|
|
|
fs = FileSystemSource(_STIX_DATA_PATH)
|
2018-07-03 13:00:18 +02:00
|
|
|
add_data_source(fs)
|
|
|
|
|
|
|
|
resp = tools()
|
|
|
|
assert len(resp) == 3
|
|
|
|
resp_ids = [tool.id for tool in resp]
|
2019-07-24 21:35:59 +02:00
|
|
|
assert constants.TOOL_ID in resp_ids
|
2018-07-03 13:00:18 +02:00
|
|
|
assert 'tool--03342581-f790-4f03-ba41-e82e67392e23' in resp_ids
|
|
|
|
assert 'tool--242f3da3-4425-4d11-8f5c-b842886da966' in resp_ids
|
|
|
|
|
|
|
|
|
|
|
|
def test_additional_filter():
|
|
|
|
resp = tools(Filter('created_by_ref', '=', 'identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5'))
|
|
|
|
assert len(resp) == 2
|
|
|
|
|
|
|
|
|
|
|
|
def test_additional_filters_list():
|
2018-07-13 17:10:05 +02:00
|
|
|
resp = tools([
|
|
|
|
Filter('created_by_ref', '=', 'identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5'),
|
|
|
|
Filter('name', '=', 'Windows Credential Editor'),
|
|
|
|
])
|
2018-07-03 13:00:18 +02:00
|
|
|
assert len(resp) == 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_default_creator():
|
2019-07-24 21:35:59 +02:00
|
|
|
set_default_creator(constants.IDENTITY_ID)
|
|
|
|
campaign = Campaign(**constants.CAMPAIGN_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
assert 'created_by_ref' not in constants.CAMPAIGN_KWARGS
|
|
|
|
assert campaign.created_by_ref == constants.IDENTITY_ID
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2019-07-24 22:23:19 +02:00
|
|
|
# turn off side-effects to avoid affecting future tests
|
|
|
|
set_default_creator(None)
|
|
|
|
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
def test_default_created_timestamp():
|
|
|
|
timestamp = "2018-03-19T01:02:03.000Z"
|
|
|
|
set_default_created(timestamp)
|
2019-07-24 21:35:59 +02:00
|
|
|
campaign = Campaign(**constants.CAMPAIGN_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
2019-07-24 21:35:59 +02:00
|
|
|
assert 'created' not in constants.CAMPAIGN_KWARGS
|
2018-07-03 13:00:18 +02:00
|
|
|
assert stix2.utils.format_datetime(campaign.created) == timestamp
|
|
|
|
assert stix2.utils.format_datetime(campaign.modified) == timestamp
|
|
|
|
|
2019-07-24 22:23:19 +02:00
|
|
|
# turn off side-effects to avoid affecting future tests
|
|
|
|
set_default_created(None)
|
|
|
|
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
def test_default_external_refs():
|
2018-07-13 17:10:05 +02:00
|
|
|
ext_ref = ExternalReference(
|
|
|
|
source_name="ACME Threat Intel",
|
|
|
|
description="Threat report",
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
set_default_external_refs(ext_ref)
|
2019-07-24 21:35:59 +02:00
|
|
|
campaign = Campaign(**constants.CAMPAIGN_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert campaign.external_references[0].source_name == "ACME Threat Intel"
|
|
|
|
assert campaign.external_references[0].description == "Threat report"
|
|
|
|
|
2019-07-24 22:23:19 +02:00
|
|
|
# turn off side-effects to avoid affecting future tests
|
|
|
|
set_default_external_refs([])
|
|
|
|
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
def test_default_object_marking_refs():
|
|
|
|
stmt_marking = StatementMarking("Copyright 2016, Example Corp")
|
2018-07-13 17:10:05 +02:00
|
|
|
mark_def = MarkingDefinition(
|
|
|
|
definition_type="statement",
|
|
|
|
definition=stmt_marking,
|
|
|
|
)
|
2018-07-03 13:00:18 +02:00
|
|
|
set_default_object_marking_refs(mark_def)
|
2019-07-24 21:35:59 +02:00
|
|
|
campaign = Campaign(**constants.CAMPAIGN_KWARGS)
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
assert campaign.object_marking_refs[0] == mark_def.id
|
|
|
|
|
2019-07-24 22:23:19 +02:00
|
|
|
# turn off side-effects to avoid affecting future tests
|
|
|
|
set_default_object_marking_refs([])
|
|
|
|
|
2018-07-03 13:00:18 +02:00
|
|
|
|
|
|
|
def test_workbench_custom_property_object_in_observable_extension():
|
2019-07-24 23:39:00 +02:00
|
|
|
ntfs = NTFSExt(
|
2018-07-03 13:00:18 +02:00
|
|
|
allow_custom=True,
|
|
|
|
sid=1,
|
|
|
|
x_foo='bar',
|
|
|
|
)
|
2019-07-24 23:39:00 +02:00
|
|
|
artifact = File(
|
2018-07-03 13:00:18 +02:00
|
|
|
name='test',
|
|
|
|
extensions={'ntfs-ext': ntfs},
|
|
|
|
)
|
|
|
|
observed_data = ObservedData(
|
|
|
|
allow_custom=True,
|
|
|
|
first_observed="2015-12-21T19:00:00Z",
|
|
|
|
last_observed="2015-12-21T19:00:00Z",
|
2018-10-15 20:48:52 +02:00
|
|
|
number_observed=1,
|
2018-07-03 13:00:18 +02:00
|
|
|
objects={"0": artifact},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert observed_data.objects['0'].extensions['ntfs-ext'].x_foo == "bar"
|
|
|
|
assert '"x_foo": "bar"' in str(observed_data)
|
|
|
|
|
|
|
|
|
|
|
|
def test_workbench_custom_property_dict_in_observable_extension():
|
2019-07-24 23:39:00 +02:00
|
|
|
artifact = File(
|
2018-07-03 13:00:18 +02:00
|
|
|
allow_custom=True,
|
|
|
|
name='test',
|
|
|
|
extensions={
|
|
|
|
'ntfs-ext': {
|
|
|
|
'allow_custom': True,
|
|
|
|
'sid': 1,
|
|
|
|
'x_foo': 'bar',
|
2018-07-13 17:10:05 +02:00
|
|
|
},
|
2018-07-03 13:00:18 +02:00
|
|
|
},
|
|
|
|
)
|
|
|
|
observed_data = ObservedData(
|
|
|
|
allow_custom=True,
|
|
|
|
first_observed="2015-12-21T19:00:00Z",
|
|
|
|
last_observed="2015-12-21T19:00:00Z",
|
2018-10-15 20:48:52 +02:00
|
|
|
number_observed=1,
|
2018-07-03 13:00:18 +02:00
|
|
|
objects={"0": artifact},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert observed_data.objects['0'].extensions['ntfs-ext'].x_foo == "bar"
|
|
|
|
assert '"x_foo": "bar"' in str(observed_data)
|