From 0a6e407f9825c8b36f3d41027aafc44ec769610c Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Wed, 25 Sep 2024 13:11:59 +0200 Subject: [PATCH 1/6] remove elastic support --- Cargo.lock | 1438 +++++------------ Cargo.toml | 18 +- src/bin/es4forensics/cli.rs | 86 - src/bin/es4forensics/main.rs | 108 -- src/bin/evtx2bodyfile/bf_data.rs | 45 +- src/bin/evtx2bodyfile/main.rs | 5 +- src/bin/evtx2bodyfile/output_format.rs | 2 - src/bin/evtx2bodyfile/output_formatter.rs | 9 - src/bin/mactime2/application.rs | 42 +- src/bin/mactime2/output/json_sorter.rs | 89 - src/bin/mactime2/output/mod.rs | 4 +- src/es4forensics/ecs/ecs_builder.rs | 70 - src/es4forensics/ecs/ecs_object.rs | 5 - src/es4forensics/ecs/event.rs | 125 -- src/es4forensics/ecs/file.rs | 101 -- src/es4forensics/ecs/host.rs | 23 - src/es4forensics/ecs/log/event_level.rs | 31 - src/es4forensics/ecs/log/log.rs | 23 - src/es4forensics/ecs/log/mod.rs | 10 - src/es4forensics/ecs/log/severity.rs | 26 - src/es4forensics/ecs/log/syslog.rs | 15 - src/es4forensics/ecs/mod.rs | 25 - src/es4forensics/ecs/objects/ad_object.rs | 21 - src/es4forensics/ecs/objects/macb.rs | 31 - src/es4forensics/ecs/objects/mod.rs | 15 - src/es4forensics/ecs/objects/ntfs_file.rs | 21 - src/es4forensics/ecs/objects/posix_file.rs | 129 -- src/es4forensics/ecs/objects/registry_key.rs | 21 - src/es4forensics/ecs/objects/simple_event.rs | 21 - src/es4forensics/ecs/objects/windows_event.rs | 83 - src/es4forensics/ecs/timeline_object.rs | 31 - src/es4forensics/index.rs | 147 -- src/es4forensics/index_builder.rs | 254 --- src/es4forensics/mod.rs | 90 -- src/es4forensics/protocol.rs | 19 - src/es4forensics/stream_source.rs | 50 - src/es4forensics/timestamp.rs | 61 - src/es4forensics/utils/json.rs | 12 - src/es4forensics/utils/mod.rs | 1 - src/lib.rs | 5 +- 40 files changed, 385 insertions(+), 2927 deletions(-) delete mode 100644 src/bin/es4forensics/cli.rs delete mode 100644 src/bin/es4forensics/main.rs delete mode 100644 src/bin/mactime2/output/json_sorter.rs delete mode 100644 src/es4forensics/ecs/ecs_builder.rs delete mode 100644 src/es4forensics/ecs/ecs_object.rs delete mode 100644 src/es4forensics/ecs/event.rs delete mode 100644 src/es4forensics/ecs/file.rs delete mode 100644 src/es4forensics/ecs/host.rs delete mode 100644 src/es4forensics/ecs/log/event_level.rs delete mode 100644 src/es4forensics/ecs/log/log.rs delete mode 100644 src/es4forensics/ecs/log/mod.rs delete mode 100644 src/es4forensics/ecs/log/severity.rs delete mode 100644 src/es4forensics/ecs/log/syslog.rs delete mode 100644 src/es4forensics/ecs/mod.rs delete mode 100644 src/es4forensics/ecs/objects/ad_object.rs delete mode 100644 src/es4forensics/ecs/objects/macb.rs delete mode 100644 src/es4forensics/ecs/objects/mod.rs delete mode 100644 src/es4forensics/ecs/objects/ntfs_file.rs delete mode 100644 src/es4forensics/ecs/objects/posix_file.rs delete mode 100644 src/es4forensics/ecs/objects/registry_key.rs delete mode 100644 src/es4forensics/ecs/objects/simple_event.rs delete mode 100644 src/es4forensics/ecs/objects/windows_event.rs delete mode 100644 src/es4forensics/ecs/timeline_object.rs delete mode 100644 src/es4forensics/index.rs delete mode 100644 src/es4forensics/index_builder.rs delete mode 100644 src/es4forensics/mod.rs delete mode 100644 src/es4forensics/protocol.rs delete mode 100644 src/es4forensics/stream_source.rs delete mode 100644 src/es4forensics/timestamp.rs delete mode 100644 src/es4forensics/utils/json.rs delete mode 100644 src/es4forensics/utils/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6320020..5aa40f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,19 +3,10 @@ version = 3 [[package]] -name = "addr2line" -version = "0.22.0" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aes" @@ -78,9 +69,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -93,33 +84,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -127,9 +118,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arbitrary" @@ -140,6 +131,12 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -152,59 +149,26 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr", "doc-comment", + "libc", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] -[[package]] -name = "async-compression" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" -dependencies = [ - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - [[package]] name = "base64" version = "0.21.7" @@ -233,6 +197,30 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "binrw" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f36b7cb3ab9ff6a2858650d8dc360e783a5d14dc29594db48c56a3c233cc265" +dependencies = [ + "array-init", + "binrw_derive", + "bytemuck", +] + +[[package]] +name = "binrw_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ea7a8c5c8eeffffac6d54d172444e15beffac6f817fac714460a9a9aa88da3" +dependencies = [ + "either", + "owo-colors", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "binwrite" version = "0.2.1" @@ -240,7 +228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42c3da1db1f384ad643e27cb6ba4f08f2a7cd8753bef8dd95c39fe45d7a474c8" dependencies = [ "binwrite_derive", - "paste", + "paste 0.1.18", ] [[package]] @@ -275,11 +263,20 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bodyfile" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae82cdaf43bd502abaf117ede4c36d876c4bf910c2cb996e1023c62cdfb6c2b5" +dependencies = [ + "duplicate 0.3.0", +] + [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata", @@ -299,16 +296,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] -name = "byteorder" -version = "1.5.0" +name = "bytemuck" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] -name = "bytes" -version = "1.6.1" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bzip2" @@ -333,9 +330,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -357,19 +354,20 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", ] [[package]] name = "cc" -version = "1.1.6" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -427,9 +425,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.9" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -446,9 +444,9 @@ dependencies = [ [[package]] name = "clap-verbosity-flag" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" +checksum = "63d19864d6b68464c59f7162c9914a0b569ddc2926b4a2d71afe62a9738eff53" dependencies = [ "clap", "log", @@ -456,43 +454,43 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] [[package]] name = "clap_complete" -version = "4.5.8" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae" +checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clio" @@ -527,14 +525,14 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "colored" @@ -573,31 +571,21 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "core-foundation" -version = "0.9.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -682,38 +670,14 @@ dependencies = [ "memchr", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -726,19 +690,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.72", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", + "strsim", + "syn 2.0.77", ] [[package]] @@ -747,9 +700,9 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -786,7 +739,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -796,7 +749,7 @@ dependencies = [ "anyhow", "assert-json-diff", "assert_cmd", - "base64 0.21.7", + "base64", "binread", "bitflags 2.6.0", "chrono", @@ -812,16 +765,15 @@ dependencies = [ "csv", "dfirtk-eventdata", "dfirtk-sessionevent-derive", - "duplicate", - "elasticsearch", + "duplicate 1.0.0", "encoding_rs", "encoding_rs_io", "evtx", "exitcode", "flate2", + "flow-record", "forensic-rs", "frnsc-prefetch", - "futures", "getset", "indicatif", "lazy-regex", @@ -833,7 +785,7 @@ dependencies = [ "more-asserts", "nt_hive2", "num", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "ouroboros", "phf", @@ -850,8 +802,6 @@ dependencies = [ "termsize", "thiserror", "time", - "tokio", - "tokio-async-drop", "walkdir", "winstructs", "zip", @@ -864,7 +814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a32cfafa6811b47801a9717057c5538b90bcce906eb841297b41f0ab0867684" dependencies = [ "anyhow", - "darling 0.20.10", + "darling", "evtx", "log", "quote", @@ -878,11 +828,11 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37be4820fae21cbed1f691dffcb02969b673bfeef4bd3fdee8cb175d1ca27023" dependencies = [ - "darling 0.20.10", + "darling", "dfirtk-eventdata", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -923,7 +873,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -934,19 +884,23 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "duplicate" -version = "1.0.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +checksum = "3cdaf23abc9bcc47fda1ae3af68425a22b4dfbd745f9077be0eaad5320f75f52" dependencies = [ - "heck 0.4.1", + "heck 0.3.2", "proc-macro-error", ] [[package]] -name = "dyn-clone" -version = "1.0.17" +name = "duplicate" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", +] [[package]] name = "either" @@ -954,26 +908,6 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -[[package]] -name = "elasticsearch" -version = "8.4.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f5658fb39528771091bb94db73064dae88ddb2f677e036c0faeea7230e93e0" -dependencies = [ - "base64 0.11.0", - "bytes", - "dyn-clone", - "lazy_static", - "percent-encoding", - "reqwest", - "rustc_version", - "serde", - "serde_json", - "serde_with", - "url", - "void", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -1089,9 +1023,9 @@ dependencies = [ [[package]] name = "evtx" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082a513e8ac6685ee6b4a52d6f774a041d3f6497bdeeabed515df2edd207872b" +checksum = "2eaa12db61bdb107becc6f8db2f28390eb791394ea8cbacd2f5f85cead2371df" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -1122,156 +1056,92 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "forensic-rs" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b505c08ffc21e66f9cfca93ebf46f6aef480d806508fd591b7f628eabee45a7" -dependencies = [ - "serde", -] - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "frnsc-prefetch" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe8c029ad817943d73e5ee4b9df54df4540e93d2ab44e9544976381a685f7de" -dependencies = [ - "crc32fast", - "forensic-rs", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" +name = "flow-record" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "12ea19db0135e4a00aa3655e953106e5b8f30b6c2f1f45313836461bc15e79a0" dependencies = [ - "futures-core", - "futures-sink", + "binrw", + "bitflags 2.6.0", + "bodyfile", + "chrono", + "flow-record-common", + "flow-record-derive", + "lazy-regex", + "rmpv", + "sha2", + "strum", ] [[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" +name = "flow-record-common" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "ade1700d676b9117a88e6a9ba18c4f90c420531e4b5fbb12cce99a3b2851dec8" dependencies = [ - "futures-core", - "futures-task", - "futures-util", + "binrw", + "chrono", + "getset", + "proc-macro2", + "quote", + "rmpv", + "sha2", + "strum", + "thiserror", ] [[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" +name = "flow-record-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "a3d39842eeb57e5db8292e1f7bdc03b4fc28f11caf2c8ed3653ce6e4e1cb17ba" dependencies = [ + "chrono", + "darling", + "flow-record-common", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] -name = "futures-sink" -version = "0.3.30" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "futures-task" -version = "0.3.30" +name = "forensic-rs" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "6b505c08ffc21e66f9cfca93ebf46f6aef480d806508fd591b7f628eabee45a7" +dependencies = [ + "serde", +] [[package]] -name = "futures-util" -version = "0.3.30" +name = "frnsc-prefetch" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "76e85e114e5bc0796dac594a82a71af0bbd3ff372bbd4988caad9314a2814c73" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", + "crc32fast", + "forensic-rs", ] [[package]] @@ -1297,47 +1167,22 @@ dependencies = [ [[package]] name = "getset" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" dependencies = [ - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.77", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -1348,6 +1193,15 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -1362,9 +1216,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hmac" @@ -1376,130 +1230,49 @@ dependencies = [ ] [[package]] -name = "http" -version = "0.2.12" +name = "iana-time-zone" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ - "bytes", - "fnv", - "itoa", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "cc", ] [[package]] -name = "httparse" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" - -[[package]] -name = "httpdate" -version = "1.0.3" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "hyper" -version = "0.14.30" +name = "indexmap" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", + "equivalent", + "hashbrown", ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "indicatif" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "2.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "indicatif" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -1532,17 +1305,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi", "libc", @@ -1551,9 +1318,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -1572,27 +1339,27 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy-regex" -version = "3.1.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -1601,14 +1368,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.1.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -1619,9 +1386,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "linux-raw-sys" @@ -1643,16 +1410,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -1705,12 +1462,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1719,22 +1470,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.11" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", + "adler2", ] [[package]] @@ -1743,23 +1483,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "7.1.3" @@ -1852,7 +1575,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -1895,16 +1618,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.7" @@ -1920,65 +1633,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -[[package]] -name = "object" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ouroboros" version = "0.18.4" @@ -2001,31 +1661,14 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] -name = "parking_lot" -version = "0.12.3" +name = "owo-colors" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parse-zoneinfo" @@ -2046,6 +1689,12 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "paste-impl" version = "0.1.18" @@ -2065,12 +1714,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - [[package]] name = "phf" version = "0.11.2" @@ -2109,29 +1752,17 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" [[package]] name = "powerfmt" @@ -2141,15 +1772,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "predicates" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "difflib", @@ -2158,15 +1792,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -2196,6 +1830,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -2219,7 +1875,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", "version_check", "yansi 1.0.1", ] @@ -2237,18 +1893,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2303,20 +1959,11 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2342,67 +1989,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] -name = "reqwest" -version = "0.11.27" +name = "rmp" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ - "async-compression", - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", + "byteorder", + "num-traits", + "paste 1.0.15", ] [[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc_version" -version = "0.2.3" +name = "rmpv" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "58450723cd9ee93273ce44a20b6ec4efe17f8ed2e3631474387bfdecf18bb2a9" dependencies = [ - "semver 0.9.0", + "num-traits", + "rmp", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -2411,15 +2022,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustversion" version = "1.0.17" @@ -2441,53 +2043,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.23" @@ -2497,77 +2052,39 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "form_urlencoded", + "indexmap", "itoa", + "memchr", "ryu", "serde", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling 0.13.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sha1" version = "0.10.6" @@ -2597,13 +2114,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] -name = "signal-hook-registry" -version = "1.4.2" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sigpipe" @@ -2652,43 +2166,12 @@ dependencies = [ "walkdir", ] -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -2714,7 +2197,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -2736,52 +2219,26 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2832,22 +2289,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -2883,111 +2340,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-async-drop" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e57fbf1da3f18c8a95469f8973c138b0a99f4ae761885c3646b0c61139b0522" - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typenum" version = "1.17.0" @@ -3003,43 +2355,23 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] -name = "unicode-normalization" -version = "0.1.23" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "utf8parse" @@ -3047,23 +2379,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" @@ -3084,15 +2404,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3101,46 +2412,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3148,32 +2448,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "winapi" @@ -3193,11 +2483,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3248,6 +2538,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3411,16 +2710,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winstructs" version = "0.3.2" @@ -3456,6 +2745,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -3467,7 +2757,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] @@ -3487,14 +2777,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.77", ] [[package]] name = "zip" -version = "2.1.5" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b895748a3ebcb69b9d38dcfdf21760859a4b0d0b0015277640c2ef4c69640e6f" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "aes", "arbitrary", @@ -3544,18 +2834,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 786b2e3..fdb60c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,6 @@ name = "pol_export" path = "src/bin/pol_export/main.rs" required-features = ["pol_export"] -[[bin]] -name = "es4forensics" -path = "src/bin/es4forensics/main.rs" -required-features = ["elastic"] - [[bin]] name = "regdump" path = "src/bin/regdump/main.rs" @@ -98,15 +93,14 @@ required-features = ["zip2bodyfile"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["pol_export", "mactime2", "evtxtools", "regdump", "hivescan", "cleanhive", "ipgrep", "ts2date", "lnk2bodyfile", "pf2bodyfile", "zip2bodyfile"] -mactime2 = ["gzip", "elastic", "chrono-tz", "thiserror", "bitflags", "encoding_rs_io", "color-print"] +mactime2 = ["gzip", "chrono-tz", "thiserror", "bitflags", "encoding_rs_io", "color-print", "strum", "strum_macros"] gzip = ["flate2"] -elastic = ["elasticsearch", "tokio", "futures", "serde_json", "sha2", "base64", "num-traits", "num-derive", "strum", "strum_macros", "tokio-async-drop"] evtxtools = ["evtxscan", "evtxcat", "evtxls", "evtxanalyze", "evtx2bodyfile"] pol_export = [] evtxscan = ["evtx"] evtxcat = ["evtx", "colored_json", "term-table", "termsize"] evtxls = ["evtx", "colored", "lazy-regex", "regex", "sigpipe", "dfirtk-eventdata"] -evtxanalyze = ["evtx", "dfirtk-sessionevent-derive", "dfirtk-eventdata", "exitcode", "walkdir"] +evtxanalyze = ["evtx", "dfirtk-sessionevent-derive", "dfirtk-eventdata", "exitcode", "walkdir", "serde_json"] evtx2bodyfile = ["evtx", "getset", "ouroboros", "indicatif"] ipgrep = [] ts2date = ["regex"] @@ -126,6 +120,7 @@ clap = {version = "4.5", features = ["derive", "wrap_help", "cargo"] } clap-verbosity-flag = "2.0.0" csv = "1.2.2" encoding_rs = "0.8" +flow-record = "0.4.2" ## setting release_max_level_info conflicts with evtx # log = {version = "0.4", features = [ "release_max_level_info" ]} @@ -172,13 +167,6 @@ ouroboros = {version="0.18", optional=true} # bodyfile, es4forensics duplicate = "1" -# es4forensics -# requires libssl-dev -elasticsearch = {version="8.4.0-alpha.1", optional=true} -tokio = { version = "1", features = ["full"], optional=true } -tokio-async-drop = {version="0", optional=true} -futures = {version="0.3", optional=true } - sha2 = {version="0.10", optional=true} base64 = {version="0.21", optional=true} num-traits = {version="0.2", optional=true} diff --git a/src/bin/es4forensics/cli.rs b/src/bin/es4forensics/cli.rs deleted file mode 100644 index b118e30..0000000 --- a/src/bin/es4forensics/cli.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::Protocol; -use clap::{Parser, ValueHint}; -use clio::Input; -use dfir_toolkit::common::HasVerboseFlag; -use log::LevelFilter; - -#[cfg(feature = "gzip")] -const INPUTFILE_HELP: &str = - "path to input file or '-' for stdin (files ending with .gz will be treated as being gzipped)"; -#[cfg(not(feature = "gzip"))] -const INPUTFILE_HELP: &str = "path to input file or '-' for stdin"; - -#[derive(clap::Subcommand, Clone)] -pub(crate) enum Action { - // create a new index - CreateIndex, - - // import timeline data - Import { - #[clap(default_value="-", help=INPUTFILE_HELP, value_hint=ValueHint::FilePath)] - input_file: Input, - - /// number of timeline entries to combine in one bulk operation - #[clap(long("bulk-size"), default_value_t = 1000)] - bulk_size: usize, - }, -} - -/// This crates provides structs and functions to insert timeline data into an elasticsearch index. -#[derive(Parser)] -#[clap(name=env!("CARGO_BIN_NAME"), author, version, long_about = None)] -pub struct Cli { - #[command(subcommand)] - pub(crate) action: Action, - - /// strict mode: do not only warn, but abort if an error occurs - #[clap(long("strict"), display_order(500))] - pub(crate) strict_mode: bool, - - /// name of the elasticsearch index - #[clap(short('I'), long("index"), display_order = 800)] - pub(crate) index_name: String, - - /// server name or IP address of elasticsearch server - #[clap( - short('H'), - long("host"), - display_order = 810, - default_value = "localhost" - )] - pub(crate) host: String, - - /// API port number of elasticsearch server - #[clap(short('P'), long("port"), display_order = 820, default_value_t = 9200)] - pub(crate) port: u16, - - /// protocol to be used to connect to elasticsearch - #[clap(long("proto"), display_order=830, default_value_t=Protocol::Https)] - pub(crate) protocol: Protocol, - - /// omit certificate validation - #[clap( - short('k'), - long("insecure"), - display_order = 840, - default_value_t = false - )] - pub(crate) omit_certificate_validation: bool, - - /// username for elasticsearch server - #[clap(short('U'), long("username"), display_order=850, default_value=Some("elastic"))] - pub(crate) username: String, - - /// password for authenticating at elasticsearch - #[clap(short('W'), long("password"), display_order = 860)] - pub(crate) password: String, - - #[clap(flatten)] - pub(crate) verbose: clap_verbosity_flag::Verbosity, -} - -impl HasVerboseFlag for Cli { - fn log_level_filter(&self) -> LevelFilter { - self.verbose.log_level_filter() - } -} diff --git a/src/bin/es4forensics/main.rs b/src/bin/es4forensics/main.rs deleted file mode 100644 index c8088fb..0000000 --- a/src/bin/es4forensics/main.rs +++ /dev/null @@ -1,108 +0,0 @@ -mod cli; - - -use std::io::BufRead; -use anyhow::{Result, anyhow}; - -use cli::{Cli, Action}; -use elasticsearch::auth::Credentials; -use dfir_toolkit::es4forensics::*; -use dfir_toolkit::common::{FancyParser, FileInput}; - -#[tokio::main] -async fn main() -> Result<()> { - let cli: Cli = Cli::parse_cli(); - - let action = cli.action.clone(); - let e4f: Es4Forensics = cli.into(); - e4f.run(action).await -} - -struct Es4Forensics { - strict_mode: bool, - index_name: String, - host: String, - port: u16, - protocol: Protocol, - omit_certificate_validation: bool, - username: String, - password: String, -} - -impl Es4Forensics { - pub async fn run(self, action: Action) -> Result<()> { - - let builder = self.create_index_builder()?; - - match action { - Action::CreateIndex => { - if builder.index_exists().await? { - return Err(anyhow!("index '{}' exists already", self.index_name)); - } - builder.create_index().await?; - Ok(()) - } - Action::Import{input_file, bulk_size} => { - self.import(builder, input_file.into(), bulk_size).await - } - } - } - - async fn import(&self, builder: IndexBuilder, reader: FileInput, bulk_size: usize) -> Result<()> { - let mut index = builder.connect().await?; - index.set_cache_size(bulk_size).await?; - - for line in reader.lines() { - let line = line?; - let value = match serde_json::from_str(&line) { - Ok(v) => v, - Err(why) => { - if self.strict_mode { - return Err(anyhow!(why)) - } else { - ::log::error!("error while parsing: {}", why); - ::log::error!("failed JSON was: {}", line); - continue; - } - } - }; - - index.add_bulk_document(value).await?; - } - index.flush().await?; - Ok(()) - } - - fn create_index_builder(&self) -> Result { - let mut builder = IndexBuilder::with_name(self.index_name.clone()) - .with_host(self.host.clone()) - .with_port(self.port) - .with_credentials(Credentials::Basic( - self.username.clone(), - self.password.clone(), - )) - .with_protocol(self.protocol.clone()); - - if self.omit_certificate_validation { - ::log::warn!("disabling certificate validation"); - builder = builder.without_certificate_validation(); - } - - Ok(builder) - } -} - -impl From for Es4Forensics { - fn from(cli: Cli) -> Self { - Self { - strict_mode: cli.strict_mode, - host: cli.host.clone(), - port: cli.port, - username: cli.username.clone(), - password: cli.password.clone(), - index_name: cli.index_name.clone(), - protocol: cli.protocol.clone(), - omit_certificate_validation: cli.omit_certificate_validation, - } - } -} \ No newline at end of file diff --git a/src/bin/evtx2bodyfile/bf_data.rs b/src/bin/evtx2bodyfile/bf_data.rs index e6d0762..7f3a059 100644 --- a/src/bin/evtx2bodyfile/bf_data.rs +++ b/src/bin/evtx2bodyfile/bf_data.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; -use anyhow::{anyhow, bail, Result}; +use anyhow::Result; use chrono::{DateTime, Utc}; use dfir_toolkit::common::bodyfile::{Bodyfile3Line, Modified}; -use dfir_toolkit::es4forensics::{objects::WindowsEvent, TimelineObject}; use evtx::SerializedEvtxRecord; use getset::{Getters, Setters}; use serde::Serialize; @@ -23,10 +22,6 @@ pub(crate) struct BfData<'a> { channel_name: &'a Value, activity_id: Option<&'a Value>, custom_data: HashMap<&'a String, &'a Value>, - - #[serde(skip)] - #[getset(set = "pub (crate)")] - enable_json_output: bool, } impl<'a> BfData<'a> { @@ -36,26 +31,13 @@ impl<'a> BfData<'a> { .with_owned_name(json!(self).to_string()); Ok(bf_line.to_string()) } - - pub(crate) fn try_into_json(&self) -> Result { - let event: WindowsEvent = self.try_into()?; - let values: Vec = event.into_values().collect(); - if values.len() != 1 { - bail!("this event resolved to an invalid number of JSON documents"); - } - serde_json::to_string(&values[0]).map_err(|why| anyhow!(why)) - } } impl<'a> TryFrom<&BfData<'a>> for String { type Error = anyhow::Error; fn try_from(value: &BfData<'a>) -> Result { - if value.enable_json_output { - value.try_into_json() - } else { - value.try_into_mactime() - } + value.try_into_mactime() } } @@ -111,29 +93,6 @@ impl<'a> TryFrom<&'a SerializedEvtxRecord> for BfData<'a> { channel_name, activity_id, custom_data, - enable_json_output: false, }) } } - -impl<'a> TryFrom<&BfData<'a>> for WindowsEvent<'a> { - type Error = anyhow::Error; - - fn try_from(bfdata: &BfData<'a>) -> Result { - let event_id = match bfdata.event_id.as_u64() { - Some(id) => id, - _ => bail!("invalid event id: {:?}", bfdata.event_id), - }; - Ok(WindowsEvent::new( - bfdata.event_record_id, - bfdata.timestamp, - event_id, - bfdata.level.try_into()?, - bfdata.computer, - bfdata.provider_name, - bfdata.channel_name, - bfdata.activity_id, - bfdata.custom_data.clone(), - )) - } -} diff --git a/src/bin/evtx2bodyfile/main.rs b/src/bin/evtx2bodyfile/main.rs index db283c3..69b6680 100644 --- a/src/bin/evtx2bodyfile/main.rs +++ b/src/bin/evtx2bodyfile/main.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Result}; use cli::Cli; use dfir_toolkit::common::FancyParser; use evtx_file::EvtxFile; -use output_formatter::{BodyfileOutputFormatter, JsonOutputFormatter}; +use output_formatter::BodyfileOutputFormatter; mod bf_data; mod cli; @@ -25,9 +25,6 @@ fn main() -> Result<()> { for input in cli.evtx_files().iter() { let file = EvtxFile::from(input); match cli.format() { - output_format::OutputFormat::Json => { - file.print_records(JsonOutputFormatter, !cli.strict())? - } output_format::OutputFormat::Bodyfile => { file.print_records(BodyfileOutputFormatter, !cli.strict())? } diff --git a/src/bin/evtx2bodyfile/output_format.rs b/src/bin/evtx2bodyfile/output_format.rs index 156289d..022fff1 100644 --- a/src/bin/evtx2bodyfile/output_format.rs +++ b/src/bin/evtx2bodyfile/output_format.rs @@ -3,8 +3,6 @@ use strum_macros::Display; #[derive(ValueEnum, Clone, Display)] pub(crate) enum OutputFormat { - #[strum(serialize = "json")] - Json, #[strum(serialize = "bodyfile")] Bodyfile, diff --git a/src/bin/evtx2bodyfile/output_formatter.rs b/src/bin/evtx2bodyfile/output_formatter.rs index 7bcf183..bc4eaa4 100644 --- a/src/bin/evtx2bodyfile/output_formatter.rs +++ b/src/bin/evtx2bodyfile/output_formatter.rs @@ -3,8 +3,6 @@ use serde_json::Value; use crate::bf_data::BfData; -#[derive(Default)] -pub(crate) struct JsonOutputFormatter; #[derive(Default)] pub(crate) struct BodyfileOutputFormatter; @@ -12,13 +10,6 @@ pub(crate) trait OutputFormatter { fn record_to_string(&self, record: &SerializedEvtxRecord) -> anyhow::Result; } -impl OutputFormatter for JsonOutputFormatter { - fn record_to_string(&self, record: &SerializedEvtxRecord) -> anyhow::Result { - let bf_data = BfData::try_from(record)?; - bf_data.try_into_json() - } -} - impl OutputFormatter for BodyfileOutputFormatter { fn record_to_string(&self, record: &SerializedEvtxRecord) -> anyhow::Result { let bf_data = BfData::try_from(record)?; diff --git a/src/bin/mactime2/application.rs b/src/bin/mactime2/application.rs index e925a21..5e369f0 100644 --- a/src/bin/mactime2/application.rs +++ b/src/bin/mactime2/application.rs @@ -9,17 +9,13 @@ use super::bodyfile::{BodyfileDecoder, BodyfileReader, BodyfileSorter}; use super::cli::Cli; use super::error::MactimeError; use super::filter::{Consumer, Joinable, Provider, RunOptions, Sorter}; -use super::output::{CsvOutput, JsonSorter, TxtOutput}; +use super::output::{CsvOutput, TxtOutput}; use super::stream::StreamReader; #[derive(ValueEnum, Clone, Display)] enum InputFormat { #[strum(serialize = "bodyfile")] Bodyfile, - - #[cfg(feature = "elastic")] - #[strum(serialize = "json")] - Json, } #[derive(ValueEnum, Clone, Display)] @@ -36,11 +32,6 @@ pub(crate) enum OutputFormat { #[strum(serialize = "json")] Json, - /// JSON-format to be used by elasticsearch - #[cfg(feature = "elastic")] - #[strum(serialize = "elastic")] - Elastic, - /// Use the old (non RFC compliant) CSV format that was used by legacy mactime. #[strum(serialize = "old-csv")] OldCsv, @@ -63,27 +54,20 @@ impl Mactime2Application { strict_mode: self.strict_mode, }; - if matches!(self.format, OutputFormat::Json) { - Box::new(JsonSorter::with_receiver(decoder.get_receiver(), options)) - } else { - let mut sorter = - BodyfileSorter::default().with_receiver(decoder.get_receiver(), options); + let mut sorter = BodyfileSorter::default().with_receiver(decoder.get_receiver(), options); - sorter = sorter.with_output(match self.format { - OutputFormat::OldCsv => { - Box::new(OldCsvOutput::new(std::io::stdout(), self.dst_zone)) - } + sorter = sorter.with_output(match self.format { + OutputFormat::OldCsv => Box::new(OldCsvOutput::new(std::io::stdout(), self.dst_zone)), - OutputFormat::Csv => Box::new(CsvOutput::new( - std::io::stdout(), - self.dst_zone, - self.show_headers, - )), - OutputFormat::Txt => Box::new(TxtOutput::new(std::io::stdout(), self.dst_zone)), - _ => panic!("invalid execution path"), - }); - Box::new(sorter) - } + OutputFormat::Csv => Box::new(CsvOutput::new( + std::io::stdout(), + self.dst_zone, + self.show_headers, + )), + OutputFormat::Txt => Box::new(TxtOutput::new(std::io::stdout(), self.dst_zone)), + _ => panic!("invalid execution path"), + }); + Box::new(sorter) } pub fn run(&self) -> anyhow::Result<()> { diff --git a/src/bin/mactime2/output/json_sorter.rs b/src/bin/mactime2/output/json_sorter.rs deleted file mode 100644 index a80b754..0000000 --- a/src/bin/mactime2/output/json_sorter.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::{ - borrow::Borrow, - collections::{BTreeMap, BTreeSet}, - sync::{mpsc::Receiver, Arc}, - thread::JoinHandle, -}; - -use dfir_toolkit::{ - common::bodyfile::Bodyfile3Line, - es4forensics::{objects::PosixFile, Timestamp, TimelineObject}, -}; -use std::convert::TryFrom; - -use crate::{ - error::MactimeError, - filter::RunOptions, - filter::{Consumer, Joinable, Runnable, Sorter}, -}; -pub struct JsonSorter { - worker: Option>>, - receiver: Option>, -} - -impl Joinable> for JsonSorter { - fn join(&mut self) -> std::thread::Result> { - self.worker.take().unwrap().join() - } -} - -impl Consumer for JsonSorter { - fn with_receiver(previous: Receiver, _options: RunOptions) -> Self { - Self { - receiver: Some(previous), - worker: None, - } - } -} - -impl Runnable for JsonSorter { - fn run(&mut self) { - let receiver = self - .receiver - .take() - .expect("no receiver provided; please call with_receiver()"); - self.worker = Some(std::thread::spawn(move || { - Self::json_worker(receiver) - })); - } -} - -impl Sorter> for JsonSorter {} - -impl JsonSorter { - fn json_worker(decoder: Receiver) -> Result<(), MactimeError> { - let mut entries: BTreeMap> = BTreeMap::new(); - loop { - let line = Arc::new(match decoder.recv() { - Err(_) => { - break; - } - Ok(l) => l, - }); - - let bfline: &Bodyfile3Line = line.borrow(); - let pf = PosixFile::try_from(bfline).unwrap(); - - let lines: Vec<(Timestamp, String)> = pf - .into_tuples() - .map(|(t, v)| (t, serde_json::to_string(&v).unwrap())) - .collect(); - - if lines.is_empty() { - log::warn!("file {} has no timestamp entries", line.get_name()); - log::warn!("raw entry is {}", line.to_string()); - } else { - for (ts, line) in lines { - entries.entry(ts).or_default().insert(line); - } - } - } - - for lines in entries.into_values() { - for line in lines { - println!("{}", line); - } - } - Ok(()) - } -} diff --git a/src/bin/mactime2/output/mod.rs b/src/bin/mactime2/output/mod.rs index c6b8115..b587893 100644 --- a/src/bin/mactime2/output/mod.rs +++ b/src/bin/mactime2/output/mod.rs @@ -1,9 +1,7 @@ mod csv_output; mod old_csv_output; mod txt_output; -mod json_sorter; pub (crate) use csv_output::*; pub (crate) use old_csv_output::*; -pub (crate) use txt_output::*; -pub (crate) use json_sorter::*; \ No newline at end of file +pub (crate) use txt_output::*; \ No newline at end of file diff --git a/src/es4forensics/ecs/ecs_builder.rs b/src/es4forensics/ecs/ecs_builder.rs deleted file mode 100644 index 6d88c8d..0000000 --- a/src/es4forensics/ecs/ecs_builder.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use anyhow::bail; -use duplicate::duplicate_item; -use serde_json::{json, Value}; - -use crate::es4forensics::ecs::ecs_object::EcsObject; -use crate::es4forensics::ecs::{log::Log, Event, File, Host}; -use crate::es4forensics::timestamp::Timestamp; - -pub struct EcsBuilder { - ts: Timestamp, - message: String, - //labels: HashMap, - tags: HashSet, - contents: HashMap<&'static str, Value>, -} - -impl EcsBuilder { - pub fn new(message: String, ts: Timestamp) -> Self { - Self { - ts, - message, - tags: HashSet::default(), - contents: HashMap::default(), - } - } - - pub fn with_additional_tag(mut self, tag: &str) -> Self { - self.tags.insert(tag.to_owned()); - self - } - - #[duplicate_item( - method ret_type; - [ with_event ] [ Event<'_> ]; - [ with_host ] [ Host ]; - [ with_log ] [ Log ]; - [ with_file ] [ File ]; - )] - pub fn method(mut self, ts: ret_type) -> anyhow::Result { - if self.contents.contains_key(ts.object_key()) { - bail!("unambigious key: '{}'", ts.object_key()); - } - self.contents.insert(ts.object_key(), json!(ts)); - Ok(self) - } -} - -impl From for (Timestamp, Value) { - fn from(val: EcsBuilder) -> (Timestamp, Value) { - let mut m = HashMap::from([ - ( - "@timestamp", - Value::Number(val.ts.timestamp_millis().into()), - ), - ("ecs", json!({"version": "8.4"})), - ("message", json!(val.message)), - ]); - - if !val.tags.is_empty() { - m.insert("tags", json!(val.tags)); - } - - for (key, value) in val.contents.into_iter() { - m.insert(key, value); - } - (val.ts, json!(m)) - } -} diff --git a/src/es4forensics/ecs/ecs_object.rs b/src/es4forensics/ecs/ecs_object.rs deleted file mode 100644 index 45c78ea..0000000 --- a/src/es4forensics/ecs/ecs_object.rs +++ /dev/null @@ -1,5 +0,0 @@ -use serde::Serialize; - -pub trait EcsObject: Serialize { - fn object_key(&self) -> &'static str; -} \ No newline at end of file diff --git a/src/es4forensics/ecs/event.rs b/src/es4forensics/ecs/event.rs deleted file mode 100644 index 2921ebe..0000000 --- a/src/es4forensics/ecs/event.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::collections::HashMap; - -use duplicate::duplicate_item; -use serde::Serialize; -use serde_json::Value; - -use super::{CustomizableField, ecs_object::EcsObject}; - -#[derive(Serialize)] -#[derive(Default)] -pub enum Kind { - Alert, - Enrichment, - #[default] - Event, - Metric, - State, - PipelineError, - Signal, -} - - - -#[derive(Serialize)] -pub enum Category { - Authentication, - Configuration, - Database, - Driver, - Email, - File, - Host, - Iam, - IntrusionDetection, - Malware, - Network, - Package, - Process, - Registry, - Session, - Threat, - Web, -} - -#[derive(Serialize)] -pub enum Type { - Access, - Admin, - Allowed, - Change, - Connection, - Creation, - Deletion, - Denied, - End, - Error, - Group, - Indicator, - Info, - Installation, - Protocol, - Start, - User, -} - -#[derive(Serialize)] -pub enum Outcome { - Failure, - Success, - Unknown, -} - -#[derive(Default, Serialize)] -pub struct Event<'a> { - event_kind: Kind, - event_category: Option, - event_type: Option, - event_outcome: Option, - code: Option, - activity: Option, - sequence: Option, - module: Option, - provider: Option, - severity: Option, - custom_data: HashMap<&'a String, &'a Value>, -} - -impl<'a> Event<'a> { - pub fn with_kind(mut self, ts: Kind) -> Self { - self.event_kind = ts; - self - } - - #[duplicate_item( - method attribute ret_type; - [ with_category ] [ event_category ] [ Category ]; - [ with_type ] [ event_type ] [ Type ]; - [ with_outcome ] [ event_outcome ] [ Outcome ]; - [ with_code ] [ code ] [ u64 ]; - [ with_sequence ] [ sequence ] [ String ]; - [ with_module ] [ module ] [ String ]; - [ with_provider ] [ provider ] [ String ]; - [ with_severity ] [ severity ] [ u8 ]; - )] - pub fn method(mut self, ts: ret_type) -> Self { - self.attribute = Some(ts); - self - } -} - -impl<'a> EcsObject for Event<'a> { - fn object_key(&self) -> &'static str { - "event" - } -} - -impl<'a> CustomizableField<'a> for Event<'a> { - fn with_custom_data( - mut self, - custom_data: &HashMap<&'a String, &'a serde_json::Value>, - ) -> Self { - self.custom_data.extend(custom_data); - self - } -} diff --git a/src/es4forensics/ecs/file.rs b/src/es4forensics/ecs/file.rs deleted file mode 100644 index f83b2f4..0000000 --- a/src/es4forensics/ecs/file.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::path::PathBuf; - -use duplicate::duplicate_item; -use serde::Serialize; - -use crate::es4forensics::timestamp::Timestamp; - -use super::ecs_object::EcsObject; - -#[derive(Serialize)] -pub enum FileType { - File, - Dir, - Symlink, -} - -#[derive(Serialize, Default)] -pub struct File { - #[serde(skip_serializing_if = "Option::is_none")] - mtime: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - accessed: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - ctime: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - created: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - directory: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - extension: Option, - gid: u64, - uid: u64, - inode: String, - mode: String, - - #[serde(skip_serializing_if = "Option::is_none")] - name: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - path: Option, - size: u64, - - #[serde(skip_serializing_if = "Option::is_none")] - target_path: Option, - - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - file_type: Option, -} - -impl From for File { - fn from(filename: String) -> Self { - let buf = PathBuf::from(&filename); - Self { - name: buf.file_name().map(|s| s.to_string_lossy().to_string()), - extension: buf.extension().map(|s| s.to_string_lossy().to_string()), - directory: buf.parent().map(|s| s.to_string_lossy().to_string()), - path: Some(filename), - ..Default::default() - } - } -} - -impl File { - #[duplicate_item( - method attribute ret_type; - [ with_mtime ] [ mtime ] [ Timestamp ]; - [ with_accessed ] [ accessed ] [ Timestamp ]; - [ with_ctime ] [ ctime ] [ Timestamp ]; - [ with_created ] [ created ] [ Timestamp ]; - [ with_target_path ] [ target_path ] [ String ]; - [ with_type ] [ file_type ] [ FileType ]; - )] - pub fn method(mut self, ts: Option) -> Self { - self.attribute = ts; - self - } - - #[duplicate_item( - method attribute ret_type; - [ with_gid ] [ gid ] [ u64 ]; - [ with_uid ] [ uid ] [ u64 ]; - [ with_inode ] [ inode ] [ String ]; - [ with_mode ] [ mode ] [ String ]; - [ with_size ] [ size ] [ u64 ]; - )] - pub fn method(mut self, ts: ret_type) -> Self { - self.attribute = ts; - self - } -} - -impl EcsObject for File { - fn object_key(&self) -> &'static str { - "file" - } -} diff --git a/src/es4forensics/ecs/host.rs b/src/es4forensics/ecs/host.rs deleted file mode 100644 index de2628b..0000000 --- a/src/es4forensics/ecs/host.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::Serialize; -use serde_json::Value; - -use super::ecs_object::EcsObject; - -#[derive(Serialize)] -pub struct Host { - name: Value -} - -impl EcsObject for Host { - fn object_key(&self) -> &'static str { - "host" - } -} - -impl From<&Value> for Host { - fn from(val: &Value) -> Self { - Self { - name: val.clone() - } - } -} diff --git a/src/es4forensics/ecs/log/event_level.rs b/src/es4forensics/ecs/log/event_level.rs deleted file mode 100644 index 8de203c..0000000 --- a/src/es4forensics/ecs/log/event_level.rs +++ /dev/null @@ -1,31 +0,0 @@ -use anyhow::bail; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::FromPrimitive; -use serde::Serialize; -use serde_json::Value; - -/// Source: -#[derive(strum_macros::Display, FromPrimitive, ToPrimitive, Serialize)] -#[strum(serialize_all = "lowercase")] -pub enum EventLevel { - LogAlways = 0, - Critical = 1, - Error = 2, - Warning = 3, - Information = 4, - Verbose = 5, -} - -impl TryFrom<&Value> for EventLevel { - type Error = anyhow::Error; - - fn try_from(value: &Value) -> Result { - match value.as_u64() { - None => bail!("unable to convert '{value}' into u8"), - Some(n) => match Self::from_u64(n) { - None => bail!("invalid numeric value: '{n}'"), - Some(me) => Ok(me) - } - } - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/log/log.rs b/src/es4forensics/ecs/log/log.rs deleted file mode 100644 index b142510..0000000 --- a/src/es4forensics/ecs/log/log.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::Serialize; - -use crate::es4forensics::ecs::ecs_object::EcsObject; - -use super::Syslog; - -#[derive(Serialize, Default)] -pub struct Log { - syslog: Option -} - -impl Log { - pub fn with_syslog(mut self, syslog: Syslog) -> Self { - self.syslog = Some(syslog); - self - } -} - -impl EcsObject for Log { - fn object_key(&self) -> &'static str { - "log" - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/log/mod.rs b/src/es4forensics/ecs/log/mod.rs deleted file mode 100644 index 705dddd..0000000 --- a/src/es4forensics/ecs/log/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod event_level; -mod severity; -mod syslog; -#[allow(clippy::module_inception)] -mod log; - -pub use event_level::*; -pub use self::log::*; -pub use severity::*; -pub use syslog::*; \ No newline at end of file diff --git a/src/es4forensics/ecs/log/severity.rs b/src/es4forensics/ecs/log/severity.rs deleted file mode 100644 index f3622da..0000000 --- a/src/es4forensics/ecs/log/severity.rs +++ /dev/null @@ -1,26 +0,0 @@ -use serde::Serialize; -use num_traits::ToPrimitive; -use std::string::ToString; - -use super::EventLevel; - -#[derive(Serialize)] -pub struct Severity { - code: u8, - name: String -} - -impl From for Severity { - fn from(level: EventLevel) -> Self { - Self { - code: level.to_u8().unwrap(), - name: level.to_string() - } - } -} - -impl Default for Severity { - fn default() -> Self { - Self::from(EventLevel::Information) - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/log/syslog.rs b/src/es4forensics/ecs/log/syslog.rs deleted file mode 100644 index fd91608..0000000 --- a/src/es4forensics/ecs/log/syslog.rs +++ /dev/null @@ -1,15 +0,0 @@ -use serde::Serialize; - -use super::Severity; - -#[derive(Serialize, Default)] -pub struct Syslog { - severity: Severity -} - -impl Syslog { - pub fn with_severity(mut self, severity: Severity) -> Self { - self.severity = severity; - self - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/mod.rs b/src/es4forensics/ecs/mod.rs deleted file mode 100644 index e85e4df..0000000 --- a/src/es4forensics/ecs/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -mod event; -mod host; -pub mod log; -mod file; -mod ecs_builder; -mod ecs_object; -mod timeline_object; -pub mod objects; -pub use ecs_builder::*; -pub use event::*; -pub use host::*; -pub use file::*; -pub use timeline_object::TimelineObject; - -use std::collections::HashMap; - -use serde_json::Value; - -pub trait ECSFields { - fn into(&self) -> Value; -} - -pub trait CustomizableField<'a> { - fn with_custom_data(self, custom_data: &HashMap<&'a String, &'a Value>) -> Self; -} diff --git a/src/es4forensics/ecs/objects/ad_object.rs b/src/es4forensics/ecs/objects/ad_object.rs deleted file mode 100644 index 2ae1d63..0000000 --- a/src/es4forensics/ecs/objects/ad_object.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; - -use serde::{Serialize, Deserialize}; -use serde_json::Value; - -use crate::es4forensics::{timestamp::Timestamp, utils::json::add_to_json}; - -#[derive(Serialize, Deserialize)] -pub struct ADObject { - -} - -impl ADObject { - #[allow(dead_code)] - pub fn documents(&self) -> impl Iterator { - let docs: HashMap = HashMap::new(); - docs.into_iter().map(|(ts, v)| { - add_to_json(&v, "|@timestamp|", Value::Number(ts.timestamp_millis().into())) - }) - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/macb.rs b/src/es4forensics/ecs/objects/macb.rs deleted file mode 100644 index 1d3d2b0..0000000 --- a/src/es4forensics/ecs/objects/macb.rs +++ /dev/null @@ -1,31 +0,0 @@ - -#[derive(Default)] -pub struct Macb { - pub modified: bool, - pub accessed: bool, - pub changed: bool, - pub created: bool, -} - -impl From<&Macb> for String { - fn from(me: &Macb) -> Self { - let mut macb = ['.', '.', '.', '.']; - if me.modified { macb[0] = 'm'; } - if me.accessed { macb[1] = 'a'; } - if me.changed { macb[2] = 'c'; } - if me.created { macb[3] = 'b'; } - - macb.into_iter().collect() - } -} - -impl From<&Macb> for Vec<&str> { - fn from(me: &Macb) -> Self { - let mut res = Vec::new(); - if me.modified { res.push("modified"); } - if me.accessed { res.push("accessed"); } - if me.changed { res.push("changed"); } - if me.created { res.push("created"); } - res - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/mod.rs b/src/es4forensics/ecs/objects/mod.rs deleted file mode 100644 index 6358571..0000000 --- a/src/es4forensics/ecs/objects/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod ad_object; -mod registry_key; -mod windows_event; -mod posix_file; -mod ntfs_file; -mod simple_event; -mod macb; - -pub use ad_object::*; -pub use registry_key::*; -pub use windows_event::*; -pub use posix_file::*; -pub use ntfs_file::*; -pub use simple_event::*; -pub use macb::*; diff --git a/src/es4forensics/ecs/objects/ntfs_file.rs b/src/es4forensics/ecs/objects/ntfs_file.rs deleted file mode 100644 index e41a33e..0000000 --- a/src/es4forensics/ecs/objects/ntfs_file.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; - -use serde::{Serialize, Deserialize}; -use serde_json::Value; - -use crate::es4forensics::{timestamp::Timestamp, utils::json::add_to_json}; - -#[derive(Serialize, Deserialize)] -pub struct NtfsFile { - -} - -impl NtfsFile { - #[allow(dead_code)] - pub fn documents(&self) -> impl Iterator { - let docs: HashMap = HashMap::new(); - docs.into_iter().map(|(ts, v)| { - add_to_json(&v, "|@timestamp|", Value::Number(ts.timestamp_millis().into())) - }) - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/posix_file.rs b/src/es4forensics/ecs/objects/posix_file.rs deleted file mode 100644 index 60ee71c..0000000 --- a/src/es4forensics/ecs/objects/posix_file.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::collections::HashMap; -use anyhow::Result; - -use crate::common::bodyfile::{Bodyfile3Line, BehavesLikeI64}; -use chrono_tz::Tz; -use serde::Serialize; - -use crate::es4forensics::{timestamp::Timestamp, ecs::{timeline_object::TimelineObject, ecs_builder::EcsBuilder}}; -use crate::es4forensics::ecs::File; - -#[derive(Serialize)] -pub struct PosixFile { - name: String, - inode: String, - uid: u64, - gid: u64, - size: u64, - atime: Option, - mtime: Option, - ctime: Option, - crtime: Option, -} - -impl PosixFile { - fn load_timestamp(ts: &TS, tz: &Tz) -> Result> where TS: BehavesLikeI64 { - match ts.as_ref() { - None => Ok(None), - Some(ts) => { - Ok(Some((*ts, tz).try_into()?)) - } - } - } - - // fn generate_macb(&self, reference_ts: &Timestamp) -> Macb { - // let mut macb = Macb::default(); - // if let Some(t) = self.mtime.as_ref() { - // macb.modified = t == reference_ts; - // } - // if let Some(t) = self.atime.as_ref() { - // macb.accessed = t == reference_ts; - // } - // if let Some(t) = self.ctime.as_ref() { - // macb.changed = t == reference_ts; - // } - // if let Some(t) = self.crtime.as_ref() { - // macb.created = t == reference_ts; - // } - // macb - // } - - fn add_builder_to(&self, docs: &mut HashMap>, ts: &Option) { - if let Some(t) = ts.as_ref() { - //let macb = self.generate_macb(t); - if ! docs.contains_key(t) { - let file = File::from(self.name.clone()) - .with_inode(self.inode.clone()) - .with_uid(self.uid) - .with_gid(self.gid) - .with_size(self.size) - .with_mtime(self.mtime.clone()) - .with_accessed(self.atime.clone()) - .with_ctime(self.ctime.clone()) - .with_created(self.crtime.clone()); - let builder = EcsBuilder::new(self.name.clone(), t.clone()) - .with_additional_tag("bodyfile") - .with_file(file); - docs.insert(t.clone(), builder); - } - } - } -} - -impl TimelineObject for PosixFile {} - -impl IntoIterator for PosixFile { - type Item = anyhow::Result; - type IntoIter = std::collections::hash_map::IntoValues; - fn into_iter(self) -> Self::IntoIter { - let mut docs = HashMap::new(); - self.add_builder_to(&mut docs, &self.mtime); - self.add_builder_to(&mut docs, &self.atime); - self.add_builder_to(&mut docs, &self.ctime); - self.add_builder_to(&mut docs, &self.crtime); - - docs.into_values() - } -} - -impl TryFrom for PosixFile { - type Error = anyhow::Error; - fn try_from(bfline: Bodyfile3Line) -> Result { - let src_tz = Tz::UTC; - Self::try_from((&bfline, &src_tz)) - } -} - -impl TryFrom<&Bodyfile3Line> for PosixFile { - type Error = anyhow::Error; - fn try_from(bfline: &Bodyfile3Line) -> Result { - let src_tz = Tz::UTC; - Self::try_from((bfline, &src_tz)) - } -} - -impl TryFrom<(&Bodyfile3Line, &Tz)> for PosixFile { - type Error = anyhow::Error; - fn try_from((bfline, src_tz): (&Bodyfile3Line, &Tz)) -> Result { - Ok(Self { - name: bfline.get_name().to_string(), - inode: bfline.get_inode().to_string(), - uid: *bfline.get_uid(), - gid: *bfline.get_gid(), - size: *bfline.get_size(), - atime: Self::load_timestamp(bfline.get_atime(), src_tz)?, - mtime: Self::load_timestamp(bfline.get_mtime(), src_tz)?, - ctime: Self::load_timestamp(bfline.get_ctime(), src_tz)?, - crtime: Self::load_timestamp(bfline.get_crtime(), src_tz)?, - }) - } -} - -impl TryFrom<&str> for PosixFile { - type Error = anyhow::Error; - - fn try_from(value: &str) -> Result { - let bfline: Bodyfile3Line = value.try_into()?; - bfline.try_into() - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/registry_key.rs b/src/es4forensics/ecs/objects/registry_key.rs deleted file mode 100644 index 76c80d0..0000000 --- a/src/es4forensics/ecs/objects/registry_key.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; - -use serde::{Serialize, Deserialize}; -use serde_json::Value; - -use crate::es4forensics::{timestamp::Timestamp, utils::json::add_to_json}; - -#[derive(Serialize, Deserialize)] -pub struct RegistryKey { - -} - -impl RegistryKey { - #[allow(dead_code)] - pub fn documents(&self) -> impl Iterator { - let docs: HashMap = HashMap::new(); - docs.into_iter().map(|(ts, v)| { - add_to_json(&v, "|@timestamp|", Value::Number(ts.timestamp_millis().into())) - }) - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/simple_event.rs b/src/es4forensics/ecs/objects/simple_event.rs deleted file mode 100644 index d393854..0000000 --- a/src/es4forensics/ecs/objects/simple_event.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; - -use serde::{Serialize, Deserialize}; -use serde_json::Value; - -use crate::es4forensics::{timestamp::Timestamp, utils::json::add_to_json}; - -#[derive(Serialize, Deserialize)] -pub struct SimpleEvent { - -} - -impl SimpleEvent { - #[allow(dead_code)] - pub fn documents(&self) -> impl Iterator { - let docs: HashMap = HashMap::new(); - docs.into_iter().map(|(ts, v)| { - add_to_json(&v, "|@timestamp|", Value::Number(ts.timestamp_millis().into())) - }) - } -} \ No newline at end of file diff --git a/src/es4forensics/ecs/objects/windows_event.rs b/src/es4forensics/ecs/objects/windows_event.rs deleted file mode 100644 index f468734..0000000 --- a/src/es4forensics/ecs/objects/windows_event.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::collections::HashMap; - -use crate::es4forensics::{ - ecs::{ecs_builder::EcsBuilder, timeline_object::TimelineObject, *}, - ecs::log::{EventLevel, Log, Severity, Syslog}, -}; -use chrono::{DateTime, Utc}; -use num_traits::ToPrimitive; -use serde_json::Value; - -pub struct WindowsEvent<'a> { - event_record_id: u64, - - timestamp: DateTime, - - event_id: u64, - level: EventLevel, - computer: &'a Value, - - provider_name: &'a Value, - channel_name: &'a Value, - _activity_id: Option<&'a Value>, - custom_data: HashMap<&'a String, &'a Value>, -} - -impl<'a> WindowsEvent<'a> { - #[allow(clippy::too_many_arguments)] - #[allow(dead_code)] - pub fn new( - event_record_id: u64, - timestamp: DateTime, - event_id: u64, - level: EventLevel, - computer: &'a Value, - provider_name: &'a Value, - channel_name: &'a Value, - _activity_id: Option<&'a Value>, - custom_data: HashMap<&'a String, &'a Value>, - ) -> Self { - Self { - event_record_id, - timestamp, - event_id, - level, - computer, - provider_name, - channel_name, - _activity_id, - custom_data, - } - } - - fn into_builder(self) -> anyhow::Result { - let event = Event::default() - .with_kind(Kind::Event) - .with_sequence(self.event_record_id.to_string()) - .with_code(self.event_id) - .with_module(self.channel_name.as_str().unwrap().to_owned()) - .with_provider(self.provider_name.as_str().unwrap().to_owned()) - .with_severity(self.level.to_u8().unwrap()) - .with_custom_data(&self.custom_data); - - let host = Host::from(self.computer); - - let log = - Log::default().with_syslog(Syslog::default().with_severity(Severity::from(self.level))); - - EcsBuilder::new(format!("{}: {}", self.channel_name, self.event_id), self.timestamp.into()) - .with_event(event)? - .with_host(host)? - .with_log(log) - } -} - -impl TimelineObject for WindowsEvent<'_> {} - -impl IntoIterator for WindowsEvent<'_> { - type Item = anyhow::Result; - type IntoIter = std::vec::IntoIter; - fn into_iter(self) -> Self::IntoIter { - vec![self.into_builder()].into_iter() - } -} diff --git a/src/es4forensics/ecs/timeline_object.rs b/src/es4forensics/ecs/timeline_object.rs deleted file mode 100644 index 1724ed5..0000000 --- a/src/es4forensics/ecs/timeline_object.rs +++ /dev/null @@ -1,31 +0,0 @@ -use serde_json::Value; - -use crate::es4forensics::timestamp::Timestamp; - -use super::ecs_builder::EcsBuilder; - -pub trait TimelineObject: IntoIterator> { - fn into_values(self) -> Box> - where - Self: Sized, - ::IntoIter: 'static, - { - let res = self.into_iter().filter_map(|b| b.ok()).map(|b| { - let (_, v) = b.into(); - v - }); - Box::new(res) - } - - fn into_tuples(self) -> Box> - where - Self: Sized, - ::IntoIter: 'static, - { - let res = self - .into_iter() - .filter_map(|b| b.ok()) - .map(EcsBuilder::into); - Box::new(res) - } -} diff --git a/src/es4forensics/index.rs b/src/es4forensics/index.rs deleted file mode 100644 index 286152b..0000000 --- a/src/es4forensics/index.rs +++ /dev/null @@ -1,147 +0,0 @@ -use anyhow::{bail, Result}; -use base64::{engine::general_purpose, Engine}; -use elasticsearch::{BulkOperation, BulkParts, Elasticsearch}; -use serde_json::Value; -use sha2::{Digest, Sha256}; -use tokio_async_drop::tokio_async_drop; - -use crate::es4forensics::ecs::TimelineObject; - -struct ElasticDocument { - id: String, - content: Value, -} - -impl From for (String, Value) { - fn from(me: ElasticDocument) -> Self { - (me.id, me.content) - } -} - -impl From for ElasticDocument { - fn from(val: Value) -> Self { - let mut hasher: Sha256 = Sha256::new(); - hasher.update(val.to_string()); - let result = hasher.finalize(); - Self { - id: general_purpose::URL_SAFE_NO_PAD.encode(result), - content: val, - } - } -} - -pub struct Index { - name: String, - client: Elasticsearch, - - cache_size: usize, - document_cache: Option>, -} - -impl Index { - pub fn new(name: String, client: Elasticsearch) -> Self { - Self { - name, - client, - cache_size: 10000, - document_cache: Some(Vec::new()), - } - } - - #[allow(dead_code)] - pub async fn add_timeline_object(&mut self, object: Obj) -> Result<()> - where - Obj: TimelineObject, - { - for builder_res in object { - match builder_res { - Err(why) => { - log::error!("Error while creating JSON value: {why}") - } - Ok(builder) => { - let (_, value) = builder.into(); - self.add_bulk_document(value).await?; - } - } - } - Ok(()) - } - - pub async fn add_bulk_document(&mut self, document: Value) -> Result<()> { - if let Some(c) = self.document_cache.as_mut() { - c.push(document.into()) - } - - if self.document_cache.as_ref().unwrap().len() >= self.cache_size { - self.flush().await - } else { - Ok(()) - } - } - - pub async fn flush(&mut self) -> Result<()> { - match self.document_cache.as_ref() { - None => log::trace!("There is no document cache"), - - Some(document_cache) => { - log::info!( - "flushing document cache with {} entries", - document_cache.len() - ); - if document_cache.is_empty() { - log::trace!("Document cache is empty"); - } else { - let parts = BulkParts::Index(&self.name); - - let item_count = self.document_cache.as_ref().unwrap().len(); - let items: Vec> = self - .document_cache - .replace(Vec::new()) - .unwrap() - .into_iter() - .map(|v| { - let (id, val) = v.into(); - BulkOperation::create(id, val).into() - }) - .collect(); - let bulk = self.client.bulk(parts).body(items); - - let response = bulk.send().await?; - - if !response.status_code().is_success() { - log::error!( - "error {} while sending bulk operation", - response.status_code() - ); - log::error!("{}", response.text().await?); - bail!("error while sending bulk operation"); - } else { - let json: Value = response.json().await?; - if json["errors"].as_bool().unwrap() { - log::error!("error while writing to elasticsearch: {json}"); - } else { - log::trace!("successfully wrote {item_count} items"); - } - } - } - } - } - Ok(()) - } - - pub async fn set_cache_size(&mut self, cache_size: usize) -> Result<()> { - if self.cache_size > cache_size { - self.flush().await?; - } - self.cache_size = cache_size; - Ok(()) - } -} - -impl Drop for Index { - fn drop(&mut self) { - tokio_async_drop!({ - let _ = self.flush().await; - }); - } -} diff --git a/src/es4forensics/index_builder.rs b/src/es4forensics/index_builder.rs deleted file mode 100644 index 50c94d8..0000000 --- a/src/es4forensics/index_builder.rs +++ /dev/null @@ -1,254 +0,0 @@ -use anyhow::{anyhow, Result}; -use elasticsearch::{ - auth::Credentials, - cat::CatIndicesParts, - cert::CertificateValidation, - http::{ - transport::{SingleNodeConnectionPool, TransportBuilder}, - Url, - }, - indices::IndicesCreateParts, - Elasticsearch, -}; -use serde_json::{json, Value}; - -use crate::es4forensics::{Protocol, index::Index}; - -pub struct IndexBuilder { - host: Option, - port: Option, - protocol: Protocol, - index_name: String, - do_certificate_validation: bool, - credentials: Option, -} - -const DEFAULT_HOST: &str = "localhost"; -const DEFAULT_PORT: u16 = 9200; - -pub trait WithHost { - fn with_host(self, host: T) -> Self; -} - -impl IndexBuilder { - pub fn with_name(index_name: String) -> Self { - Self { - host: None, - port: None, - protocol: Protocol::default(), - index_name, - do_certificate_validation: true, - credentials: None, - } - } - - pub fn with_port(mut self, port: u16) -> Self { - self.port = Some(port); - self - } - - pub fn with_protocol(mut self, protocol: Protocol) -> Self { - self.protocol = protocol; - self - } - - pub fn without_certificate_validation(mut self) -> Self { - self.do_certificate_validation = false; - self - } - - pub fn with_credentials(mut self, credentials: Credentials) -> Self { - self.credentials = Some(credentials); - self - } - - pub fn host(&self) -> &str { - match self.host.as_ref() { - Some(h) => h, - None => DEFAULT_HOST, - } - } - - pub fn port(&self) -> u16 { - match self.port.as_ref() { - Some(p) => *p, - None => DEFAULT_PORT, - } - } - - pub async fn index_exists(&self) -> Result { - let client = self.create_client()?; - self.client_has_index(&client).await - } - - pub async fn connect(self) -> Result { - let client = self.create_client()?; - Ok(Index::new(self.index_name, client)) - } - - pub async fn create_index(&self) -> Result { - let client = self.create_client()?; - - if !self.client_has_index(&client).await? { - log::info!("create index with mappings"); - let index_body = json!({ - "mappings": { - "properties": { - "@timestamp": { - "type": "date", - "format": "epoch_millis" - }, - "tags": { - "type": "keyword" - }, - "file": { - "properties": { - "accessed": { - "type": "date", - "format": "epoch_millis" - }, - "created": { - "type": "date", - "format": "epoch_millis" - }, - "ctime": { - "type": "date", - "format": "epoch_millis" - }, - "mtime": { - "type": "date", - "format": "epoch_millis" - }, - "macb_short": { - "type": "keyword" - }, - "macb_long": { - "type": "keyword" - } - } - } - } - } - }); - let parts = IndicesCreateParts::Index(&self.index_name); - let response = client - .indices() - .create(parts) - .body(index_body) - .send() - .await?; - match response.error_for_status_code_ref() { - Ok(_response) => (), - Err(why) => { - log::error!( - "Error while creating index: {}", - response.text().await? - ); - log::error!("error message was: {}", why); - return Err(anyhow!(why)) - } - } - - //let pipeline_id = format!("{}_pipeline", self.index_name()); - //self.create_pipeline(&client, &pipeline_id).await?; - } - Ok(Index::new(self.index_name.clone(), client)) - } -/* - async fn create_pipeline(&self, client: &Elasticsearch, pipeline_id: &str) -> Result<()> { - let pipeline_parts = IngestPutPipelineParts::Id(pipeline_id); - let set_timestamp = json!({ - "description": "Creates a timestamp when a document is initially indexed", - "processors": [ - { - "set": { - "field": "timestamp", - "value": "{{{_ingest.timestamp}}}" - } - } - ] - }); - let ingest_response = client - .ingest() - .put_pipeline(pipeline_parts) - .body(set_timestamp) - .send() - .await?; - - match ingest_response.error_for_status_code_ref() { - Err(why) => { - log::error!( - "Error while creating pipeline: {}", - ingest_response.text().await? - ); - log::error!("error message was: {}", why); - Err(anyhow!(why)) - } - Ok(_response) => { - log::info!("sucessfully created pipeline {pipeline_id}"); - Ok(()) - } - } - } -*/ - fn create_client(&self) -> Result { - let url = Url::parse(&format!("{}://{}:{}", self.protocol, self.host(), self.port()))?; - let conn_pool = SingleNodeConnectionPool::new(url); - let mut transport_builder = TransportBuilder::new(conn_pool) - .cert_validation(if self.do_certificate_validation { - CertificateValidation::Default - } else { - CertificateValidation::None - }) - .disable_proxy(); - - if let Some(credentials) = &self.credentials { - transport_builder = transport_builder.auth(credentials.clone()); - } - let transport = transport_builder.build()?; - Ok(Elasticsearch::new(transport)) - } - - async fn client_has_index(&self, client: &Elasticsearch) -> Result { - log::info!("test if index '{}' exists", self.index_name); - - let response = client - .cat() - .indices(CatIndicesParts::Index(&["*"])) - .format("json") - .send() - .await?; - response.error_for_status_code_ref()?; - - if response.content_length().unwrap_or(0) == 0 { - log::debug!("empty result; index does not seem to exist"); - Ok(false) - } else { - let response_body = response.json::().await?; - - match response_body.as_array() { - None => { - log::debug!("index does not exist"); - Ok(false) - } - Some(body) => Ok(body - .iter() - .any(|r| *r["index"].as_str().unwrap() == self.index_name)), - } - } - } -} - -impl WithHost for IndexBuilder { - fn with_host(mut self, host: String) -> Self { - self.host = Some(host); - self - } -} - -impl WithHost<&str> for IndexBuilder { - fn with_host(mut self, host: &str) -> Self { - self.host = Some(host.to_owned()); - self - } -} diff --git a/src/es4forensics/mod.rs b/src/es4forensics/mod.rs deleted file mode 100644 index b05ab41..0000000 --- a/src/es4forensics/mod.rs +++ /dev/null @@ -1,90 +0,0 @@ - -//! This crates provides structs and functions to insert timeline data into -//! an elasticsearch index. -//! -//! # Creating Indices -//! ``` -//! use dfir_toolkit::es4forensics::IndexBuilder; -//! use dfir_toolkit::es4forensics::WithHost; -//! use elasticsearch::auth::Credentials; -//! -//!# #[tokio::main] -//!# async fn main() { -//! let username = "elastic"; -//! let password = "elastic"; -//! let credentials = Credentials::Basic(username.to_string(), password.to_string()); -//! let mut index = IndexBuilder::with_name("elastic4forensics_test".to_string()) -//! .with_host("127.0.0.1") -//! .with_port(9200) -//! .without_certificate_validation() -//! .with_credentials(credentials) -//! .create_index().await; -//!# } -//! ``` -//! After doing this, you can easily add documents to the index using [`Index::add_timeline_object`] -//! -//! # Adding documents to elasticsearch -//! -//! For example, consider we have a line from a bodyfile. We need to convert this -//! into a [`ecs::objects::PosixFile`]-Object, which can then be added to an Index: -//! -//! ``` -//! use dfir_toolkit::es4forensics::objects::PosixFile; -//!# use dfir_toolkit::es4forensics::Index; -//! -//!# fn foo(mut index: Index) { -//! let str_line = "0|/Users/Administrator ($FILE_NAME)|93552-48-2|d/drwxrwxrwx|0|0|92|1577092511|1577092511|1577092511|-1"; -//! let posix_file: PosixFile = str_line.try_into().unwrap(); -//! -//! index.add_timeline_object(posix_file); -//!# } -//! ``` -//! -//! # Exporting documents in JSON format -//! -//! Sometimes you might want to simply export your documents, instead of directly importing them into -//! elasticsearch. -//! -//! Keep in mind that one bodyfile line might contain multiple different timestamps (up to four), -//! which yields up to four elasticsearch documents. Therefore, [`ecs::objects::ElasticObject::documents()`] returns an -//! iterator over [`serde_json::Value`] -//! -//! ``` -//! use dfir_toolkit::es4forensics::objects::PosixFile; -//! use dfir_toolkit::es4forensics::Timestamp; -//! use dfir_toolkit::es4forensics::TimelineObject; -//! use serde_json::Value; -//!# use dfir_toolkit::es4forensics::Index; -//! -//!# fn foo(mut index: Index) { -//! let str_line = "0|/Users/Administrator ($FILE_NAME)|93552-48-2|d/drwxrwxrwx|0|0|92|1577092511|1577092511|1577092511|-1"; -//! let posix_file: PosixFile = str_line.try_into().unwrap(); -//! -//! for json_value in posix_file.into_values() { -//! println!("{json_value}"); -//! } -//!# } -//! ``` - -#[cfg(feature="elasticsearch")] -mod index; - -#[cfg(feature="elasticsearch")] -mod index_builder; - -mod timestamp; -mod utils; -mod ecs; - -mod protocol; -mod stream_source; - -#[cfg(feature="elasticsearch")] -pub use index::*; - -#[cfg(feature="elasticsearch")] -pub use index_builder::*; -pub use timestamp::*; -pub use ecs::*; -pub use protocol::*; -pub use stream_source::*; \ No newline at end of file diff --git a/src/es4forensics/protocol.rs b/src/es4forensics/protocol.rs deleted file mode 100644 index e75343d..0000000 --- a/src/es4forensics/protocol.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::fmt::Display; - -use clap::ValueEnum; - - -#[derive(ValueEnum, Clone, Default)] -pub enum Protocol { - Http, - #[default] Https, -} - -impl Display for Protocol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Protocol::Http => write!(f, "http"), - Protocol::Https => write!(f, "https"), - } - } -} \ No newline at end of file diff --git a/src/es4forensics/stream_source.rs b/src/es4forensics/stream_source.rs deleted file mode 100644 index d321e46..0000000 --- a/src/es4forensics/stream_source.rs +++ /dev/null @@ -1,50 +0,0 @@ -use anyhow::Result; -use std::{ - fs::File, - io::{BufRead, BufReader, Read}, -}; - -#[cfg(feature = "gzip")] -use flate2::read::GzDecoder; - -pub enum StreamSource { - Stdin, - File(Box), -} - -impl StreamSource { - pub fn from(filename: &str) -> Result { - if filename == "-" { - Ok(StreamSource::Stdin) - } else { - let file = BufReader::new(File::open(filename)?); - - #[cfg(not(feature = "gzip"))] - let reader: Box = Box::new(file); - - #[cfg(feature = "gzip")] - let reader = Self::open_gzip(filename, file); - - Ok(StreamSource::File(reader)) - } - } - - #[cfg(feature = "gzip")] - fn open_gzip(filename: &str, file: R) -> Box { - - if filename.ends_with(".gz") { - Box::new(BufReader::new(GzDecoder::new(file))) - } else { - Box::new(BufReader::new(file)) - } - } -} - -impl From for Box { - fn from(me: StreamSource) -> Self { - match me { - StreamSource::Stdin => Box::new(BufReader::new(std::io::stdin())), - StreamSource::File(f) => f, - } - } -} diff --git a/src/es4forensics/timestamp.rs b/src/es4forensics/timestamp.rs deleted file mode 100644 index a093c1d..0000000 --- a/src/es4forensics/timestamp.rs +++ /dev/null @@ -1,61 +0,0 @@ -use anyhow::{anyhow, Result}; -use chrono::{DateTime, TimeZone, Utc}; -use chrono_tz::Tz; -use serde::Serialize; -use serde_json::{json, Value}; -use std::hash::Hash; - -#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord)] -pub struct Timestamp { - ts: i64, -} - -impl Serialize for Timestamp { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_i64(self.ts) - } -} - -impl From> for Timestamp -where - Tz: TimeZone, -{ - fn from(d: DateTime) -> Self { - let ts = d.with_timezone(&Utc); - log::trace!("converting {:?} to {}", d, ts.timestamp_millis()); - Self { - ts: ts.timestamp_millis(), - } - } -} - -impl TryFrom<(i64, &Tz)> for Timestamp { - type Error = anyhow::Error; - - fn try_from((unix_ts, src_tz): (i64, &Tz)) -> Result { - let ts = match DateTime::from_timestamp(unix_ts, 0) { - Some(ts) => ts.with_timezone(src_tz), - None => { - return Err(anyhow!("INVALID DATETIME")); - } - }; - Ok(Self { - ts: ts.timestamp_millis(), - }) - } -} - -impl From<&Timestamp> for Value { - fn from(ts: &Timestamp) -> Self { - json!(ts.ts) - } -} - -impl Timestamp { - pub fn timestamp_millis(&self) -> i64 { - self.ts - } -} diff --git a/src/es4forensics/utils/json.rs b/src/es4forensics/utils/json.rs deleted file mode 100644 index 384f32e..0000000 --- a/src/es4forensics/utils/json.rs +++ /dev/null @@ -1,12 +0,0 @@ -use serde_json::Value; - -pub fn add_to_json(value: &Value, ident: &str, s: Value) -> Value { - match value { - Value::Object(m) => { - let mut m = m.clone(); - m.insert(ident.to_string(), s); - Value::Object(m) - } - v => v.clone(), - } -} diff --git a/src/es4forensics/utils/mod.rs b/src/es4forensics/utils/mod.rs deleted file mode 100644 index 22fdbb3..0000000 --- a/src/es4forensics/utils/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod json; diff --git a/src/lib.rs b/src/lib.rs index 7576a65..e9b056e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,3 @@ pub mod registry; pub mod common; -pub mod evtx; - -#[cfg(feature="elastic")] -pub mod es4forensics; \ No newline at end of file +pub mod evtx; \ No newline at end of file From 1aca4ad4c82a6743feb67526ddb72596dd3b374f Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Wed, 25 Sep 2024 14:37:45 +0200 Subject: [PATCH 2/6] initial support for record format --- Cargo.lock | 13 ++- Cargo.toml | 8 +- src/bin/mactime2/application.rs | 7 +- src/bin/mactime2/output/mod.rs | 4 +- src/bin/mactime2/output/record_output.rs | 120 +++++++++++++++++++++++ 5 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/bin/mactime2/output/record_output.rs diff --git a/Cargo.lock b/Cargo.lock index 5aa40f0..58b82eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1072,9 +1072,9 @@ dependencies = [ [[package]] name = "flow-record" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ea19db0135e4a00aa3655e953106e5b8f30b6c2f1f45313836461bc15e79a0" +checksum = "6ae63f7bfd1e8412006223dc7df0e17060d7667bdbd73ce8ab2a33e1191f2394" dependencies = [ "binrw", "bitflags 2.6.0", @@ -1090,9 +1090,9 @@ dependencies = [ [[package]] name = "flow-record-common" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade1700d676b9117a88e6a9ba18c4f90c420531e4b5fbb12cce99a3b2851dec8" +checksum = "1b75fa5020fb9807f66c0229b5ebd8cb93440531865ebf8f6d3d28c2ea1aaa4d" dependencies = [ "binrw", "chrono", @@ -1100,16 +1100,15 @@ dependencies = [ "proc-macro2", "quote", "rmpv", - "sha2", "strum", "thiserror", ] [[package]] name = "flow-record-derive" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d39842eeb57e5db8292e1f7bdc03b4fc28f11caf2c8ed3653ce6e4e1cb17ba" +checksum = "91fd6daf453a4d3cf7dc047a603dca62d4396a8cbf1e43cbbcd86f94de1e3af9" dependencies = [ "chrono", "darling", diff --git a/Cargo.toml b/Cargo.toml index fdb60c8..3e0ab39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0" # this version is required, because earlier versions handle missing `created` # timestamps as `Uncategorized`, instead of `Unsupported` -rust-version = "1.78" +rust-version = "1.80" [package.metadata.deb] maintainer-scripts = "scripts/maintainer" @@ -93,7 +93,7 @@ required-features = ["zip2bodyfile"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["pol_export", "mactime2", "evtxtools", "regdump", "hivescan", "cleanhive", "ipgrep", "ts2date", "lnk2bodyfile", "pf2bodyfile", "zip2bodyfile"] -mactime2 = ["gzip", "chrono-tz", "thiserror", "bitflags", "encoding_rs_io", "color-print", "strum", "strum_macros"] +mactime2 = ["gzip", "chrono-tz", "thiserror", "bitflags", "encoding_rs_io", "color-print", "strum", "strum_macros", "sha2"] gzip = ["flate2"] evtxtools = ["evtxscan", "evtxcat", "evtxls", "evtxanalyze", "evtx2bodyfile"] pol_export = [] @@ -120,7 +120,7 @@ clap = {version = "4.5", features = ["derive", "wrap_help", "cargo"] } clap-verbosity-flag = "2.0.0" csv = "1.2.2" encoding_rs = "0.8" -flow-record = "0.4.2" +flow-record = "0.4.4" ## setting release_max_level_info conflicts with evtx # log = {version = "0.4", features = [ "release_max_level_info" ]} @@ -137,7 +137,7 @@ clio = {version="0.3", features=["clap-parse"] } #clio = {path="../clio", features=["clap-parse"]} # mactime2 -chrono-tz = {version="0.8", optional=true} +chrono-tz = {version="0", optional=true} serde_json = {version = "1", optional=true} flate2 = {version="1", optional=true} thiserror = {version="1", optional=true} diff --git a/src/bin/mactime2/application.rs b/src/bin/mactime2/application.rs index 5e369f0..6d92778 100644 --- a/src/bin/mactime2/application.rs +++ b/src/bin/mactime2/application.rs @@ -3,7 +3,7 @@ use clap::ValueEnum; use clio::Input; use strum_macros::Display; -use crate::output::OldCsvOutput; +use crate::output::{OldCsvOutput, RecordOutput}; use super::bodyfile::{BodyfileDecoder, BodyfileReader, BodyfileSorter}; use super::cli::Cli; @@ -32,6 +32,10 @@ pub(crate) enum OutputFormat { #[strum(serialize = "json")] Json, + /// flow.record format used by `dissect` and `rdump` + #[strum(serialize = "record")] + Record, + /// Use the old (non RFC compliant) CSV format that was used by legacy mactime. #[strum(serialize = "old-csv")] OldCsv, @@ -65,6 +69,7 @@ impl Mactime2Application { self.show_headers, )), OutputFormat::Txt => Box::new(TxtOutput::new(std::io::stdout(), self.dst_zone)), + OutputFormat::Record => Box::new(RecordOutput::new(std::io::stdout(), self.dst_zone)), _ => panic!("invalid execution path"), }); Box::new(sorter) diff --git a/src/bin/mactime2/output/mod.rs b/src/bin/mactime2/output/mod.rs index b587893..0c24740 100644 --- a/src/bin/mactime2/output/mod.rs +++ b/src/bin/mactime2/output/mod.rs @@ -1,7 +1,9 @@ mod csv_output; mod old_csv_output; mod txt_output; +mod record_output; pub (crate) use csv_output::*; pub (crate) use old_csv_output::*; -pub (crate) use txt_output::*; \ No newline at end of file +pub (crate) use txt_output::*; +pub (crate) use record_output::*; \ No newline at end of file diff --git a/src/bin/mactime2/output/record_output.rs b/src/bin/mactime2/output/record_output.rs new file mode 100644 index 0000000..2e46a2c --- /dev/null +++ b/src/bin/mactime2/output/record_output.rs @@ -0,0 +1,120 @@ +use std::io::Write; + +use chrono::{DateTime, Utc}; +use chrono_tz::Tz; +use dfir_toolkit::common::bodyfile::BehavesLikeI64; +use dfir_toolkit::common::bodyfile::Bodyfile3Line; +use flow_record::artifacts::posix::FileMode; +use flow_record::derive::FlowRecord; +use flow_record::{artifacts::posix::FileType, prelude::*}; +use types::PathType; +use types::{Filesize, Path}; + +use crate::bodyfile::{ListEntry, Mactime2Writer}; + +pub(crate) struct RecordOutput +where + W: Write + Send, +{ + _dst_zone: Tz, + writer: Serializer, +} + +impl RecordOutput +where + W: Write + Send, +{ + pub fn new(writer: W, _dst_zone: Tz) -> Self { + Self { + _dst_zone, + writer: Serializer::new(writer), + } + } + #[allow(dead_code)] + pub fn with_writer(mut self, writer: W) -> Self + where + W: Write + Send + 'static, + { + self.writer = Serializer::new(writer); + self + } +} + +impl Mactime2Writer for RecordOutput +where + W: Write + Send, +{ + fn write_line(&mut self, _timestamp: &i64, entry: &ListEntry) -> std::io::Result<()> { + let record = FileRecord::try_from(entry.line.as_ref()).expect("invalid bodyfile data"); + self.writer.serialize(record).unwrap(); + Ok(()) + } + + fn into_writer(self) -> W { + self.writer.into_inner() + } +} + +#[derive(FlowRecord)] +#[flow_record(version = 1, source = "Posix", classification = "file")] +pub struct FileRecord { + file_name: Path, + user_id: u64, + group_id: u64, + file_type: FileType, + mode: FileMode, + size: Filesize, + + modified: Option>, + accessed: Option>, + changed: Option>, + birth: Option>, +} + +struct UnixTimestamp(i64); + +impl From for UnixTimestamp { + fn from(value: i64) -> Self { + Self(value) + } +} + +impl From for Option> { + fn from(value: UnixTimestamp) -> Self { + if value.0 != -1 { + DateTime::from_timestamp(value.0, 0) + } else { + None + } + } +} + +impl TryFrom<&Bodyfile3Line> for FileRecord { + type Error = flow_record::prelude::Error; + fn try_from(line: &Bodyfile3Line) -> Result { + Ok(Self { + file_name: Path::new(line.get_name().to_string().into(), PathType::Posix), + user_id: *line.get_uid(), + group_id: *line.get_gid(), + mode: FileMode::try_from(&line.get_mode_as_string()[..])?, + file_type: FileType::try_from(&line.get_mode_as_string()[..])?, + size: Filesize::from(*line.get_size()), + modified: line + .get_mtime() + .as_ref() + .and_then(|t| Option::>::from(UnixTimestamp::from(*t))), + accessed: line + .get_atime() + .as_ref() + .and_then(|t| Option::>::from(UnixTimestamp::from(*t))), + changed: line + .get_ctime() + .as_ref() + .and_then(|t| Option::>::from(UnixTimestamp::from(*t))), + birth: line + .get_crtime() + .as_ref() + .and_then(|t| Option::>::from(UnixTimestamp::from(*t))), + }) + } +} From 4d2094a811f0ff287b9e6e2ecfce031418200fc9 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Wed, 25 Sep 2024 14:43:46 +0200 Subject: [PATCH 3/6] more robust handling of empty file mode --- src/bin/mactime2/output/record_output.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bin/mactime2/output/record_output.rs b/src/bin/mactime2/output/record_output.rs index 2e46a2c..09b9365 100644 --- a/src/bin/mactime2/output/record_output.rs +++ b/src/bin/mactime2/output/record_output.rs @@ -92,12 +92,22 @@ impl From for Option> { impl TryFrom<&Bodyfile3Line> for FileRecord { type Error = flow_record::prelude::Error; fn try_from(line: &Bodyfile3Line) -> Result { + let mode; + let file_type; + + if line.get_mode_as_string().is_empty() { + mode = FileMode::UNSPECIFIED; + file_type = FileType::Unknown; + } else { + mode = FileMode::try_from(&line.get_mode_as_string()[..])?; + file_type = FileType::try_from(&line.get_mode_as_string()[..])?; + } Ok(Self { file_name: Path::new(line.get_name().to_string().into(), PathType::Posix), user_id: *line.get_uid(), group_id: *line.get_gid(), - mode: FileMode::try_from(&line.get_mode_as_string()[..])?, - file_type: FileType::try_from(&line.get_mode_as_string()[..])?, + mode, + file_type, size: Filesize::from(*line.get_size()), modified: line .get_mtime() From 1b573a4d9afc740745e1b8795e44194207de9ea5 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Sat, 28 Sep 2024 18:41:53 +0200 Subject: [PATCH 4/6] initial support for record format in evtx2bodyfile --- src/bin/evtx2bodyfile/evtx_file.rs | 23 ++-- src/bin/evtx2bodyfile/main.rs | 14 ++- src/bin/evtx2bodyfile/output_format.rs | 5 + src/bin/evtx2bodyfile/output_formatter.rs | 18 ---- src/bin/evtx2bodyfile/output_writer.rs | 121 ++++++++++++++++++++++ 5 files changed, 145 insertions(+), 36 deletions(-) delete mode 100644 src/bin/evtx2bodyfile/output_formatter.rs create mode 100644 src/bin/evtx2bodyfile/output_writer.rs diff --git a/src/bin/evtx2bodyfile/evtx_file.rs b/src/bin/evtx2bodyfile/evtx_file.rs index 1d8e939..eb7b40f 100644 --- a/src/bin/evtx2bodyfile/evtx_file.rs +++ b/src/bin/evtx2bodyfile/evtx_file.rs @@ -5,7 +5,7 @@ use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; use ouroboros::self_referencing; use serde_json::Value; -use crate::output_formatter::OutputFormatter; +use crate::output_writer::OutputWriter; pub(crate) struct EvtxFile(Input); @@ -58,24 +58,21 @@ impl From<&Input> for EvtxFile { } impl EvtxFile { - pub(crate) fn print_records(self, formatter: F, treat_errors_as_warnings: bool) -> Result<()> + pub(crate) fn print_records(self, treat_errors_as_warnings: bool) -> Result<()> where - F: OutputFormatter, + F: OutputWriter, { + let mut formatter = F::from(std::io::stdout()); let bar = self.create_progress_bar().unwrap(); for value in self.into_iter() { - match formatter.record_to_string(&value) { - Ok(s) => println!("{s}"), - Err(why) => { - if treat_errors_as_warnings { - log::warn!("Error while reading record: {why}"); - } else { - bar.finish_and_clear(); - return Err(why); - } + if let Err(why) = formatter.output(&value) { + if treat_errors_as_warnings { + log::warn!("Error while reading record: {why}"); + } else { + bar.finish_and_clear(); + return Err(why); } } - bar.inc(1); } bar.finish_and_clear(); diff --git a/src/bin/evtx2bodyfile/main.rs b/src/bin/evtx2bodyfile/main.rs index 69b6680..d17f153 100644 --- a/src/bin/evtx2bodyfile/main.rs +++ b/src/bin/evtx2bodyfile/main.rs @@ -1,14 +1,17 @@ +use std::io::Stdout; + use anyhow::{bail, Result}; use cli::Cli; use dfir_toolkit::common::FancyParser; use evtx_file::EvtxFile; -use output_formatter::BodyfileOutputFormatter; +use output_format::OutputFormat; +use output_writer::{BodyfileOutputWriter, RecordOutputWriter}; mod bf_data; mod cli; mod evtx_file; mod output_format; -mod output_formatter; +mod output_writer; #[macro_use] mod macros; @@ -24,11 +27,12 @@ fn main() -> Result<()> { for input in cli.evtx_files().iter() { let file = EvtxFile::from(input); + match cli.format() { - output_format::OutputFormat::Bodyfile => { - file.print_records(BodyfileOutputFormatter, !cli.strict())? - } + OutputFormat::Bodyfile => file.print_records::>(!cli.strict())?, + OutputFormat::Record => file.print_records::>(!cli.strict())?, } } + Ok(()) } diff --git a/src/bin/evtx2bodyfile/output_format.rs b/src/bin/evtx2bodyfile/output_format.rs index 022fff1..00cb47a 100644 --- a/src/bin/evtx2bodyfile/output_format.rs +++ b/src/bin/evtx2bodyfile/output_format.rs @@ -4,6 +4,11 @@ use strum_macros::Display; #[derive(ValueEnum, Clone, Display)] pub(crate) enum OutputFormat { + /// bodyfile format #[strum(serialize = "bodyfile")] Bodyfile, + + /// flow record format () + #[strum(serialize = "record")] + Record } diff --git a/src/bin/evtx2bodyfile/output_formatter.rs b/src/bin/evtx2bodyfile/output_formatter.rs deleted file mode 100644 index bc4eaa4..0000000 --- a/src/bin/evtx2bodyfile/output_formatter.rs +++ /dev/null @@ -1,18 +0,0 @@ -use evtx::SerializedEvtxRecord; -use serde_json::Value; - -use crate::bf_data::BfData; - -#[derive(Default)] -pub(crate) struct BodyfileOutputFormatter; - -pub(crate) trait OutputFormatter { - fn record_to_string(&self, record: &SerializedEvtxRecord) -> anyhow::Result; -} - -impl OutputFormatter for BodyfileOutputFormatter { - fn record_to_string(&self, record: &SerializedEvtxRecord) -> anyhow::Result { - let bf_data = BfData::try_from(record)?; - bf_data.try_into_mactime() - } -} diff --git a/src/bin/evtx2bodyfile/output_writer.rs b/src/bin/evtx2bodyfile/output_writer.rs new file mode 100644 index 0000000..8e52607 --- /dev/null +++ b/src/bin/evtx2bodyfile/output_writer.rs @@ -0,0 +1,121 @@ +use std::io::Write; + +use chrono::{DateTime, Utc}; +use dfirtk_eventdata::{ActivityId, EventId, ProcessId, RelatedActivityId}; +use evtx::SerializedEvtxRecord; +use flow_record::prelude::{FlowRecord, Serializer}; +use flow_record::{ + prelude::{rmpv, FieldType}, + ToMsgPackValue, +}; +use flow_record::derive::FlowRecord; + +use crate::bf_data::BfData; + +#[derive(Default)] +pub(crate) struct BodyfileOutputWriter(W); +pub(crate) struct RecordOutputWriter(Serializer); + +pub(crate) trait OutputWriter: From +where + W: Write, +{ + fn output(&mut self, record: &SerializedEvtxRecord) -> anyhow::Result<()>; +} + +impl From for BodyfileOutputWriter +where + W: Write, +{ + fn from(writer: W) -> Self { + Self(writer) + } +} + +impl From for RecordOutputWriter +where + W: Write, +{ + fn from(writer: W) -> Self { + Self(Serializer::new(writer)) + } +} + +impl OutputWriter for BodyfileOutputWriter +where + W: Write, +{ + fn output(&mut self, record: &SerializedEvtxRecord) -> anyhow::Result<()> { + let bf_data = BfData::try_from(record)?; + let s = bf_data.try_into_mactime()?; + writeln!(self.0, "{s}")?; + Ok(()) + } +} + +impl OutputWriter for RecordOutputWriter +where + W: Write, +{ + fn output(&mut self, record: &SerializedEvtxRecord) -> anyhow::Result<()> { + let event = WindowsEvent::try_from(record)?; + self.0.serialize(event)?; + Ok(()) + } +} + +#[derive(FlowRecord)] +#[flow_record(version = 1, source = "evtx2bodyfile", classification = "evtx")] +struct WindowsEvent { + record_timestamp: DateTime, + event_id: u16, + event_record_id: u64, + activity_id: String, + related_activity_id: String, + process_id: u64, +} + +impl TryFrom<&SerializedEvtxRecord> for WindowsEvent { + type Error = anyhow::Error; + + fn try_from(record: &SerializedEvtxRecord) -> Result { + let record_timestamp = record.timestamp; + let event_id = EventId::try_from(record)?.0; + let event_record_id = record.event_record_id; + let activity_id = ActivityId::try_from(record)?.value().to_string(); + let related_activity_id = RelatedActivityId::try_from(record)?.to_string(); + let process_id = ProcessId::try_from(record)?.0; + Ok(Self { + record_timestamp, + event_id, + event_record_id, + activity_id, + related_activity_id, + process_id, + }) + } +} +struct ValueWrapper<'v>(&'v serde_json::Value); + +impl<'v> From<&'v serde_json::Value> for ValueWrapper<'v> { + fn from(value: &'v serde_json::Value) -> Self { + Self(value) + } +} + +impl<'v> ToMsgPackValue for ValueWrapper<'v> { + fn to_msgpack_value(self) -> rmpv::Value { + match self.0 { + serde_json::Value::Null => rmpv::Value::Nil, + serde_json::Value::Bool(_) => todo!(), + serde_json::Value::Number(_number) => todo!(), + serde_json::Value::String(_) => todo!(), + serde_json::Value::Array(_vec) => todo!(), + serde_json::Value::Object(_map) => todo!(), + } + } + + fn field_type() -> FieldType { + FieldType::String + } +} From 9976bc7017db002b09ec8fd3fb04f7ba106891d1 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 4 Oct 2024 13:40:17 +0200 Subject: [PATCH 5/6] support for the flow record format in evtx2bodyfile :-o --- Cargo.lock | 142 +++++++++++++------------ Cargo.toml | 3 +- src/bin/evtx2bodyfile/main.rs | 1 + src/bin/evtx2bodyfile/output_writer.rs | 81 +++++++++----- src/bin/evtx2bodyfile/value_map.rs | 44 ++++++++ 5 files changed, 179 insertions(+), 92 deletions(-) create mode 100644 src/bin/evtx2bodyfile/value_map.rs diff --git a/Cargo.lock b/Cargo.lock index 58b82eb..3b1fb7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -361,9 +361,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.8.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" dependencies = [ "chrono", "chrono-tz-build", @@ -404,12 +404,11 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" dependencies = [ "parse-zoneinfo", - "phf", "phf_codegen", ] @@ -425,9 +424,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -444,9 +443,9 @@ dependencies = [ [[package]] name = "clap-verbosity-flag" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d19864d6b68464c59f7162c9914a0b569ddc2926b4a2d71afe62a9738eff53" +checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed" dependencies = [ "clap", "log", @@ -454,9 +453,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -467,9 +466,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.29" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" +checksum = "74a01f4f9ee6c066d42a1c8dedf0dcddad16c72a8981a309d6398de3a75b0c39" dependencies = [ "clap", ] @@ -483,7 +482,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -525,7 +524,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -691,7 +690,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -702,7 +701,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -739,7 +738,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -832,7 +831,7 @@ dependencies = [ "dfirtk-eventdata", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -873,7 +872,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1035,7 +1034,7 @@ dependencies = [ "crc32fast", "dialoguer", "encoding", - "hashbrown", + "hashbrown 0.14.5", "indoc", "log", "quick-xml", @@ -1062,9 +1061,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -1072,9 +1071,9 @@ dependencies = [ [[package]] name = "flow-record" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ae63f7bfd1e8412006223dc7df0e17060d7667bdbd73ce8ab2a33e1191f2394" +checksum = "2c97225c1c7a29ae6038e63acac95a61a5e4c47db226652dc621e25a6cfed21c" dependencies = [ "binrw", "bitflags 2.6.0", @@ -1090,9 +1089,9 @@ dependencies = [ [[package]] name = "flow-record-common" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b75fa5020fb9807f66c0229b5ebd8cb93440531865ebf8f6d3d28c2ea1aaa4d" +checksum = "2e160d0a8bbd51e5a3712a4c790a406b5aa64a4eda5cdb0520fe9bd272e7cf84" dependencies = [ "binrw", "chrono", @@ -1106,16 +1105,16 @@ dependencies = [ [[package]] name = "flow-record-derive" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fd6daf453a4d3cf7dc047a603dca62d4396a8cbf1e43cbbcd86f94de1e3af9" +checksum = "bb813a896b9340cab6b932d3282a38f2bbb65869a9ce78c9ac20d45152bf9ee7" dependencies = [ "chrono", "darling", "flow-record-common", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1173,7 +1172,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1192,6 +1191,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.3.2" @@ -1259,12 +1264,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1374,7 +1379,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1574,7 +1579,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1634,9 +1639,12 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "ouroboros" @@ -1660,7 +1668,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1759,9 +1767,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -1848,7 +1856,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1874,7 +1882,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "version_check", "yansi 1.0.1", ] @@ -1960,9 +1968,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1972,9 +1980,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1983,9 +1991,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rmp" @@ -2068,7 +2076,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2196,7 +2204,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2218,9 +2226,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2229,9 +2237,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -2262,12 +2270,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2303,7 +2311,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2431,7 +2439,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -2453,7 +2461,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2756,7 +2764,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2776,7 +2784,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3e0ab39..b039d3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,8 @@ clap = {version = "4.5", features = ["derive", "wrap_help", "cargo"] } clap-verbosity-flag = "2.0.0" csv = "1.2.2" encoding_rs = "0.8" -flow-record = "0.4.4" +flow-record = "0.4.6" +#flow-record = {path="../flow-record"} ## setting release_max_level_info conflicts with evtx # log = {version = "0.4", features = [ "release_max_level_info" ]} diff --git a/src/bin/evtx2bodyfile/main.rs b/src/bin/evtx2bodyfile/main.rs index d17f153..469a13b 100644 --- a/src/bin/evtx2bodyfile/main.rs +++ b/src/bin/evtx2bodyfile/main.rs @@ -12,6 +12,7 @@ mod cli; mod evtx_file; mod output_format; mod output_writer; +mod value_map; #[macro_use] mod macros; diff --git a/src/bin/evtx2bodyfile/output_writer.rs b/src/bin/evtx2bodyfile/output_writer.rs index 8e52607..d072d42 100644 --- a/src/bin/evtx2bodyfile/output_writer.rs +++ b/src/bin/evtx2bodyfile/output_writer.rs @@ -1,16 +1,15 @@ use std::io::Write; +use anyhow::anyhow; use chrono::{DateTime, Utc}; use dfirtk_eventdata::{ActivityId, EventId, ProcessId, RelatedActivityId}; use evtx::SerializedEvtxRecord; use flow_record::prelude::{FlowRecord, Serializer}; -use flow_record::{ - prelude::{rmpv, FieldType}, - ToMsgPackValue, -}; + use flow_record::derive::FlowRecord; use crate::bf_data::BfData; +use crate::value_map::ValueMap; #[derive(Default)] pub(crate) struct BodyfileOutputWriter(W); @@ -66,25 +65,49 @@ where #[derive(FlowRecord)] #[flow_record(version = 1, source = "evtx2bodyfile", classification = "evtx")] -struct WindowsEvent { +struct WindowsEvent<'v> { record_timestamp: DateTime, event_id: u16, event_record_id: u64, activity_id: String, related_activity_id: String, process_id: u64, + channel: Option<&'v str>, + provider_name: Option<&'v str>, + provider_guid: Option<&'v str>, + level: u64, + computer: Option<&'v str>, + event_source_name: Option<&'v str>, + + event_data: Option>, + user_data: Option>, } -impl TryFrom<&SerializedEvtxRecord> for WindowsEvent { +impl<'v> TryFrom<&'v SerializedEvtxRecord> for WindowsEvent<'v> { type Error = anyhow::Error; - fn try_from(record: &SerializedEvtxRecord) -> Result { + fn try_from(record: &'v SerializedEvtxRecord) -> Result { let record_timestamp = record.timestamp; let event_id = EventId::try_from(record)?.0; let event_record_id = record.event_record_id; let activity_id = ActivityId::try_from(record)?.value().to_string(); let related_activity_id = RelatedActivityId::try_from(record)?.to_string(); let process_id = ProcessId::try_from(record)?.0; + + let event = record + .data + .get("Event") + .ok_or_else(|| anyhow!("missing 'Event' entry"))?; + let event_data = event.opt_value("/EventData"); + let user_data = event.opt_value("/UserData"); + + let channel = event.opt_string("/System/Channel"); + let computer = event.opt_string("/System/Computer"); + let level = event.number("/System/Level")?; + let provider_name = event.opt_string("/System/Provider/#attributes/Name"); + let provider_guid = event.opt_string("/System/Provider/#attributes/Guid"); + let event_source_name = event.opt_string("/System/Provider/#attributes/EventSourceName"); + Ok(Self { record_timestamp, event_id, @@ -92,30 +115,40 @@ impl TryFrom<&SerializedEvtxRecord> for WindowsEvent { activity_id, related_activity_id, process_id, + event_data, + user_data, + channel, + provider_name, + provider_guid, + level, + computer, + event_source_name, }) } } -struct ValueWrapper<'v>(&'v serde_json::Value); -impl<'v> From<&'v serde_json::Value> for ValueWrapper<'v> { - fn from(value: &'v serde_json::Value) -> Self { - Self(value) - } +trait EvtxValues { + fn opt_value(&self, id: &str) -> Option>; + fn opt_string(&self, id: &str) -> Option<&str>; + fn number(&self, id: &str) -> anyhow::Result; } -impl<'v> ToMsgPackValue for ValueWrapper<'v> { - fn to_msgpack_value(self) -> rmpv::Value { - match self.0 { - serde_json::Value::Null => rmpv::Value::Nil, - serde_json::Value::Bool(_) => todo!(), - serde_json::Value::Number(_number) => todo!(), - serde_json::Value::String(_) => todo!(), - serde_json::Value::Array(_vec) => todo!(), - serde_json::Value::Object(_map) => todo!(), - } +impl EvtxValues for serde_json::Value { + fn opt_value(&self, id: &str) -> Option> { + self.pointer(id).map(ValueMap::from) + } + + fn opt_string(&self, id: &str) -> Option<&str> { + self.pointer(id) + .and_then(|v| match v { + serde_json::Value::String(s) => Some(&s[..]), + _ => None, + }) } - fn field_type() -> FieldType { - FieldType::String + fn number(&self, id: &str) -> anyhow::Result { + self.pointer(id) + .and_then(|v| v.as_u64()) + .ok_or_else(|| anyhow!("missing 'Level' entry")) } } diff --git a/src/bin/evtx2bodyfile/value_map.rs b/src/bin/evtx2bodyfile/value_map.rs new file mode 100644 index 0000000..08e78de --- /dev/null +++ b/src/bin/evtx2bodyfile/value_map.rs @@ -0,0 +1,44 @@ +use flow_record::{ + prelude::{rmpv, FieldType}, + ToMsgPackValue, +}; + +pub struct ValueMap<'v>(&'v serde_json::Value); + +impl<'v> From<&'v serde_json::Value> for ValueMap<'v> { + fn from(value: &'v serde_json::Value) -> Self { + Self(value) + } +} + +impl<'v> ToMsgPackValue for ValueMap<'v> { + fn to_msgpack_value(self) -> rmpv::Value { + rmpv::Value::Array(vec![json_to_msgpack(self.0)]) + } + + fn field_type() -> FieldType { + FieldType::DictList + } +} + +fn json_to_msgpack(v: &serde_json::Value) -> rmpv::Value { + match v { + serde_json::Value::Null => rmpv::Value::Nil, + serde_json::Value::Bool(b) => rmpv::Value::Boolean(*b), + serde_json::Value::Number(n) => rmpv::Value::String(n.to_string().into()), + serde_json::Value::String(s) => rmpv::Value::String(s.to_string().into()), + serde_json::Value::Array(vec) => { + rmpv::Value::Array(vec.iter().map(json_to_msgpack).collect()) + } + serde_json::Value::Object(map) => rmpv::Value::Map( + map.into_iter() + .map(|(k, v)| { + ( + rmpv::Value::String(k.to_string().into()), + json_to_msgpack(v), + ) + }) + .collect(), + ), + } +} From 64eb68525a9b8872020ead439c8552e0b4230fc6 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 4 Oct 2024 14:03:49 +0200 Subject: [PATCH 6/6] regdump knows flow-record --- Cargo.lock | 12 +++---- Cargo.toml | 2 +- src/bin/regdump/cli.rs | 22 +++++++++++-- src/bin/regdump/main.rs | 72 +++++++++++++++++++++++++++++++---------- 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b1fb7e..90258f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1071,9 +1071,9 @@ dependencies = [ [[package]] name = "flow-record" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c97225c1c7a29ae6038e63acac95a61a5e4c47db226652dc621e25a6cfed21c" +checksum = "8532627151728b1edcc197cd7fba2eb37808a303660afabb5612b071d4441e72" dependencies = [ "binrw", "bitflags 2.6.0", @@ -1089,9 +1089,9 @@ dependencies = [ [[package]] name = "flow-record-common" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e160d0a8bbd51e5a3712a4c790a406b5aa64a4eda5cdb0520fe9bd272e7cf84" +checksum = "3cac7bb4b4869a9efb5daf49fc148cedbc0a78c602804a620500e981fa1ac6ed" dependencies = [ "binrw", "chrono", @@ -1105,9 +1105,9 @@ dependencies = [ [[package]] name = "flow-record-derive" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813a896b9340cab6b932d3282a38f2bbb65869a9ce78c9ac20d45152bf9ee7" +checksum = "1254d93653dca708e568ce7251ae565e0948db501ea5a0c2a06d643e89f636ce" dependencies = [ "chrono", "darling", diff --git a/Cargo.toml b/Cargo.toml index b039d3c..cf09366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,7 @@ clap = {version = "4.5", features = ["derive", "wrap_help", "cargo"] } clap-verbosity-flag = "2.0.0" csv = "1.2.2" encoding_rs = "0.8" -flow-record = "0.4.6" +flow-record = "0.4.7" #flow-record = {path="../flow-record"} ## setting release_max_level_info conflicts with evtx diff --git a/src/bin/regdump/cli.rs b/src/bin/regdump/cli.rs index d32dc64..1ab5d49 100644 --- a/src/bin/regdump/cli.rs +++ b/src/bin/regdump/cli.rs @@ -1,9 +1,25 @@ use std::{path::PathBuf, fs::File}; -use clap::{Parser, ValueHint}; +use clap::{Parser, ValueEnum, ValueHint}; use dfir_toolkit::common::HasVerboseFlag; use log::LevelFilter; use nt_hive2::{HiveParseMode, Hive}; +use strum_macros::Display; + +#[derive(ValueEnum, Clone, Display)] +pub (crate) enum OutputFormat { + /// registry export format format + #[strum(serialize = "reg")] + Reg, + + /// bodyfile format + #[strum(serialize = "bodyfile")] + Bodyfile, + + /// flow record format () + #[strum(serialize = "record")] + Record +} /// parses registry hive files and prints a bodyfile #[derive(Parser)] @@ -19,8 +35,8 @@ pub (crate) struct Cli { pub (crate) logfiles: Vec, /// print as bodyfile format - #[clap(short('b'), long("bodyfile"))] - pub (crate) display_bodyfile: bool, + #[clap(short('F'), long("format"), default_value_t=OutputFormat::Reg)] + pub (crate) format: OutputFormat, /// ignore the base block (e.g. if it was encrypted by some ransomware) #[clap(short('I'), long)] diff --git a/src/bin/regdump/main.rs b/src/bin/regdump/main.rs index 5948cde..b440a25 100644 --- a/src/bin/regdump/main.rs +++ b/src/bin/regdump/main.rs @@ -1,11 +1,14 @@ use anyhow::{bail, Result}; +use chrono::{DateTime, Utc}; use dfir_toolkit::common::bodyfile::Bodyfile3Line; use dfir_toolkit::common::{FancyParser, FormattableDatetime}; +use flow_record::derive::*; +use flow_record::prelude::*; use nt_hive2::*; use simplelog::{Config, SimpleLogger}; use std::fs::File; -use std::io::{Read, Seek}; +use std::io::{Read, Seek, Write}; use crate::cli::Cli; @@ -15,12 +18,18 @@ fn main() -> Result<()> { let mut cli = Cli::parse_cli(); let _ = SimpleLogger::init(cli.verbose.log_level_filter(), Config::default()); - fn do_print_key(hive: &mut Hive, root_key: &KeyNode, cli: &Cli) -> Result<()> + fn do_print_key( + hive: &mut Hive, + root_key: &KeyNode, + cli: &Cli, + ser: &mut Serializer, + ) -> Result<()> where RS: Read + Seek, + W: Write, { let mut path = Vec::new(); - print_key(hive, root_key, &mut path, cli) + print_key(hive, root_key, &mut path, cli, ser) } match File::open(&cli.hive_file) { @@ -45,7 +54,8 @@ fn main() -> Result<()> { }; let root_key = &clean_hive.root_key_node().unwrap(); - do_print_key(&mut clean_hive, root_key, &cli).unwrap(); + let mut serializer = Serializer::new(std::io::stdout()); + do_print_key(&mut clean_hive, root_key, &cli, &mut serializer).unwrap(); } Err(why) => { eprintln!( @@ -59,35 +69,63 @@ fn main() -> Result<()> { Ok(()) } -fn print_key( +#[derive(FlowRecord)] +#[flow_record(version = 1, source = "regdump", classification = "reg")] +struct RegistryKey<'d> { + path: String, + ctime: &'d DateTime, + values_count: usize +} + +fn print_key( hive: &mut Hive, keynode: &KeyNode, path: &mut Vec, cli: &Cli, + ser: &mut Serializer, ) -> Result<()> where RS: Read + Seek, + W: Write, { path.push(keynode.name().to_string()); let current_path = path.join("\\"); - if cli.display_bodyfile { - let bf_line = Bodyfile3Line::new() - .with_name(¤t_path) - .with_ctime(keynode.timestamp().into()); - println!("{}", bf_line); - } else { - if cli.hide_timestamps { - println!("\n[{}]", ¤t_path); - } else { - println!("\n[{}]; {}", ¤t_path, FormattableDatetime::from(keynode.timestamp())); + + match cli.format { + cli::OutputFormat::Reg => { + if cli.hide_timestamps { + println!("\n[{}]", ¤t_path); + } else { + println!( + "\n[{}]; {}", + ¤t_path, + FormattableDatetime::from(keynode.timestamp()) + ); + } + + print_values(keynode); } - print_values(keynode); + cli::OutputFormat::Bodyfile => { + let bf_line = Bodyfile3Line::new() + .with_name(¤t_path) + .with_ctime(keynode.timestamp().into()); + println!("{}", bf_line); + } + + cli::OutputFormat::Record => { + let key = RegistryKey { + path: current_path, + ctime: keynode.timestamp(), + values_count: keynode.values().len() + }; + ser.serialize(key)?; + } } for sk in keynode.subkeys(hive).unwrap().iter() { - print_key(hive, &sk.borrow(), path, cli)?; + print_key(hive, &sk.borrow(), path, cli, ser)?; } path.pop();