Skip to main content

stix_matcher/
lib.rs

1//! Match STIX 2.1 patterns against observed STIX objects.
2//!
3//! # Example
4//!
5//! ```
6//! use stix_matcher::match_scos;
7//! use stix_pattern::parse;
8//! use stix_model::StixObject;
9//!
10//! let pattern = parse("[ipv4-addr:value = '198.51.100.1']").unwrap();
11//! let sco = StixObject::from_json(serde_json::json!({
12//!     "type": "ipv4-addr", "id": "ipv4-addr--1", "value": "198.51.100.1"
13//! })).unwrap();
14//!
15//! let result = match_scos(&pattern, &[sco]).unwrap();
16//! assert!(result.is_match());
17//! ```
18
19#![warn(missing_docs)]
20
21pub mod compare;
22pub mod error;
23pub mod eval;
24pub mod observation;
25pub mod pattern_ops;
26pub mod resolve;
27pub mod result;
28pub mod subset;
29
30pub use error::MatchError;
31pub use observation::Observation;
32pub use result::MatchResult;
33
34use stix_model::{Bundle, ObjectStore, StixObject, TypedObject};
35use stix_pattern::ast::Pattern;
36
37/// Match a pattern against a list of pre-built observations.
38pub fn match_observations(
39    pattern: &Pattern,
40    observations: &[Observation],
41) -> Result<MatchResult, MatchError> {
42    eval::eval_pattern(pattern, observations, None)
43}
44
45/// Match a pattern against `observed-data` SDOs, resolving their `object_refs`
46/// through `store` (MITRE-compatible entry point).
47pub fn match_observed_data(
48    pattern: &Pattern,
49    observed: &[stix_model::ObservedData],
50    store: &ObjectStore,
51) -> Result<MatchResult, MatchError> {
52    let observations: Vec<Observation> = observed
53        .iter()
54        .map(|od| {
55            let objects = od
56                .sco_ids()
57                .iter()
58                .filter_map(|id| store.get(id).cloned())
59                .collect();
60            Observation {
61                objects,
62                first_observed: Some(od.first_observed.clone()),
63                last_observed: Some(od.last_observed.clone()),
64                number_observed: od.number_observed,
65            }
66        })
67        .collect();
68    eval::eval_pattern(pattern, &observations, Some(store))
69}
70
71/// Match a pattern against a whole bundle, deriving observations from its
72/// `observed-data` SDOs and resolving references through the bundle's objects.
73pub fn match_bundle(pattern: &Pattern, bundle: &Bundle) -> Result<MatchResult, MatchError> {
74    let store = ObjectStore::from_bundle(bundle);
75    let observed: Vec<stix_model::ObservedData> = bundle
76        .objects
77        .iter()
78        .filter_map(|o| match o {
79            StixObject::Typed(TypedObject::ObservedData(od)) => Some(od.clone()),
80            _ => None,
81        })
82        .collect();
83    match_observed_data(pattern, &observed, &store)
84}
85
86/// Match a pattern against a flat list of cyber-observable objects, treated as a
87/// single observation.
88pub fn match_scos(pattern: &Pattern, scos: &[StixObject]) -> Result<MatchResult, MatchError> {
89    let store = ObjectStore::from_objects(scos);
90    let observation = Observation::new(scos.to_vec());
91    eval::eval_pattern(pattern, std::slice::from_ref(&observation), Some(&store))
92}