From 2fd06a5e83ae41631185ea188f5375e1d21acbca Mon Sep 17 00:00:00 2001 From: "Philip (a-0)" <@ph:a-0.me> Date: Sun, 2 Jul 2023 20:40:33 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 202 ++++++++++++++ Cargo.toml | 14 + README.md | 18 ++ src/lib.rs | 10 + src/v1/mod.rs | 11 + src/v1/schemas/attribute.rs | 11 + src/v1/schemas/attribute_attachment.rs | 47 ++++ src/v1/schemas/attribute_category.rs | 53 ++++ src/v1/schemas/attribute_comment.rs | 44 +++ src/v1/schemas/attribute_event_uuid.rs | 44 +++ src/v1/schemas/attribute_id.rs | 57 ++++ src/v1/schemas/attribute_list.rs | 25 ++ src/v1/schemas/attribute_no_id.rs | 11 + .../schemas/attribute_rest_search_filter.rs | 11 + src/v1/schemas/attribute_rest_search_list.rs | 25 ++ .../attribute_rest_search_list_item.rs | 11 + .../schemas/attribute_statistics_response.rs | 11 + src/v1/schemas/attribute_type.rs | 255 ++++++++++++++++++ src/v1/schemas/attribute_value.rs | 44 +++ src/v1/schemas/decay_score.rs | 17 ++ src/v1/schemas/decay_score_list.rs | 13 + src/v1/schemas/decaying_model.rs | 12 + src/v1/schemas/decaying_model_parameters.rs | 15 ++ .../describe_attribute_types_response.rs | 16 ++ src/v1/schemas/event.rs | 11 + src/v1/schemas/event_attribute_count.rs | 40 +++ src/v1/schemas/event_id.rs | 52 ++++ src/v1/schemas/event_info.rs | 37 +++ src/v1/schemas/event_no_id.rs | 11 + src/v1/schemas/event_organisation.rs | 11 + src/v1/schemas/event_proposal_email_lock.rs | 11 + src/v1/schemas/event_report.rs | 11 + src/v1/schemas/event_tag.rs | 11 + src/v1/schemas/event_tag_id.rs | 52 ++++ src/v1/schemas/event_tag_list.rs | 25 ++ src/v1/schemas/extended_attribute.rs | 11 + src/v1/schemas/full_decaying_model.rs | 11 + src/v1/schemas/mod.rs | 36 +++ src/v1/schemas/uuid.rs | 46 ++++ 40 files changed, 1354 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 src/v1/mod.rs create mode 100644 src/v1/schemas/attribute.rs create mode 100644 src/v1/schemas/attribute_attachment.rs create mode 100644 src/v1/schemas/attribute_category.rs create mode 100644 src/v1/schemas/attribute_comment.rs create mode 100644 src/v1/schemas/attribute_event_uuid.rs create mode 100644 src/v1/schemas/attribute_id.rs create mode 100644 src/v1/schemas/attribute_list.rs create mode 100644 src/v1/schemas/attribute_no_id.rs create mode 100644 src/v1/schemas/attribute_rest_search_filter.rs create mode 100644 src/v1/schemas/attribute_rest_search_list.rs create mode 100644 src/v1/schemas/attribute_rest_search_list_item.rs create mode 100644 src/v1/schemas/attribute_statistics_response.rs create mode 100644 src/v1/schemas/attribute_type.rs create mode 100644 src/v1/schemas/attribute_value.rs create mode 100644 src/v1/schemas/decay_score.rs create mode 100644 src/v1/schemas/decay_score_list.rs create mode 100644 src/v1/schemas/decaying_model.rs create mode 100644 src/v1/schemas/decaying_model_parameters.rs create mode 100644 src/v1/schemas/describe_attribute_types_response.rs create mode 100644 src/v1/schemas/event.rs create mode 100644 src/v1/schemas/event_attribute_count.rs create mode 100644 src/v1/schemas/event_id.rs create mode 100644 src/v1/schemas/event_info.rs create mode 100644 src/v1/schemas/event_no_id.rs create mode 100644 src/v1/schemas/event_organisation.rs create mode 100644 src/v1/schemas/event_proposal_email_lock.rs create mode 100644 src/v1/schemas/event_report.rs create mode 100644 src/v1/schemas/event_tag.rs create mode 100644 src/v1/schemas/event_tag_id.rs create mode 100644 src/v1/schemas/event_tag_list.rs create mode 100644 src/v1/schemas/extended_attribute.rs create mode 100644 src/v1/schemas/full_decaying_model.rs create mode 100644 src/v1/schemas/mod.rs create mode 100644 src/v1/schemas/uuid.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..945e643 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,202 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "misplib" +version = "0.1.0" +dependencies = [ + "phf", + "regex-macro", + "serde", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fa36e7add16db296640bba993a65dae2a0088a8e5cd5f935c8bfbd3710145b" +dependencies = [ + "once_cell", + "regex", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "syn" +version = "2.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c1c3f7b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "misplib" +version = "0.1.0" +edition = "2021" + +[dependencies] +phf = { version = "0.11", default-features = false, features = [ "macros" ] } +regex-macro = "0.2.0" +serde = { version = "1.0.164", features = ["derive"] } +serde_json = "1.0.99" + +[lib] +name = "misplib" +path = "src/lib.rs" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e6a39ff --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# misplib +This crate contains all data structures present in the [MISP API](https://www.misp-project.org/openapi/), and accompanying helper functions. + +## Versioning system +For every commit in the [main MISP repository](https://github.com/MISP/MISP) which changes the [API definition](https://raw.githubusercontent.com/MISP/MISP/develop/app/webroot/doc/openapi.yaml) a new version of misplib is created. Version numbers are assigned as ascending integers. + +Multiple versions of misplib can be accessed, they are defined in `src/v/mod.rs`, in this file you can also find the unique commit hash of the MISP repository this version is developed for. + +## Contributing +You can contribute to this project by +- adding tests to existing types +- proposing a new version if you detected a change in the MISP API +- adding helper functions (such as implementations of useful traits etc.) +- working on the serialization and deserialization of all types. The goal is to be automatically parse any MISP API object into valid rust misplib objects. +- anything else you can think of :) + +## Contact +Feel free to discuss and ask questions in https://matrix.to/#/#misplib:a-0.me \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b954b85 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +macro_rules! expose_submodules { + ( $( $x:ident ),* ) => { + $( + mod $x; + pub use self::$x::*; + )* + }; +} + +pub mod v1; diff --git a/src/v1/mod.rs b/src/v1/mod.rs new file mode 100644 index 0000000..0d4df35 --- /dev/null +++ b/src/v1/mod.rs @@ -0,0 +1,11 @@ +macro_rules! default_derive { + ($i:item) => { + use serde::{Serialize, Deserialize}; + #[derive(Serialize, Deserialize, PartialEq, Debug)] + $i + }; +} + +mod schemas; + +pub use schemas::*; \ No newline at end of file diff --git a/src/v1/schemas/attribute.rs b/src/v1/schemas/attribute.rs new file mode 100644 index 0000000..192e4de --- /dev/null +++ b/src/v1/schemas/attribute.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct Attribute { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_attachment.rs b/src/v1/schemas/attribute_attachment.rs new file mode 100644 index 0000000..12d8c6d --- /dev/null +++ b/src/v1/schemas/attribute_attachment.rs @@ -0,0 +1,47 @@ +use regex_macro::regex; + +default_derive!{ + pub struct AttributeAttachment { + content: String + } +} + +impl TryFrom<&str> for AttributeAttachment { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$"); + if re.is_match(value) { + Ok(AttributeAttachment { content: String::from(value) }) + } + else { + Err("Failed to parse AttributeAttachment") + } + } +} + +impl Into for AttributeAttachment { + fn into(self) -> String { + self.content + } +} + + +#[test] +fn forbidden_char() { + let att: Result = "uqiolgfnluiqoegn&".try_into(); + println!("{:?}", att); + assert!(att.is_err()) +} + +#[test] +fn valid1() { + let att: Result = "".try_into(); + assert_eq!(att, Ok(AttributeAttachment {content: String::from("")})) +} + +#[test] +fn valid2() { + let att: Result = "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu".try_into(); + assert_eq!(att, Ok(AttributeAttachment{content: String::from("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu")})) +} diff --git a/src/v1/schemas/attribute_category.rs b/src/v1/schemas/attribute_category.rs new file mode 100644 index 0000000..b893c6b --- /dev/null +++ b/src/v1/schemas/attribute_category.rs @@ -0,0 +1,53 @@ +use phf::phf_set; + + +default_derive!{ + pub struct AttributeCategory { + cat: String, + } +} + +impl TryFrom<&str> for AttributeCategory { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let valid_categories: phf::Set<&'static str> = phf_set!("Internal reference", "Targeting data", "Antivirus detection", "Payload delivery", "Artifacts dropped", "Payload installation", "Persistence mechanism", "Network activity", "Payload type", "Attribution", "External analysis", "Financial fraud", "Support Tool", "Social network", "Person", "Other"); + if value.len() <= 255 && valid_categories.contains(value) { + Ok(AttributeCategory { cat: value.to_string() }) + } + else { + Err("Failed to parse AttributeCategory") + } + } +} + +impl Into for AttributeCategory { + fn into(self) -> String { + self.cat + } +} + + +#[test] +fn empty() { + let cat: Result = "".try_into(); + assert!(cat.is_err()) +} + +#[test] +fn oversized() { + let cat: Result = format!("{:>256}", "Test").as_str().try_into(); + assert!(cat.is_err()) +} + +#[test] +fn valid1() { + let cat: Result = "Internal reference".try_into(); + assert_eq!(cat, Ok(AttributeCategory { cat: "Internal reference".to_string() })) +} + +#[test] +fn valid2() { + let cat: Result = "Person".try_into(); + assert_eq!(cat, Ok(AttributeCategory {cat: String::from("Person")})) +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_comment.rs b/src/v1/schemas/attribute_comment.rs new file mode 100644 index 0000000..b3664a4 --- /dev/null +++ b/src/v1/schemas/attribute_comment.rs @@ -0,0 +1,44 @@ + +default_derive!{ + pub struct AttributeComment { + text: String + } +} + +impl TryFrom<&str> for AttributeComment { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + if value.len() <= 65535 { + Ok(AttributeComment { text: value.to_string() }) + } + else { + Err("Failed to parse AttributeComment") + } + } +} + +impl Into for AttributeComment { + fn into(self) -> String { + self.text + } +} + + +#[test] +fn oversized() { + let comment: Result = format!("{:>65536}", "Test").as_str().try_into(); + assert!(comment.is_err()) +} + +#[test] +fn valid1() { + let comment: Result = "".try_into(); + assert_eq!(comment, Ok(AttributeComment { text: "".to_string() })) +} + +#[test] +fn valid2() { + let comment: Result = "Comment".try_into(); + assert_eq!(comment, Ok(AttributeComment{text: String::from("Comment")})) +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_event_uuid.rs b/src/v1/schemas/attribute_event_uuid.rs new file mode 100644 index 0000000..c8077ad --- /dev/null +++ b/src/v1/schemas/attribute_event_uuid.rs @@ -0,0 +1,44 @@ +use super::UUID; + + +default_derive!{ + pub struct AttributeEventUUID { + uuid: String + } +} + +impl TryFrom<&str> for AttributeEventUUID { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let uuid: Result = value.try_into(); + match uuid { + Ok(id) => Ok(AttributeEventUUID{uuid: id.into()}), + Err(_) => Err("Failed to parse AttributeEventUUID"), + } + } +} + +impl Into for AttributeEventUUID { + fn into(self) -> String { + self.uuid + } +} + +#[test] +fn empty() { + let uuid: Result = "".try_into(); + assert!(uuid.is_err()) +} + +#[test] +fn oversized() { + let uuid: Result = format!("{:>37}", "Test").as_str().try_into(); + assert!(uuid.is_err()) +} + +#[test] +fn valid1() { + let uuid: Result = "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".try_into(); + assert_eq!(uuid, Ok(AttributeEventUUID{ uuid: "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".to_string() })) +} diff --git a/src/v1/schemas/attribute_id.rs b/src/v1/schemas/attribute_id.rs new file mode 100644 index 0000000..bd8214e --- /dev/null +++ b/src/v1/schemas/attribute_id.rs @@ -0,0 +1,57 @@ +use regex_macro::regex; + +default_derive!{ + pub struct AttributeId { + id: String + } +} + +impl TryFrom<&str> for AttributeId { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^[[:digit:]]+$"); + if re.is_match(value) && value.len() <= 10 { + Ok(AttributeId{ id: value.to_string() }) + } + else { + Err("Failed to parse AttributeId") + } + } +} + +impl Into for AttributeId { + fn into(self) -> String { + self.id + } +} + +#[test] +fn empty() { + let id: Result = "".try_into(); + assert!(id.is_err()) +} + +#[test] +fn oversized() { + let id: Result = "12345678910".try_into(); + assert!(id.is_err()) +} + +#[test] +fn forbidden_char() { + let id: Result = "123r5".try_into(); + assert!(id.is_err()) +} + +#[test] +fn valid1() { + let id: Result = "0".try_into(); + assert_eq!(id, Ok(AttributeId{id: String::from("0")})) +} + +#[test] +fn valid2() { + let id: Result = "123456789".try_into(); + assert_eq!(id, Ok(AttributeId{id: String::from("123456789")})); +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_list.rs b/src/v1/schemas/attribute_list.rs new file mode 100644 index 0000000..fe22a4e --- /dev/null +++ b/src/v1/schemas/attribute_list.rs @@ -0,0 +1,25 @@ +use super::Attribute; + + +default_derive!{ + pub struct AttributeList { + attrs: Vec + } +} + +impl From> for AttributeList { + fn from(value: Vec) -> Self { + AttributeList { attrs: value } + } +} + +impl Into> for AttributeList { + fn into(self) -> Vec { + self.attrs + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_no_id.rs b/src/v1/schemas/attribute_no_id.rs new file mode 100644 index 0000000..4aa126f --- /dev/null +++ b/src/v1/schemas/attribute_no_id.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct AttributeWithoutId { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_rest_search_filter.rs b/src/v1/schemas/attribute_rest_search_filter.rs new file mode 100644 index 0000000..73cb823 --- /dev/null +++ b/src/v1/schemas/attribute_rest_search_filter.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct AttributeRestSearchFilter { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_rest_search_list.rs b/src/v1/schemas/attribute_rest_search_list.rs new file mode 100644 index 0000000..17dac30 --- /dev/null +++ b/src/v1/schemas/attribute_rest_search_list.rs @@ -0,0 +1,25 @@ +use super::AttributeRestSearchListItem; + + +default_derive!{ + pub struct AttributeRestSearchList { + items: Vec + } +} + +impl From> for AttributeRestSearchList { + fn from(value: Vec) -> Self { + AttributeRestSearchList { items: value } + } +} + +impl Into> for AttributeRestSearchList { + fn into(self) -> Vec { + self.items + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_rest_search_list_item.rs b/src/v1/schemas/attribute_rest_search_list_item.rs new file mode 100644 index 0000000..6609962 --- /dev/null +++ b/src/v1/schemas/attribute_rest_search_list_item.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct AttributeRestSearchListItem { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_statistics_response.rs b/src/v1/schemas/attribute_statistics_response.rs new file mode 100644 index 0000000..fda5015 --- /dev/null +++ b/src/v1/schemas/attribute_statistics_response.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct AttributeStatisticsResponse { + stats: Vec<(String, i64)> + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/attribute_type.rs b/src/v1/schemas/attribute_type.rs new file mode 100644 index 0000000..0d9dc21 --- /dev/null +++ b/src/v1/schemas/attribute_type.rs @@ -0,0 +1,255 @@ +use phf::phf_set; + +default_derive! { + pub struct AttributeType { + t: String + } +} + +impl TryFrom<&str> for AttributeType { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let valid_types: phf::Set<&'static str> = phf_set!( + "md5", + "sha1", + "sha256", + "filename", + "pdb", + "filename|md5", + "filename|sha1", + "filename|sha256", + "ip-src", + "ip-dst", + "hostname", + "domain", + "domain|ip", + "email", + "email-src", + "eppn", + "email-dst", + "email-subject", + "email-attachment", + "email-body", + "float", + "git-commit-id", + "url", + "http-method", + "user-agent", + "ja3-fingerprint-md5", + "jarm-fingerprint", + "favicon-mmh3", + "hassh-md5", + "hasshserver-md5", + "regkey", + "regkey|value", + "AS", + "snort", + "bro", + "zeek", + "community-id", + "pattern-in-file", + "pattern-in-traffic", + "pattern-in-memory", + "pattern-filename", + "pgp-public-key", + "pgp-private-key", + "yara", + "stix2-pattern", + "sigma", + "gene", + "kusto-query", + "mime-type", + "identity-card-number", + "cookie", + "vulnerability", + "cpe", + "weakness", + "attachment", + "malware-sample", + "link", + "comment", + "text", + "hex", + "other", + "named pipe", + "mutex", + "process-state", + "target-user", + "target-email", + "target-machine", + "target-org", + "target-location", + "target-external", + "btc", + "dash", + "xmr", + "iban", + "bic", + "bank-account-nr", + "aba-rtn", + "bin", + "cc-number", + "prtn", + "phone-number", + "threat-actor", + "campaign-name", + "campaign-id", + "malware-type", + "uri", + "authentihash", + "vhash", + "ssdeep", + "imphash", + "telfhash", + "pehash", + "impfuzzy", + "sha224", + "sha384", + "sha512", + "sha512/224", + "sha512/256", + "sha3-224", + "sha3-256", + "sha3-384", + "sha3-512", + "tlsh", + "cdhash", + "filename|authentihash", + "filename|vhash", + "filename|ssdeep", + "filename|imphash", + "filename|impfuzzy", + "filename|pehash", + "filename|sha224", + "filename|sha384", + "filename|sha512", + "filename|sha512/224", + "filename|sha512/256", + "filename|sha3-224", + "filename|sha3-256", + "filename|sha3-384", + "filename|sha3-512", + "filename|tlsh", + "windows-scheduled-task", + "windows-service-name", + "windows-service-displayname", + "whois-registrant-email", + "whois-registrant-phone", + "whois-registrant-name", + "whois-registrant-org", + "whois-registrar", + "whois-creation-date", + "x509-fingerprint-sha1", + "x509-fingerprint-md5", + "x509-fingerprint-sha256", + "dns-soa-email", + "size-in-bytes", + "counter", + "datetime", + "port", + "ip-dst|port", + "ip-src|port", + "hostname|port", + "mac-address", + "mac-eui-64", + "email-dst-display-name", + "email-src-display-name", + "email-header", + "email-reply-to", + "email-x-mailer", + "email-mime-boundary", + "email-thread-index", + "email-message-id", + "github-username", + "github-repository", + "github-organisation", + "jabber-id", + "twitter-id", + "dkim", + "dkim-signature", + "first-name", + "middle-name", + "last-name", + "full-name", + "date-of-birth", + "place-of-birth", + "gender", + "passport-number", + "passport-country", + "passport-expiration", + "redress-number", + "nationality", + "visa-number", + "issue-date-of-the-visa", + "primary-residence", + "country-of-residence", + "special-service-request", + "frequent-flyer-number", + "travel-details", + "payment-details", + "place-port-of-original-embarkation", + "place-port-of-clearance", + "place-port-of-onward-foreign-destination", + "passenger-name-record-locator-number", + "mobile-application-id", + "chrome-extension-id", + "cortex", + "boolean", + "anonymised" + ); + if value.len() <= 100 && valid_types.contains(value) { + Ok(AttributeType { + t: value.to_string(), + }) + } else { + Err("Failed to parse AttributeType") + } + } +} + +impl Into for AttributeType { + fn into(self) -> String { + self.t + } +} + +#[test] +fn empty() { + let t: Result = "".try_into(); + assert!(t.is_err()) +} + +#[test] +fn oversized() { + let t: Result = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvw".try_into(); + assert!(t.is_err()) +} + +#[test] +fn unknown() { + let t: Result = "abcde".try_into(); + assert!(t.is_err()) +} + +#[test] +fn valid1() { + let t: Result = "text".try_into(); + assert_eq!( + t, + Ok(AttributeType { + t: "text".to_string() + }) + ) +} + +#[test] +fn valid2() { + let t: Result = "filename|sha512/224".try_into(); + assert_eq!( + t, + Ok(AttributeType { + t: "filename|sha512/224".to_string() + }) + ) +} diff --git a/src/v1/schemas/attribute_value.rs b/src/v1/schemas/attribute_value.rs new file mode 100644 index 0000000..25b89d9 --- /dev/null +++ b/src/v1/schemas/attribute_value.rs @@ -0,0 +1,44 @@ + +default_derive!{ + pub struct AttributeValue { + value: String + } +} + +impl TryFrom<&str> for AttributeValue { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + if value.len() <= 131071 { + Ok(AttributeValue { value: value.to_string() }) + } + else { + Err("Failed to parse AttributeValue") + } + } +} + +impl Into for AttributeValue { + fn into(self) -> String { + self.value + } +} + + +#[test] +fn oversized() { + let val: Result = format!("{:>131072}", "Test").as_str().try_into(); + assert!(val.is_err()) +} + +#[test] +fn valid1() { + let val: Result = "".try_into(); + assert_eq!(val, Ok(AttributeValue { value: "".to_string() })) +} + +#[test] +fn valid2() { + let val: Result = "123456789".try_into(); + assert_eq!(val, Ok(AttributeValue {value: String::from("123456789")})) +} \ No newline at end of file diff --git a/src/v1/schemas/decay_score.rs b/src/v1/schemas/decay_score.rs new file mode 100644 index 0000000..58e54bd --- /dev/null +++ b/src/v1/schemas/decay_score.rs @@ -0,0 +1,17 @@ +use super::{FullDecayingModel, DecayingModel}; + + +default_derive!{ + pub struct DecayScore { + score: f64, + base_score: f64, + decayed: bool, + decaying_model: DecayingModel, + full_decaying_model: FullDecayingModel, + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/decay_score_list.rs b/src/v1/schemas/decay_score_list.rs new file mode 100644 index 0000000..97fc965 --- /dev/null +++ b/src/v1/schemas/decay_score_list.rs @@ -0,0 +1,13 @@ +use super::DecayScore; + + +default_derive!{ + pub struct DecayScoreList { + scores: Vec + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/decaying_model.rs b/src/v1/schemas/decaying_model.rs new file mode 100644 index 0000000..d040f5e --- /dev/null +++ b/src/v1/schemas/decaying_model.rs @@ -0,0 +1,12 @@ + +default_derive!{ + pub struct DecayingModel { + id: String, + name: String, + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/decaying_model_parameters.rs b/src/v1/schemas/decaying_model_parameters.rs new file mode 100644 index 0000000..4f90504 --- /dev/null +++ b/src/v1/schemas/decaying_model_parameters.rs @@ -0,0 +1,15 @@ + +default_derive!{ + pub struct DecayingModelParameters { + lifetime: f64, + decay_speed: f64, + threshold: f64, + default_base_score: f64, + base_score_config: Vec<(String, f64)> + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/describe_attribute_types_response.rs b/src/v1/schemas/describe_attribute_types_response.rs new file mode 100644 index 0000000..c72b8f1 --- /dev/null +++ b/src/v1/schemas/describe_attribute_types_response.rs @@ -0,0 +1,16 @@ +use super::{AttributeType, AttributeCategory}; + + +default_derive!{ + pub struct DescribeAttributeTypesResponse { + sane_defaults: Vec<(AttributeType, (String, i64))>, + types: Vec, + categories: Vec, + category_type_mappings: Vec<(AttributeCategory, Vec)> + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event.rs b/src/v1/schemas/event.rs new file mode 100644 index 0000000..dd34672 --- /dev/null +++ b/src/v1/schemas/event.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct Event { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event_attribute_count.rs b/src/v1/schemas/event_attribute_count.rs new file mode 100644 index 0000000..57fdac0 --- /dev/null +++ b/src/v1/schemas/event_attribute_count.rs @@ -0,0 +1,40 @@ +use regex_macro::regex; + + +default_derive!{ + pub struct EventAttributeCount { + count: String + } +} + +impl TryFrom<&str> for EventAttributeCount { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^[[:digit:]]+$"); + if re.is_match(value) { + Ok(EventAttributeCount { count: value.to_string() }) + } + else { + Err("Failed to parse EventId") + } + } +} + +impl Into for EventAttributeCount { + fn into(self) -> String { + self.count + } +} + +#[test] +fn empty() { + let id: Result = "".try_into(); + assert!(id.is_err()) +} + +#[test] +fn valid1() { + let id: Result = "12345".try_into(); + assert_eq!(id, Ok(EventAttributeCount { count: "12345".to_string() })) +} \ No newline at end of file diff --git a/src/v1/schemas/event_id.rs b/src/v1/schemas/event_id.rs new file mode 100644 index 0000000..f2dfedc --- /dev/null +++ b/src/v1/schemas/event_id.rs @@ -0,0 +1,52 @@ +use regex_macro::regex; + + +default_derive!{ + pub struct EventId { + id: String, + } +} + +impl TryFrom<&str> for EventId { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^[[:digit:]]+$"); + if value.len() <= 10 && re.is_match(value) { + Ok(EventId { id: value.to_string() }) + } + else { + Err("Failed to parse EventId") + } + } +} + +impl Into for EventId { + fn into(self) -> String { + self.id + } +} + +#[test] +fn empty() { + let id: Result = "".try_into(); + assert!(id.is_err()) +} + +#[test] +fn oversized() { + let id: Result = "12345678910".try_into(); + assert!(id.is_err()) +} + +#[test] +fn valid1() { + let id: Result = "12345".try_into(); + assert_eq!(id, Ok(EventId { id: "12345".to_string() })) +} + +#[test] +fn valid2() { + let id: Result = "0123456789".try_into(); + assert_eq!(id, Ok(EventId { id: "0123456789".to_string() })) +} \ No newline at end of file diff --git a/src/v1/schemas/event_info.rs b/src/v1/schemas/event_info.rs new file mode 100644 index 0000000..aeb5e50 --- /dev/null +++ b/src/v1/schemas/event_info.rs @@ -0,0 +1,37 @@ + +default_derive!{ + pub struct EventInfo { + text: String + } +} + +impl TryFrom<&str> for EventInfo { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + if value.len() <= 65535 { + Ok(EventInfo { text: value.to_string() }) + } + else { + Err("Failed to parse EventInfo") + } + } +} + +impl Into for EventInfo { + fn into(self) -> String { + self.text + } +} + +#[test] +fn oversized() { + let info: Result = format!("{:>65536}", "Test").as_str().try_into(); + assert!(info.is_err()) +} + +#[test] +fn valid1() { + let info: Result = "Informational text".try_into(); + assert_eq!(info, Ok(EventInfo { text: "Informational text".to_string() })) +} \ No newline at end of file diff --git a/src/v1/schemas/event_no_id.rs b/src/v1/schemas/event_no_id.rs new file mode 100644 index 0000000..e995516 --- /dev/null +++ b/src/v1/schemas/event_no_id.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct EventWithoutId { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event_organisation.rs b/src/v1/schemas/event_organisation.rs new file mode 100644 index 0000000..177813c --- /dev/null +++ b/src/v1/schemas/event_organisation.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct EventOrganisation { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event_proposal_email_lock.rs b/src/v1/schemas/event_proposal_email_lock.rs new file mode 100644 index 0000000..98aca14 --- /dev/null +++ b/src/v1/schemas/event_proposal_email_lock.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct EventProposalEmailLock { + lock: bool + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event_report.rs b/src/v1/schemas/event_report.rs new file mode 100644 index 0000000..f36f3ee --- /dev/null +++ b/src/v1/schemas/event_report.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct EventReport { + //TODO, not described in OpenAPI documentation + } +} + +#[test] +fn valid1() { + assert!(true) +} \ No newline at end of file diff --git a/src/v1/schemas/event_tag.rs b/src/v1/schemas/event_tag.rs new file mode 100644 index 0000000..7c2a0dc --- /dev/null +++ b/src/v1/schemas/event_tag.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct EventTag { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/event_tag_id.rs b/src/v1/schemas/event_tag_id.rs new file mode 100644 index 0000000..153228f --- /dev/null +++ b/src/v1/schemas/event_tag_id.rs @@ -0,0 +1,52 @@ +use regex_macro::regex; + + +default_derive!{ + pub struct EventTagId { + id: String + } +} + +impl TryFrom<&str> for EventTagId { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^[[:digit:]]+$"); + if value.len() <= 10 && re.is_match(value) { + Ok(EventTagId { id: value.to_string() }) + } + else { + Err("Failed to parse EventTagId") + } + } +} + +impl Into for EventTagId { + fn into(self) -> String { + self.id + } +} + +#[test] +fn empty() { + let id: Result = "".try_into(); + assert!(id.is_err()) +} + +#[test] +fn oversized() { + let id: Result = "12345678910".try_into(); + assert!(id.is_err()) +} + +#[test] +fn valid1() { + let id: Result = "12345".try_into(); + assert_eq!(id, Ok(EventTagId { id: "12345".to_string() })) +} + +#[test] +fn valid2() { + let id: Result = "0123456789".try_into(); + assert_eq!(id, Ok(EventTagId { id: "0123456789".to_string() })) +} \ No newline at end of file diff --git a/src/v1/schemas/event_tag_list.rs b/src/v1/schemas/event_tag_list.rs new file mode 100644 index 0000000..93c183e --- /dev/null +++ b/src/v1/schemas/event_tag_list.rs @@ -0,0 +1,25 @@ +use super::EventTag; + + +default_derive!{ + pub struct EventTagList { + event_tags: Vec + } +} + +impl From> for EventTagList { + fn from(value: Vec) -> Self { + EventTagList { event_tags: value } + } +} + +impl Into> for EventTagList { + fn into(self) -> Vec { + self.event_tags + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/extended_attribute.rs b/src/v1/schemas/extended_attribute.rs new file mode 100644 index 0000000..fbdae69 --- /dev/null +++ b/src/v1/schemas/extended_attribute.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct ExtendedAttribute { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/full_decaying_model.rs b/src/v1/schemas/full_decaying_model.rs new file mode 100644 index 0000000..5184c86 --- /dev/null +++ b/src/v1/schemas/full_decaying_model.rs @@ -0,0 +1,11 @@ + +default_derive!{ + pub struct FullDecayingModel { + //TODO + } +} + +#[test] +fn valid1() { + todo!() +} \ No newline at end of file diff --git a/src/v1/schemas/mod.rs b/src/v1/schemas/mod.rs new file mode 100644 index 0000000..01b5296 --- /dev/null +++ b/src/v1/schemas/mod.rs @@ -0,0 +1,36 @@ + +expose_submodules!( + attribute_attachment, + attribute_category, + attribute_comment, + attribute_event_uuid, + attribute_id, + attribute_list, + attribute_no_id, + attribute_rest_search_filter, + attribute_rest_search_list_item, + attribute_rest_search_list, + attribute_statistics_response, + attribute_type, + attribute_value, + attribute, + decay_score_list, + decay_score, + decaying_model_parameters, + decaying_model, + describe_attribute_types_response, + event_attribute_count, + event_id, + event_info, + event_no_id, + event_organisation, + event_proposal_email_lock, + event_report, + event_tag_id, + event_tag_list, + event_tag, + event, + extended_attribute, + full_decaying_model, + uuid +); \ No newline at end of file diff --git a/src/v1/schemas/uuid.rs b/src/v1/schemas/uuid.rs new file mode 100644 index 0000000..fc493c3 --- /dev/null +++ b/src/v1/schemas/uuid.rs @@ -0,0 +1,46 @@ +use regex_macro::regex; + + +default_derive!{ + pub struct UUID { + id: String + } +} + +impl TryFrom<&str> for UUID { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + let re = regex!("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + if value.len() <= 36 && re.is_match(value) { + Ok(UUID { id: value.to_string() }) + } + else { + Err("Failed to parse UUID") + } + } +} + +impl Into for UUID { + fn into(self) -> String { + self.id + } +} + +#[test] +fn empty() { + let uuid: Result = "".try_into(); + assert!(uuid.is_err()) +} + +#[test] +fn oversized() { + let uuid: Result = format!("{:>37}", "Test").as_str().try_into(); + assert!(uuid.is_err()) +} + +#[test] +fn valid1() { + let uuid: Result = "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".try_into(); + assert_eq!(uuid, Ok(UUID { id: "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".to_string() })) +}