commit
37e9049536
|
@ -0,0 +1,3 @@
|
||||||
|
include LICENSE
|
||||||
|
include CHANGELOG
|
||||||
|
recursive-exclude stix2\test *
|
10
README.rst
10
README.rst
|
@ -62,6 +62,16 @@ To parse a STIX JSON string into a Python STIX object, use ``parse()``:
|
||||||
|
|
||||||
For more in-depth documentation, please see `https://stix2.readthedocs.io/ <https://stix2.readthedocs.io/>`__.
|
For more in-depth documentation, please see `https://stix2.readthedocs.io/ <https://stix2.readthedocs.io/>`__.
|
||||||
|
|
||||||
|
STIX 2.X Technical Specification Support
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
This version of python-stix2 supports STIX 2.0 by default. Although, the
|
||||||
|
`stix2` Python library is built to support multiple versions of the STIX
|
||||||
|
Technical Specification. With every major release of stix2 the ``import stix2``
|
||||||
|
statement will automatically load the SDO/SROs equivalent to the most recent
|
||||||
|
supported 2.X Technical Specification. Please see the library documentation
|
||||||
|
for more details.
|
||||||
|
|
||||||
Governance
|
Governance
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Technical Specification Support\n",
|
||||||
|
"\n",
|
||||||
|
"### How imports will work\n",
|
||||||
|
"\n",
|
||||||
|
"Imports can be used in different ways depending on the use case and support levels.\n",
|
||||||
|
"\n",
|
||||||
|
"People who want to (in general) support the latest version of STIX 2.X without making changes, implicitly using the latest version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import stix2\n",
|
||||||
|
"\n",
|
||||||
|
"stix2.Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"or,"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from stix2 import Indicator\n",
|
||||||
|
"\n",
|
||||||
|
"Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"People who want to use an explicit version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import stix2.v20\n",
|
||||||
|
"\n",
|
||||||
|
"stix2.v20.Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"or,"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from stix2.v20 import Indicator\n",
|
||||||
|
"\n",
|
||||||
|
"Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"or even,"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import stix2.v20 as stix2\n",
|
||||||
|
"\n",
|
||||||
|
"stix2.Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"The last option makes it easy to update to a new version in one place per file, once you've made the deliberate action to do this.\n",
|
||||||
|
"\n",
|
||||||
|
"People who want to use multiple versions in a single file:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import stix2\n",
|
||||||
|
"\n",
|
||||||
|
"stix2.v20.Indicator()\n",
|
||||||
|
"\n",
|
||||||
|
"stix2.v21.Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"or,"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from stix2 import v20, v21\n",
|
||||||
|
"\n",
|
||||||
|
"v20.Indicator()\n",
|
||||||
|
"v21.Indicator()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"or (less preferred):"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from stix2.v20 import Indicator as Indicator_v20\n",
|
||||||
|
"from stix2.v21 import Indicator as Indicator_v21\n",
|
||||||
|
"\n",
|
||||||
|
"Indicator_v20()\n",
|
||||||
|
"Indicator_v21()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### How parsing will work\n",
|
||||||
|
"If the ``version`` positional argument is not provided. The data will be parsed using the latest version of STIX 2.X supported by the `stix2` library.\n",
|
||||||
|
"\n",
|
||||||
|
"You can lock your `parse()` method to a specific STIX version by"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from stix2 import parse\n",
|
||||||
|
"\n",
|
||||||
|
"indicator = parse(\"\"\"{\n",
|
||||||
|
" \"type\": \"indicator\",\n",
|
||||||
|
" \"id\": \"indicator--dbcbd659-c927-4f9a-994f-0a2632274394\",\n",
|
||||||
|
" \"created\": \"2017-09-26T23:33:39.829Z\",\n",
|
||||||
|
" \"modified\": \"2017-09-26T23:33:39.829Z\",\n",
|
||||||
|
" \"labels\": [\n",
|
||||||
|
" \"malicious-activity\"\n",
|
||||||
|
" ],\n",
|
||||||
|
" \"name\": \"File hash for malware variant\",\n",
|
||||||
|
" \"pattern\": \"[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']\",\n",
|
||||||
|
" \"valid_from\": \"2017-09-26T23:33:39.829952Z\"\n",
|
||||||
|
"}\"\"\", version=\"2.0\")\n",
|
||||||
|
"print(indicator)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Keep in mind that if a 2.1 or higher object is parsed, the operation will fail."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### How will custom work\n",
|
||||||
|
"\n",
|
||||||
|
"CustomObject, CustomObservable, CustomMarking and CustomExtension must be registered explicitly by STIX version. This is a design decision since properties or requirements may change as the STIX Technical Specification advances.\n",
|
||||||
|
"\n",
|
||||||
|
"You can perform this by,"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import stix2\n",
|
||||||
|
"\n",
|
||||||
|
"# Make my custom observable available in STIX 2.0\n",
|
||||||
|
"@stix2.v20.CustomObservable('x-new-object-type',\n",
|
||||||
|
" ((\"prop\", stix2.properties.BooleanProperty())))\n",
|
||||||
|
"class NewObject2(object):\n",
|
||||||
|
" pass\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"# Make my custom observable available in STIX 2.1\n",
|
||||||
|
"@stix2.v21.CustomObservable('x-new-object-type',\n",
|
||||||
|
" ((\"prop\", stix2.properties.BooleanProperty())))\n",
|
||||||
|
"class NewObject2(object):\n",
|
||||||
|
" pass"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 0
|
||||||
|
}
|
2
setup.py
2
setup.py
|
@ -45,7 +45,7 @@ setup(
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
],
|
],
|
||||||
keywords="stix stix2 json cti cyber threat intelligence",
|
keywords="stix stix2 json cti cyber threat intelligence",
|
||||||
packages=find_packages(),
|
packages=find_packages(exclude=['*.test']),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'python-dateutil',
|
'python-dateutil',
|
||||||
'pytz',
|
'pytz',
|
||||||
|
|
|
@ -19,27 +19,10 @@
|
||||||
|
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
|
||||||
from . import exceptions
|
from .core import Bundle, _collect_stix2_obj_maps, _register_type, parse
|
||||||
from .common import (TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking,
|
|
||||||
ExternalReference, GranularMarking, KillChainPhase,
|
|
||||||
MarkingDefinition, StatementMarking, TLPMarking)
|
|
||||||
from .core import Bundle, _register_type, parse
|
|
||||||
from .environment import Environment, ObjectFactory
|
from .environment import Environment, ObjectFactory
|
||||||
from .markings import (add_markings, clear_markings, get_markings, is_marked,
|
from .markings import (add_markings, clear_markings, get_markings, is_marked,
|
||||||
remove_markings, set_markings)
|
remove_markings, set_markings)
|
||||||
from .observables import (URL, AlternateDataStream, ArchiveExt, Artifact,
|
|
||||||
AutonomousSystem, CustomExtension, CustomObservable,
|
|
||||||
Directory, DomainName, EmailAddress, EmailMessage,
|
|
||||||
EmailMIMEComponent, File, HTTPRequestExt, ICMPExt,
|
|
||||||
IPv4Address, IPv6Address, MACAddress, Mutex,
|
|
||||||
NetworkTraffic, NTFSExt, PDFExt, Process,
|
|
||||||
RasterImageExt, SocketExt, Software, TCPExt,
|
|
||||||
UNIXAccountExt, UserAccount, WindowsPEBinaryExt,
|
|
||||||
WindowsPEOptionalHeaderType, WindowsPESection,
|
|
||||||
WindowsProcessExt, WindowsRegistryKey,
|
|
||||||
WindowsRegistryValueType, WindowsServiceExt,
|
|
||||||
X509Certificate, X509V3ExtenstionsType,
|
|
||||||
parse_observable)
|
|
||||||
from .patterns import (AndBooleanExpression, AndObservationExpression,
|
from .patterns import (AndBooleanExpression, AndObservationExpression,
|
||||||
BasicObjectPathComponent, EqualityComparisonExpression,
|
BasicObjectPathComponent, EqualityComparisonExpression,
|
||||||
FloatConstant, FollowedByObservationExpression,
|
FloatConstant, FollowedByObservationExpression,
|
||||||
|
@ -58,9 +41,6 @@ from .patterns import (AndBooleanExpression, AndObservationExpression,
|
||||||
ReferenceObjectPathComponent, RepeatQualifier,
|
ReferenceObjectPathComponent, RepeatQualifier,
|
||||||
StartStopQualifier, StringConstant, TimestampConstant,
|
StartStopQualifier, StringConstant, TimestampConstant,
|
||||||
WithinQualifier)
|
WithinQualifier)
|
||||||
from .sdo import (AttackPattern, Campaign, CourseOfAction, CustomObject,
|
|
||||||
Identity, Indicator, IntrusionSet, Malware, ObservedData,
|
|
||||||
Report, ThreatActor, Tool, Vulnerability)
|
|
||||||
from .sources import CompositeDataSource
|
from .sources import CompositeDataSource
|
||||||
from .sources.filesystem import (FileSystemSink, FileSystemSource,
|
from .sources.filesystem import (FileSystemSink, FileSystemSource,
|
||||||
FileSystemStore)
|
FileSystemStore)
|
||||||
|
@ -68,6 +48,10 @@ from .sources.filters import Filter
|
||||||
from .sources.memory import MemorySink, MemorySource, MemoryStore
|
from .sources.memory import MemorySink, MemorySource, MemoryStore
|
||||||
from .sources.taxii import (TAXIICollectionSink, TAXIICollectionSource,
|
from .sources.taxii import (TAXIICollectionSink, TAXIICollectionSource,
|
||||||
TAXIICollectionStore)
|
TAXIICollectionStore)
|
||||||
from .sro import Relationship, Sighting
|
|
||||||
from .utils import get_dict, new_version, revoke
|
from .utils import get_dict, new_version, revoke
|
||||||
|
from .v20 import * # This import will always be the latest STIX 2.X version
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
|
|
||||||
|
_collect_stix2_obj_maps()
|
||||||
|
|
||||||
|
DEFAULT_VERSION = "2.0" # Default version will always be the latest STIX 2.X version
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
"""STIX 2.0 Objects that are neither SDOs nor SROs."""
|
"""STIX 2.0 Objects that are neither SDOs nor SROs."""
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
import importlib
|
||||||
|
import pkgutil
|
||||||
|
|
||||||
|
import stix2
|
||||||
|
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
from .base import _STIXBase
|
from .base import _STIXBase
|
||||||
from .common import MarkingDefinition
|
|
||||||
from .properties import IDProperty, ListProperty, Property, TypeProperty
|
from .properties import IDProperty, ListProperty, Property, TypeProperty
|
||||||
from .sdo import (AttackPattern, Campaign, CourseOfAction, Identity, Indicator,
|
from .utils import get_class_hierarchy_names, get_dict
|
||||||
IntrusionSet, Malware, ObservedData, Report,
|
|
||||||
STIXDomainObject, ThreatActor, Tool, Vulnerability)
|
|
||||||
from .sro import Relationship, Sighting, STIXRelationshipObject
|
|
||||||
from .utils import get_dict
|
|
||||||
|
|
||||||
|
|
||||||
class STIXObjectProperty(Property):
|
class STIXObjectProperty(Property):
|
||||||
|
@ -22,8 +21,8 @@ class STIXObjectProperty(Property):
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to
|
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to
|
||||||
# a bundle with no further checks.
|
# a bundle with no further checks.
|
||||||
if isinstance(value, (STIXDomainObject, STIXRelationshipObject,
|
if any(x in ('STIXDomainObject', 'STIXRelationshipObject', 'MarkingDefinition')
|
||||||
MarkingDefinition)):
|
for x in get_class_hierarchy_names(value)):
|
||||||
return value
|
return value
|
||||||
try:
|
try:
|
||||||
dictified = get_dict(value)
|
dictified = get_dict(value)
|
||||||
|
@ -67,37 +66,30 @@ class Bundle(_STIXBase):
|
||||||
super(Bundle, self).__init__(**kwargs)
|
super(Bundle, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
OBJ_MAP = {
|
STIX2_OBJ_MAPS = {}
|
||||||
'attack-pattern': AttackPattern,
|
|
||||||
'bundle': Bundle,
|
|
||||||
'campaign': Campaign,
|
|
||||||
'course-of-action': CourseOfAction,
|
|
||||||
'identity': Identity,
|
|
||||||
'indicator': Indicator,
|
|
||||||
'intrusion-set': IntrusionSet,
|
|
||||||
'malware': Malware,
|
|
||||||
'marking-definition': MarkingDefinition,
|
|
||||||
'observed-data': ObservedData,
|
|
||||||
'report': Report,
|
|
||||||
'relationship': Relationship,
|
|
||||||
'threat-actor': ThreatActor,
|
|
||||||
'tool': Tool,
|
|
||||||
'sighting': Sighting,
|
|
||||||
'vulnerability': Vulnerability,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def parse(data, allow_custom=False):
|
def parse(data, allow_custom=False, version=None):
|
||||||
"""Deserialize a string or file-like object into a STIX object.
|
"""Deserialize a string or file-like object into a STIX object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (str, dict, file-like object): The STIX 2 content to be parsed.
|
data (str, dict, file-like object): The STIX 2 content to be parsed.
|
||||||
allow_custom (bool): Whether to allow custom properties or not. Default: False.
|
allow_custom (bool): Whether to allow custom properties or not.
|
||||||
|
Default: False.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An instantiated Python STIX object.
|
An instantiated Python STIX object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if not version:
|
||||||
|
# Use latest version
|
||||||
|
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||||
|
else:
|
||||||
|
v = 'v' + version.replace('.', '')
|
||||||
|
|
||||||
|
OBJ_MAP = STIX2_OBJ_MAPS[v]
|
||||||
obj = get_dict(data)
|
obj = get_dict(data)
|
||||||
|
|
||||||
if 'type' not in obj:
|
if 'type' not in obj:
|
||||||
|
@ -110,8 +102,34 @@ def parse(data, allow_custom=False):
|
||||||
return obj_class(allow_custom=allow_custom, **obj)
|
return obj_class(allow_custom=allow_custom, **obj)
|
||||||
|
|
||||||
|
|
||||||
def _register_type(new_type):
|
def _register_type(new_type, version=None):
|
||||||
"""Register a custom STIX Object type.
|
"""Register a custom STIX Object type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_type (class): A class to register in the Object map.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
"""
|
"""
|
||||||
|
if not version:
|
||||||
|
# Use latest version
|
||||||
|
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||||
|
else:
|
||||||
|
v = 'v' + version.replace('.', '')
|
||||||
|
|
||||||
|
OBJ_MAP = STIX2_OBJ_MAPS[v]
|
||||||
OBJ_MAP[new_type._type] = new_type
|
OBJ_MAP[new_type._type] = new_type
|
||||||
|
|
||||||
|
|
||||||
|
def _collect_stix2_obj_maps():
|
||||||
|
"""Navigate the package once and retrieve all OBJ_MAP dicts for each v2X
|
||||||
|
package."""
|
||||||
|
if not STIX2_OBJ_MAPS:
|
||||||
|
top_level_module = importlib.import_module('stix2')
|
||||||
|
path = top_level_module.__path__
|
||||||
|
prefix = str(top_level_module.__name__) + '.'
|
||||||
|
|
||||||
|
for module_loader, name, is_pkg in pkgutil.walk_packages(path=path,
|
||||||
|
prefix=prefix):
|
||||||
|
if name.startswith('stix2.v2') and is_pkg:
|
||||||
|
mod = importlib.import_module(name, str(top_level_module.__name__))
|
||||||
|
STIX2_OBJ_MAPS[name.split('.')[-1]] = mod.OBJ_MAP
|
||||||
|
|
|
@ -8,13 +8,10 @@ TODO:
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from stix2.common import MarkingDefinition
|
|
||||||
from stix2.core import Bundle, parse
|
from stix2.core import Bundle, parse
|
||||||
from stix2.sdo import STIXDomainObject
|
|
||||||
from stix2.sources import DataSink, DataSource, DataStore
|
from stix2.sources import DataSink, DataSource, DataStore
|
||||||
from stix2.sources.filters import Filter, apply_common_filters
|
from stix2.sources.filters import Filter, apply_common_filters
|
||||||
from stix2.sro import STIXRelationshipObject
|
from stix2.utils import deduplicate, get_class_hierarchy_names
|
||||||
from stix2.utils import deduplicate
|
|
||||||
|
|
||||||
|
|
||||||
class FileSystemStore(DataStore):
|
class FileSystemStore(DataStore):
|
||||||
|
@ -78,7 +75,7 @@ class FileSystemSink(DataSink):
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write(str(stix_obj))
|
f.write(str(stix_obj))
|
||||||
|
|
||||||
def add(self, stix_data=None, allow_custom=False):
|
def add(self, stix_data=None, allow_custom=False, version=None):
|
||||||
"""Add STIX objects to file directory.
|
"""Add STIX objects to file directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -87,6 +84,8 @@ class FileSystemSink(DataSink):
|
||||||
json encoded string.
|
json encoded string.
|
||||||
allow_custom (bool): whether to allow custom objects/properties or
|
allow_custom (bool): whether to allow custom objects/properties or
|
||||||
not. Default: False.
|
not. Default: False.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
``stix_data`` can be a Bundle object, but each object in it will be
|
``stix_data`` can be a Bundle object, but each object in it will be
|
||||||
|
@ -94,12 +93,13 @@ class FileSystemSink(DataSink):
|
||||||
the Bundle contained, but not the Bundle itself.
|
the Bundle contained, but not the Bundle itself.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(stix_data, (STIXDomainObject, STIXRelationshipObject, MarkingDefinition)):
|
if any(x in ('STIXDomainObject', 'STIXRelationshipObject', 'MarkingDefinition')
|
||||||
|
for x in get_class_hierarchy_names(stix_data)):
|
||||||
# adding python STIX object
|
# adding python STIX object
|
||||||
self._check_path_and_write(stix_data)
|
self._check_path_and_write(stix_data)
|
||||||
|
|
||||||
elif isinstance(stix_data, (str, dict)):
|
elif isinstance(stix_data, (str, dict)):
|
||||||
stix_data = parse(stix_data, allow_custom)
|
stix_data = parse(stix_data, allow_custom, version)
|
||||||
if stix_data["type"] == "bundle":
|
if stix_data["type"] == "bundle":
|
||||||
# extract STIX objects
|
# extract STIX objects
|
||||||
for stix_obj in stix_data.get("objects", []):
|
for stix_obj in stix_data.get("objects", []):
|
||||||
|
@ -146,15 +146,17 @@ class FileSystemSource(DataSource):
|
||||||
def stix_dir(self):
|
def stix_dir(self):
|
||||||
return self._stix_dir
|
return self._stix_dir
|
||||||
|
|
||||||
def get(self, stix_id, _composite_filters=None, allow_custom=False):
|
def get(self, stix_id, _composite_filters=None, allow_custom=False, version=None):
|
||||||
"""Retrieve STIX object from file directory via STIX ID.
|
"""Retrieve STIX object from file directory via STIX ID.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
stix_id (str): The STIX ID of the STIX object to be retrieved.
|
stix_id (str): The STIX ID of the STIX object to be retrieved.
|
||||||
composite_filters (set): set of filters passed from the parent
|
_composite_filters (set): set of filters passed from the parent
|
||||||
CompositeDataSource, not user supplied
|
CompositeDataSource, not user supplied
|
||||||
allow_custom (bool): whether to retrieve custom objects/properties
|
allow_custom (bool): whether to retrieve custom objects/properties
|
||||||
or not. Default: False.
|
or not. Default: False.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(STIX object): STIX object that has the supplied STIX ID.
|
(STIX object): STIX object that has the supplied STIX ID.
|
||||||
|
@ -164,7 +166,8 @@ class FileSystemSource(DataSource):
|
||||||
"""
|
"""
|
||||||
query = [Filter("id", "=", stix_id)]
|
query = [Filter("id", "=", stix_id)]
|
||||||
|
|
||||||
all_data = self.query(query=query, _composite_filters=_composite_filters, allow_custom=allow_custom)
|
all_data = self.query(query=query, _composite_filters=_composite_filters,
|
||||||
|
allow_custom=allow_custom, version=version)
|
||||||
|
|
||||||
if all_data:
|
if all_data:
|
||||||
stix_obj = sorted(all_data, key=lambda k: k['modified'])[0]
|
stix_obj = sorted(all_data, key=lambda k: k['modified'])[0]
|
||||||
|
@ -173,7 +176,7 @@ class FileSystemSource(DataSource):
|
||||||
|
|
||||||
return stix_obj
|
return stix_obj
|
||||||
|
|
||||||
def all_versions(self, stix_id, _composite_filters=None, allow_custom=False):
|
def all_versions(self, stix_id, _composite_filters=None, allow_custom=False, version=None):
|
||||||
"""Retrieve STIX object from file directory via STIX ID, all versions.
|
"""Retrieve STIX object from file directory via STIX ID, all versions.
|
||||||
|
|
||||||
Note: Since FileSystem sources/sinks don't handle multiple versions
|
Note: Since FileSystem sources/sinks don't handle multiple versions
|
||||||
|
@ -181,10 +184,12 @@ class FileSystemSource(DataSource):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
stix_id (str): The STIX ID of the STIX objects to be retrieved.
|
stix_id (str): The STIX ID of the STIX objects to be retrieved.
|
||||||
composite_filters (set): set of filters passed from the parent
|
_composite_filters (set): set of filters passed from the parent
|
||||||
CompositeDataSource, not user supplied
|
CompositeDataSource, not user supplied
|
||||||
allow_custom (bool): whether to retrieve custom objects/properties
|
allow_custom (bool): whether to retrieve custom objects/properties
|
||||||
or not. Default: False.
|
or not. Default: False.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(list): of STIX objects that has the supplied STIX ID.
|
(list): of STIX objects that has the supplied STIX ID.
|
||||||
|
@ -192,9 +197,10 @@ class FileSystemSource(DataSource):
|
||||||
a python STIX objects and then returned
|
a python STIX objects and then returned
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return [self.get(stix_id=stix_id, _composite_filters=_composite_filters, allow_custom=allow_custom)]
|
return [self.get(stix_id=stix_id, _composite_filters=_composite_filters,
|
||||||
|
allow_custom=allow_custom, version=version)]
|
||||||
|
|
||||||
def query(self, query=None, _composite_filters=None, allow_custom=False):
|
def query(self, query=None, _composite_filters=None, allow_custom=False, version=None):
|
||||||
"""Search and retrieve STIX objects based on the complete query.
|
"""Search and retrieve STIX objects based on the complete query.
|
||||||
|
|
||||||
A "complete query" includes the filters from the query, the filters
|
A "complete query" includes the filters from the query, the filters
|
||||||
|
@ -203,10 +209,12 @@ class FileSystemSource(DataSource):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
query (list): list of filters to search on
|
query (list): list of filters to search on
|
||||||
composite_filters (set): set of filters passed from the
|
_composite_filters (set): set of filters passed from the
|
||||||
CompositeDataSource, not user supplied
|
CompositeDataSource, not user supplied
|
||||||
allow_custom (bool): whether to retrieve custom objects/properties
|
allow_custom (bool): whether to retrieve custom objects/properties
|
||||||
or not. Default: False.
|
or not. Default: False.
|
||||||
|
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||||
|
None, use latest version.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(list): list of STIX objects that matches the supplied
|
(list): list of STIX objects that matches the supplied
|
||||||
|
@ -297,7 +305,7 @@ class FileSystemSource(DataSource):
|
||||||
all_data = deduplicate(all_data)
|
all_data = deduplicate(all_data)
|
||||||
|
|
||||||
# parse python STIX objects from the STIX object dicts
|
# parse python STIX objects from the STIX object dicts
|
||||||
stix_objs = [parse(stix_obj_dict, allow_custom) for stix_obj_dict in all_data]
|
stix_objs = [parse(stix_obj_dict, allow_custom, version) for stix_obj_dict in all_data]
|
||||||
|
|
||||||
return stix_objs
|
return stix_objs
|
||||||
|
|
||||||
|
|
|
@ -132,8 +132,9 @@ def test_create_bundle_invalid(indicator, malware, relationship):
|
||||||
assert excinfo.value.reason == 'This property may not contain a Bundle object'
|
assert excinfo.value.reason == 'This property may not contain a Bundle object'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_bundle():
|
@pytest.mark.parametrize("version", ["2.0"])
|
||||||
bundle = stix2.parse(EXPECTED_BUNDLE)
|
def test_parse_bundle(version):
|
||||||
|
bundle = stix2.parse(EXPECTED_BUNDLE, version=version)
|
||||||
|
|
||||||
assert bundle.type == "bundle"
|
assert bundle.type == "bundle"
|
||||||
assert bundle.id.startswith("bundle--")
|
assert bundle.id.startswith("bundle--")
|
||||||
|
|
|
@ -484,3 +484,13 @@ def test_parse_observable_with_unregistered_custom_extension():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.parse_observable(input_str)
|
stix2.parse_observable(input_str)
|
||||||
assert "Can't parse Unknown extension type" in str(excinfo.value)
|
assert "Can't parse Unknown extension type" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_custom_object():
|
||||||
|
# Not the way to register custom object.
|
||||||
|
class CustomObject2(object):
|
||||||
|
_type = 'awesome-object'
|
||||||
|
|
||||||
|
stix2._register_type(CustomObject2)
|
||||||
|
# Note that we will always check against newest OBJ_MAP.
|
||||||
|
assert (CustomObject2._type, CustomObject2) in stix2.OBJ_MAP.items()
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from stix2 import TCPExt
|
from stix2 import EmailMIMEComponent, ExtensionsProperty, TCPExt
|
||||||
from stix2.exceptions import AtLeastOnePropertyError, DictionaryKeyError
|
from stix2.exceptions import AtLeastOnePropertyError, DictionaryKeyError
|
||||||
from stix2.observables import EmailMIMEComponent, ExtensionsProperty
|
|
||||||
from stix2.properties import (BinaryProperty, BooleanProperty,
|
from stix2.properties import (BinaryProperty, BooleanProperty,
|
||||||
DictionaryProperty, EmbeddedObjectProperty,
|
DictionaryProperty, EmbeddedObjectProperty,
|
||||||
EnumProperty, FloatProperty, HashesProperty,
|
EnumProperty, FloatProperty, HashesProperty,
|
||||||
|
|
|
@ -247,3 +247,11 @@ def revoke(data):
|
||||||
if data.get("revoked"):
|
if data.get("revoked"):
|
||||||
raise RevokeError("revoke")
|
raise RevokeError("revoke")
|
||||||
return new_version(data, revoked=True)
|
return new_version(data, revoked=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_class_hierarchy_names(obj):
|
||||||
|
"""Given an object, return the names of the class hierarchy."""
|
||||||
|
names = []
|
||||||
|
for cls in obj.__class__.__mro__:
|
||||||
|
names.append(cls.__name__)
|
||||||
|
return names
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
from ..core import Bundle
|
||||||
|
from .common import (TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking,
|
||||||
|
ExternalReference, GranularMarking, KillChainPhase,
|
||||||
|
MarkingDefinition, StatementMarking, TLPMarking)
|
||||||
|
from .observables import (URL, AlternateDataStream, ArchiveExt, Artifact,
|
||||||
|
AutonomousSystem, CustomExtension, CustomObservable,
|
||||||
|
Directory, DomainName, EmailAddress, EmailMessage,
|
||||||
|
EmailMIMEComponent, ExtensionsProperty, File,
|
||||||
|
HTTPRequestExt, ICMPExt, IPv4Address, IPv6Address,
|
||||||
|
MACAddress, Mutex, NetworkTraffic, NTFSExt, PDFExt,
|
||||||
|
Process, RasterImageExt, SocketExt, Software, TCPExt,
|
||||||
|
UNIXAccountExt, UserAccount, WindowsPEBinaryExt,
|
||||||
|
WindowsPEOptionalHeaderType, WindowsPESection,
|
||||||
|
WindowsProcessExt, WindowsRegistryKey,
|
||||||
|
WindowsRegistryValueType, WindowsServiceExt,
|
||||||
|
X509Certificate, X509V3ExtenstionsType,
|
||||||
|
parse_observable)
|
||||||
|
from .sdo import (AttackPattern, Campaign, CourseOfAction, CustomObject,
|
||||||
|
Identity, Indicator, IntrusionSet, Malware, ObservedData,
|
||||||
|
Report, ThreatActor, Tool, Vulnerability)
|
||||||
|
from .sro import Relationship, Sighting
|
||||||
|
|
||||||
|
OBJ_MAP = {
|
||||||
|
'attack-pattern': AttackPattern,
|
||||||
|
'bundle': Bundle,
|
||||||
|
'campaign': Campaign,
|
||||||
|
'course-of-action': CourseOfAction,
|
||||||
|
'identity': Identity,
|
||||||
|
'indicator': Indicator,
|
||||||
|
'intrusion-set': IntrusionSet,
|
||||||
|
'malware': Malware,
|
||||||
|
'marking-definition': MarkingDefinition,
|
||||||
|
'observed-data': ObservedData,
|
||||||
|
'report': Report,
|
||||||
|
'relationship': Relationship,
|
||||||
|
'threat-actor': ThreatActor,
|
||||||
|
'tool': Tool,
|
||||||
|
'sighting': Sighting,
|
||||||
|
'vulnerability': Vulnerability,
|
||||||
|
}
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from .base import _STIXBase
|
from ..base import _STIXBase
|
||||||
from .markings import _MarkingsMixin
|
from ..markings import _MarkingsMixin
|
||||||
from .properties import (HashesProperty, IDProperty, ListProperty, Property,
|
from ..properties import (HashesProperty, IDProperty, ListProperty, Property,
|
||||||
ReferenceProperty, SelectorProperty, StringProperty,
|
ReferenceProperty, SelectorProperty, StringProperty,
|
||||||
TimestampProperty, TypeProperty)
|
TimestampProperty, TypeProperty)
|
||||||
from .utils import NOW, get_dict
|
from ..utils import NOW, get_dict
|
||||||
|
|
||||||
|
|
||||||
class ExternalReference(_STIXBase):
|
class ExternalReference(_STIXBase):
|
|
@ -7,15 +7,15 @@ Observable and do not have a ``_type`` attribute.
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from .base import _Extension, _Observable, _STIXBase
|
from ..base import _Extension, _Observable, _STIXBase
|
||||||
from .exceptions import (AtLeastOnePropertyError, DependentPropertiesError,
|
from ..exceptions import (AtLeastOnePropertyError, DependentPropertiesError,
|
||||||
ParseError)
|
ParseError)
|
||||||
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
|
||||||
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
EmbeddedObjectProperty, EnumProperty, FloatProperty,
|
||||||
HashesProperty, HexProperty, IntegerProperty,
|
HashesProperty, HexProperty, IntegerProperty,
|
||||||
ListProperty, ObjectReferenceProperty, Property,
|
ListProperty, ObjectReferenceProperty, Property,
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
StringProperty, TimestampProperty, TypeProperty)
|
||||||
from .utils import get_dict
|
from ..utils import get_dict
|
||||||
|
|
||||||
|
|
||||||
class ObservableProperty(Property):
|
class ObservableProperty(Property):
|
|
@ -4,14 +4,14 @@ from collections import OrderedDict
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
|
|
||||||
from .base import _STIXBase
|
from ..base import _STIXBase
|
||||||
|
from ..markings import _MarkingsMixin
|
||||||
|
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
|
||||||
|
ListProperty, PatternProperty, ReferenceProperty,
|
||||||
|
StringProperty, TimestampProperty, TypeProperty)
|
||||||
|
from ..utils import NOW
|
||||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||||
from .markings import _MarkingsMixin
|
|
||||||
from .observables import ObservableProperty
|
from .observables import ObservableProperty
|
||||||
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
|
|
||||||
ListProperty, PatternProperty, ReferenceProperty,
|
|
||||||
StringProperty, TimestampProperty, TypeProperty)
|
|
||||||
from .utils import NOW
|
|
||||||
|
|
||||||
|
|
||||||
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
||||||
|
@ -358,7 +358,7 @@ def CustomObject(type='x-custom-type', properties=None):
|
||||||
return
|
return
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
stix2._register_type(_Custom)
|
stix2._register_type(_Custom, version="2.0")
|
||||||
return _Custom
|
return _Custom
|
||||||
|
|
||||||
return custom_builder
|
return custom_builder
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from .base import _STIXBase
|
from ..base import _STIXBase
|
||||||
|
from ..markings import _MarkingsMixin
|
||||||
|
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
|
||||||
|
ListProperty, ReferenceProperty, StringProperty,
|
||||||
|
TimestampProperty, TypeProperty)
|
||||||
|
from ..utils import NOW
|
||||||
from .common import ExternalReference, GranularMarking
|
from .common import ExternalReference, GranularMarking
|
||||||
from .markings import _MarkingsMixin
|
|
||||||
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
|
|
||||||
ListProperty, ReferenceProperty, StringProperty,
|
|
||||||
TimestampProperty, TypeProperty)
|
|
||||||
from .utils import NOW
|
|
||||||
|
|
||||||
|
|
||||||
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
Loading…
Reference in New Issue