Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Objects & Bundles

stix-model imports STIX objects and bundles into a model that is typed where it helps and flexible everywhere else.

The trichotomy: typed, generic, custom

Every imported object is one of three shapes:

ShapeWhenWhat you get
TypedRecognized types (currently observed-data)A real Rust struct (ObservedData) with typed fields
GenericEvery other type — including custom x-* typesA value-backed property map preserving all properties
CustomTypes you register yourselfYour struct (see Custom Object Types)

The crucial design point: all three implement the ObjectView trait —

#![allow(unused)]
fn main() {
pub trait ObjectView {
    fn id(&self) -> Option<&str>;
    fn type_(&self) -> Option<&str>;
    fn property(&self, name: &str) -> Option<StixValue>;
}
}

— and the matcher consumes only ObjectView. That’s why unknown and custom types match out of the box, and why typed objects can synthesize properties on demand (a property need not literally exist in the JSON to be matchable).

StixValue

property() returns a StixValue — a JSON-shaped dynamic value: null, bool, integer, float, string, list, or object. Two things to know:

  • Integers and floats are distinct (numeric comparisons promote, but the values are stored as parsed).
  • Timestamps, hex, and binary are carried as strings at this layer; the matcher compares them against pattern literals as strings.

Bundles and the object store

#![allow(unused)]
fn main() {
use stix::model::{Bundle, ObjectStore, ObjectView};

let bundle = Bundle::from_json_str(json)?;      // validates type == "bundle"
let store  = ObjectStore::from_bundle(&bundle); // id → object index

let obj = store.get("ipv4-addr--a1").unwrap();
assert_eq!(obj.property("value").unwrap().as_str(), Some("198.51.100.5"));
}

The ObjectStore is how reference properties resolve: when a pattern path walks through src_ref, the matcher looks the id up in the store. No store → reference paths resolve to nothing (not an error — an empty result).

ObservedData, the typed SDO

observed-data is the one built-in typed struct because the matcher needs its fields: first_observed / last_observed / number_observed (temporal metadata) and object_refs (which SCOs were seen together). It tolerates STIX 2.0’s inline objects map as well, and retains unknown properties via a flattened map so nothing is lost on round-trip.