cyberstorm/src/domains/common.rs

237 lines
7.2 KiB
Rust

use std::fmt;
use std::num::{NonZeroU16, ParseIntError};
use std::str::FromStr;
use serde::de::{DeserializeOwned, Deserializer, Error};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use strum::{EnumString, IntoStaticStr};
pub use toml::Value;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct References(Vec<String>);
pub trait DomainModel: Send + Serialize + DeserializeOwned {
fn kind() -> DomainModelKind;
fn name(&self) -> &str;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumString, IntoStaticStr)]
#[strum(serialize_all = "snake_case")]
pub enum DomainKind {
Source,
Threat,
Observe,
React,
Mitigate,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, EnumString, IntoStaticStr)]
pub enum DomainModelKind {
#[strum(serialize = "SIE")]
SourceIntelligence,
#[strum(serialize = "SPR")]
SourceProvider,
#[strum(serialize = "SRT")]
SourceRequirement,
#[strum(serialize = "TTC")]
ThreatTactic,
#[strum(serialize = "TTE")]
ThreatTechnique,
#[strum(serialize = "TSE")]
ThreatSoftware,
#[strum(serialize = "TSN")]
ThreatSimulation,
#[strum(serialize = "OET")]
ObserveEvent,
#[strum(serialize = "ODN")]
ObserveDetection,
#[strum(serialize = "OPR")]
ObserveProvider,
#[strum(serialize = "OCN")]
ObserveConfiguration,
#[strum(serialize = "RSE")]
ReactStage,
#[strum(serialize = "RAN")]
ReactAction,
#[strum(serialize = "RPK")]
ReactPlaybook,
#[strum(serialize = "MSY")]
MitigateStrategy,
#[strum(serialize = "MPM")]
MitigatePlatform,
#[strum(serialize = "MCN")]
MitigateConfiguration,
}
impl DomainModelKind {
pub fn domain(&self) -> DomainKind {
match self {
DomainModelKind::SourceIntelligence => DomainKind::Source,
DomainModelKind::SourceProvider => DomainKind::Source,
DomainModelKind::SourceRequirement => DomainKind::Source,
DomainModelKind::ThreatTactic => DomainKind::Threat,
DomainModelKind::ThreatTechnique => DomainKind::Threat,
DomainModelKind::ThreatSoftware => DomainKind::Threat,
DomainModelKind::ThreatSimulation => DomainKind::Threat,
DomainModelKind::ObserveEvent => DomainKind::Observe,
DomainModelKind::ObserveDetection => DomainKind::Observe,
DomainModelKind::ObserveProvider => DomainKind::Observe,
DomainModelKind::ObserveConfiguration => DomainKind::Observe,
DomainModelKind::ReactStage => DomainKind::React,
DomainModelKind::ReactAction => DomainKind::React,
DomainModelKind::ReactPlaybook => DomainKind::React,
DomainModelKind::MitigateStrategy => DomainKind::Mitigate,
DomainModelKind::MitigatePlatform => DomainKind::Mitigate,
DomainModelKind::MitigateConfiguration => DomainKind::Mitigate,
}
}
pub fn parts(self) -> (&'static str, &'static str) {
match self {
DomainModelKind::SourceIntelligence => ("source", "intelligence"),
DomainModelKind::SourceProvider => ("source", "provider"),
DomainModelKind::SourceRequirement => ("source", "requirement"),
DomainModelKind::ThreatTactic => ("threat", "tactic"),
DomainModelKind::ThreatTechnique => ("threat", "technique"),
DomainModelKind::ThreatSoftware => ("threat", "software"),
DomainModelKind::ThreatSimulation => ("threat", "simulation"),
DomainModelKind::ObserveEvent => ("observe", "event"),
DomainModelKind::ObserveDetection => ("observe", "detection"),
DomainModelKind::ObserveProvider => ("observe", "provider"),
DomainModelKind::ObserveConfiguration => ("observe", "configuration"),
DomainModelKind::ReactStage => ("react", "stage"),
DomainModelKind::ReactAction => ("react", "action"),
DomainModelKind::ReactPlaybook => ("react", "playbook"),
DomainModelKind::MitigateStrategy => ("mitigate", "strategy"),
DomainModelKind::MitigatePlatform => ("mitigate", "platform"),
DomainModelKind::MitigateConfiguration => ("mitigate", "configuration"),
}
}
}
///////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub struct ModelId {
pub major: u16,
pub minor: Option<NonZeroU16>,
}
impl ModelId {
pub fn new(major: u16) -> Self {
Self { major, minor: None }
}
pub fn is_example(self) -> bool {
self.major == 0
}
}
impl fmt::Display for ModelId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:0>5}", self.major)?;
if let Some(minor) = self.minor {
write!(f, ".{:0>3}", minor)?;
}
Ok(())
}
}
impl FromStr for ModelId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, '.');
let major = parts.next().unwrap().parse()?;
let minor = parts.next().map(FromStr::from_str).transpose()?;
Ok(Self { major, minor })
}
}
impl Serialize for ModelId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
impl<'de> Deserialize<'de> for ModelId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <&str>::deserialize(deserializer)?;
s.parse().map_err(D::Error::custom)
}
}
///////////////////////////////////////////////////////////////////////////////
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub struct DomainId {
pub kind: DomainModelKind,
pub model_id: ModelId,
}
impl DomainId {
pub fn new(kind: DomainModelKind, model_id: ModelId) -> Self {
Self { kind, model_id }
}
}
impl fmt::Debug for DomainId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Id")
.field(&format_args!("{}", self))
.finish()
}
}
impl fmt::Display for DomainId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", <&'static str>::from(&self.kind), self.model_id)
}
}
impl FromStr for DomainId {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() > 3 && s.is_char_boundary(3) {
let (kind, model_id) = s.split_at(3);
if let (Ok(kind), Ok(model_id)) =
(DomainModelKind::from_str(kind), ModelId::from_str(model_id))
{
return Ok(Self { kind, model_id });
}
}
Err("invalid storm id")
}
}
impl Serialize for DomainId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
impl<'de> Deserialize<'de> for DomainId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <&str>::deserialize(deserializer)?;
s.parse().map_err(D::Error::custom)
}
}