Skip to content

Latest commit

 

History

History
350 lines (262 loc) · 12.4 KB

README.Javascript.rst

File metadata and controls

350 lines (262 loc) · 12.4 KB

The STIX Visualizer is written in Javascript (the small bit of Python is for integration into Jupyter). This file documents the Javascript module.

STIX is a graph-like data model. This library is designed to visualize STIX content in the natural way, as a graph. It converts STIX content into a set of nodes and a set of edges.

The library separates the creation of graph data (as converted from STIX content) from how that data is visualized. This allows for different views of the same graph data. If there is a lot of STIX content for example, a visual graph rendering may be too cluttered or may not perform well. The primary rendering of the graph data is intended to be a graph. But alternative views may be more suitable under some circumstances.

Two view implementations are included. There is a graph view based on visjs, and a simpler textual list view which just puts graph data into a simple HTML list.

The stix2viz module exports the following functions:

  • makeGraphData(stixContent, config)
  • makeGraphView(domElement, nodeDataSet, edgeDataSet, stixIdToObject, config)
  • makeListView(domElement, nodeDataSet, edgeDataSet, stixIdToObject, config)

The configuration object which is the last parameter of all of these functions is documented in the configuration section.

The first step to visualizing STIX content using this library, is to create the base graph data. This is done with makeGraphData. It accepts STIX content in a few different forms, including a single STIX object, array of objects, or bundle of objects, as JSON text, plain Javascript object, array, or Map.

The function returns a 3-tuple, [nodeDataSet, edgeDataSet, stixIdToObject]. The first two elements are DataSet objects with the graph data; the last contains the same STIX content as was passed in, but in a normalized form, to simplify handling. The normalized form is a Map instance which maps STIX IDs to Map instances containing the data for each STIX object. All nested mappings are recursively converted to Map instances as necessary.

Creation of a view is done within the context of a web page. One element from the web page must be chosen to act as the root of the content which will comprise the view. The makeGraphView or makeListView function is called with this root element, all the values obtained from making the graph data, and a configuration object. The view should automatically appear in the web page (depending on its styling).

These functions return instances of internal classes: the classes themselves are not exported, but the functions act as factories for them. There are some utility methods defined on the classes, e.g. a destroy() method for destroying the view and releasing resources.

All three of the public API functions accept a configuration object. Different library components require different types of configuration, but it can all co-exist within the same object. The components will look for and use whatever settings they need.

Configuration can be given as JSON text, a plain Javascript object, or a Map. A few top-level keys map to various types of config settings, described in the following subsections.

Some settings are naturally organized per STIX type, e.g. how one labels graph nodes corresponding to particular STIX types. One can use the STIX type as a top-level configuration key, and map to type-specific settings:

{
    "<stix_type>": {
        "displayProperty": "<prop_name>",
        "displayIcon": "<icon_file_name>",
        "embeddedRelationships": ["...relationships..."]
    }
}

The meanings are as follows:

  • displayProperty names a top-level property of STIX objects of the given type. The value of that property is used as the graph node label for nodes corresponding to STIX objects of this type. If an object-specific label is given, it will override this setting.

  • displayIcon gives a URL to an icon file to use for this STIX type. This would be most relevant for graphical visualizations which use icons. Both library included views use this to create a legend: the legend will display icons even though the list view is textual.

  • embeddedRelationships describes what embedded relationships should be converted to graph edges, and how to create the edges. The value of this property is an array of length-three arrays:

    ["<property_path>", "<edge_label>", true]

    The first element is a property path which should identify a _ref(s) property in objects of that type. The second is a label to use for the edges, and the third element is a boolean which determines the directionality of the resulting edges. If true, the edge direction will be referrer -> referent; otherwise, the direction will be the reverse.

displayProperty and embeddedRelationships are used only when creating graph data. displayIcon is used only in the views.

There is one config section which contains object-specific settings: userLabels. It allows users to directly label individual STIX objects. It is a mapping from STIX ID to label:

{
    "userLabels": {
        "identity--349fbdc2-959e-4f76-9a44-256e226419ba": "Bob"
    }
}

This overrides per-type label settings.

It is possible to include or exclude STIX objects from being used to create graph data, on the basis of some criteria:

{
    "include": "<criteria>",
    "exclude": "<criteria>"
}

include is used to describe which STIX objects to include; exclude is used to describe which STIX objects to exclude. Users can choose one of these, depending on what is most natural for their usage. It is also possible to include both settings. If both are included, STIX objects are included which match include and do not match exclude.

How to express the criteria is described in the next section.

These criteria are intended to support a true/false match capability on STIX objects. The design is based on Mongo queries, but is not the same.

A set of criteria is expressed as a mapping. Each entry in the mapping represents sub-criteria, and the sub-criteria represented by all map entries are implicitly AND'd. At the top level, property path criteria and logical operators are most useful. At nested levels, one can use value and presence criteria as well.

Value criteria express a comparison directly on some value. With respect to STIX objects, this type of criteria is not useful at the top level because useful checks against whole objects (and arrays) are not defined. They are only useful at nested levels, applied to simple values. Value criteria can be given as a plain value, which acts as an equality check, or a mapping with an operator which maps to an operand value.

For example:

{ "$gt": 80 }

An example of usage of the above value criterion is:

{
    "confidence": { "$gt": 80 }
}

This matches objects with a confidence value greater than 80. One could use $eq to perform an equality check, or use 80 directly as the value criterion, which means the same thing.

Supported value criterion operators include: $eq, $gt, $gte, $in, $lt, $lte, $ne, $nin. $in and $nin must map to arrays, since they mean "in" and "not in" the given array.

A property path criterion maps a property path to some sub-criteria. The property path acts as a kind of "selector" of values from the object (or some sub-object). These values are checked against the sub-criteria, and the results are OR'd.

For example, given object:

{
    "foo": [
        {"bar": 1},
        {"bar": 2}
    ]
}

Criteria:

{
    "foo.bar": 1
}

will produce a match. The "foo.bar" property path selects values 1 and 2, and 1 as the mapped criterion is a value sub-criterion which acts as a direct equality check with that value (using the Javascript "===" operator). These checks are implicitly OR'd, so the net result is equivalent to (1 === 1 || 2 === 1).

Logical operator criteria are map entries with keys $and, $or, or $not. They behave as one would expect: the first two must map to arrays of criteria; the last maps to a single criterion.

$not deserves some special discussion. It causes evaluation of the mapped criterion, and simply inverts the result. It does not invert any nested operators, so it can result in subtle behavioral differences as compared to an inverted operator. For example, given object:

{
    "foo": [1, 2]
}

The following criteria produce results as shown:

  • {"foo": {"$in": [2, 3]}}: match
  • {"foo": {"$nin": [2, 3]}}: match
  • {"foo": {"$not": {"$in": [2, 3]}}}: no match

The first is equivalent to (1 in [2, 3]) OR (2 in [2, 3]), which is true; the second is equivalent to (1 not in [2, 3]) OR (2 not in [2, 3]) which is also true; and the last is the inversion of the first, so it is false.

The second is checking for a value not in [2, 3], whereas the last is effectively ensuring that none of the values are in [2, 3], and those are different criteria.

There is one presence criterion, which occurs when a map key is $exists. It only makes sense nested directly under a property path criterion, and is intended to act as a property presence check. It can't be quite that simple though, because a property path isn't as simple as a plain property name. So this criterion has a more general behavior, but acts as expected when the property path has only one component (is just a property name).

The $exists key must map to a boolean. If it maps to true, the criterion matches if the property path selects any values from the object. If it maps to false, the criterion matches if the property path selects nothing from the object.

For example, given object:

{
    "foo": [
        {"bar": 1},
        {"bar": 2}
    ]
}

The following criteria produce results as shown:

  • {"foo": {"$exists": true}}: match
  • {"bar": {"$exists": false}}: match
  • {"foo.bar": {"$exists": false}}: no match

Property paths are used in various places in the configuration settings to identify parts of STIX objects. They can also be seen as "selectors" of sub-components of a given object. If array-valued properties are present, a single property path may identify multiple parts of the same object. Property paths are designed to be transparent to arrays. There is no way to identify a particular element of an array in a property path.

Property paths are strings in a particular syntax. That syntax is a sequence of property names separated by dots. Property paths shouldn't begin or end with a dot, there should be no adjacent dots, and the path should not be empty. Property names can't contain dots; there is no escaping. But this should be okay since STIX property names should not contain dots.

For example, given structure:

{
    "a": [
        {"b": 1},
        {
            "b": {
                "c": [2, 3]
            }
        }
    ]
}

Paths and selected sub-components include:

  • "a" -> {"b": 1}, {"b": {"c": [2, 3]}} (two results)
  • "a.b" -> 1, {"c": [2, 3]} (two results)
  • "a.b.c" -> 2, 3 (two results)
  • "a.b.c.d" -> (no results)

Notice that if the property path refers to an array (i.e. the last path component names an array-valued property), it does not select the array itself, it selects the individual elements of the array. If those individual elements are themselves arrays, they are presently not searched. I.e. if the path refers to an array, the resulting selection is not as if the array was flattened.