In [1]:
# Delete this cell to re-enable tracebacks
import sys
ipython = get_ipython()

def hide_traceback(exc_tuple=None, filename=None, tb_offset=None,
                   exception_only=False, running_compiled_code=False):
    etype, value, tb = sys.exc_info()
    value.__cause__ = None  # suppress chained exceptions
    return ipython._showtraceback(etype, value, ipython.InteractiveTB.get_exception_only(etype, value))

ipython.showtraceback = hide_traceback

In [2]:
# JSON output syntax highlighting
from __future__ import print_function
from pygments import highlight
from pygments.lexers import JsonLexer, TextLexer
from pygments.formatters import HtmlFormatter
from IPython.display import display, HTML
from IPython.core.interactiveshell import InteractiveShell

InteractiveShell.ast_node_interactivity = "all"

def json_print(inpt):
    string = str(inpt)
    formatter = HtmlFormatter()
    if string[0] == '{':
        lexer = JsonLexer()
    else:
        lexer = TextLexer()
    return HTML('<style type="text/css">{}</style>{}'.format(
                formatter.get_style_defs('.highlight'),
                highlight(string, lexer, formatter)))

globals()['print'] = json_print

Using Environments

An Environment object makes it easier to use STIX 2 content as part of a larger application or ecosystem. It allows you to abstract away the nasty details of sending and receiving STIX data, and to create STIX objects with default values for common properties.

Storing and Retrieving STIX Content

An Environment can be set up with a DataStore if you want to store and retrieve STIX content from the same place.


In [3]:
from stix2 import Environment, MemoryStore

env = Environment(store=MemoryStore())

If desired, you can instead set up an Environment with different data sources and sinks. In the following example we set up an environment that retrieves objects from memory and a directory on the filesystem, and stores objects in a different directory on the filesystem.


In [6]:
from stix2 import CompositeDataSource, FileSystemSink, FileSystemSource, MemorySource

src = CompositeDataSource()
src.add_data_sources([MemorySource(), FileSystemSource("/tmp/stix2_source")])
env2 = Environment(source=src,
                   sink=FileSystemSink("/tmp/stix2_sink"))

Once you have an Environment you can store some STIX content in its DataSinks with add():


In [7]:
from stix2 import Indicator

indicator = Indicator(id="indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
                      pattern_type="stix",
                      pattern="[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']")
env.add(indicator)

You can retrieve STIX objects from the DataSources in the Environment with get(), query(), all_versions(), creator_of(), related_to(), and relationships() just as you would for a DataSource.


In [8]:
print(env.get("indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7"))


Out[8]:
{
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
    "created": "2020-06-26T14:46:08.384618Z",
    "modified": "2020-06-26T14:46:08.384618Z",
    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "valid_from": "2020-06-26T14:46:08.384618Z"
}

Creating STIX Objects With Defaults

To create STIX objects with default values for certain properties, use an ObjectFactory. For instance, say we want all objects we create to have a created_by_ref property pointing to the Identity object representing our organization.


In [9]:
from stix2 import Indicator, ObjectFactory

factory = ObjectFactory(created_by_ref="identity--311b2d2d-f010-4473-83ec-1edf84858f4c")

Once you've set up the ObjectFactory, use its create() method, passing in the class for the type of object you wish to create, followed by the other properties and their values for the object.


In [10]:
ind = factory.create(Indicator,
                     pattern_type="stix",
                     pattern="[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']")
print(ind)


Out[10]:
{
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--4db1493b-8822-4b1c-a471-1c1cdc53ec6d",
    "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
    "created": "2020-06-26T14:46:36.666866Z",
    "modified": "2020-06-26T14:46:36.666866Z",
    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "valid_from": "2020-06-26T14:46:36.666866Z"
}

All objects we create with that ObjectFactory will automatically get the default value for created_by_ref. These are the properties for which defaults can be set:

  • created_by_ref
  • created
  • external_references
  • object_marking_refs

These defaults can be bypassed. For example, say you have an Environment with multiple default values but want to create an object with a different value for created_by_ref, or none at all.


In [11]:
factory2 = ObjectFactory(created_by_ref="identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
                         created="2017-09-25T18:07:46.255472Z")
env2 = Environment(factory=factory2)

ind2 = env2.create(Indicator,
                   created_by_ref=None,
                   pattern_type="stix",
                   pattern="[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']")
print(ind2)


Out[11]:
{
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--e7e92c87-df40-4ffb-a6da-9667b0acddb1",
    "created": "2017-09-25T18:07:46.255472Z",
    "modified": "2017-09-25T18:07:46.255472Z",
    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "valid_from": "2020-06-26T14:47:58.470047Z"
}

In [12]:
ind3 = env2.create(Indicator,
                       created_by_ref="identity--962cabe5-f7f3-438a-9169-585a8c971d12",
                       pattern_type="stix",
                       pattern="[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']")
print(ind3)


Out[12]:
{
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--40540b9b-47a7-4855-81a3-b6d3ff6f8592",
    "created_by_ref": "identity--962cabe5-f7f3-438a-9169-585a8c971d12",
    "created": "2017-09-25T18:07:46.255472Z",
    "modified": "2017-09-25T18:07:46.255472Z",
    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "valid_from": "2020-06-26T14:48:11.028904Z"
}

For the full power of the Environment layer, create an Environment with both a DataStore/Source/Sink and an ObjectFactory:


In [13]:
environ = Environment(ObjectFactory(created_by_ref="identity--311b2d2d-f010-4473-83ec-1edf84858f4c"),
                      MemoryStore())

i = environ.create(Indicator,
                   pattern_type="stix",
                   pattern="[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']")
environ.add(i)
print(environ.get(i.id))


Out[13]:
{
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--3ab656d1-e549-4a6e-a2df-e84ff515fcd3",
    "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c",
    "created": "2020-06-26T14:48:20.238719Z",
    "modified": "2020-06-26T14:48:20.238719Z",
    "pattern": "[file:hashes.md5 = 'd41d8cd98f00b204e9800998ecf8427e']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "valid_from": "2020-06-26T14:48:20.238719Z"
}