fixed a bug on new project creation where the config file path wasn't

being passed correctly. Also started the groundwork for adding more of
my rust based tooling to tetanus.
This commit is contained in:
2026-08-27 12:23:18 -05:00
parent 9ed69b03a0
commit 0bb3a125aa
6 changed files with 1469 additions and 167 deletions
Generated
+583 -15
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
version = "0.6.1"
@@ -329,6 +335,20 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auto_generate_cdp"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "359220d0b9360b79d17d648d0a3ba1e792ec36bdbc227c8fd0351df3a0415704"
dependencies = [
"convert_case 0.8.0",
"proc-macro2",
"quote",
"serde",
"serde_json",
"ureq",
]
[[package]]
name = "autocfg"
version = "1.5.1"
@@ -364,6 +384,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]]
name = "bit-set"
version = "0.5.3"
@@ -670,6 +696,15 @@ dependencies = [
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "convert_case"
version = "0.10.0"
@@ -719,6 +754,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
@@ -826,14 +870,38 @@ dependencies = [
"cmov",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core 0.20.11",
"darling_macro 0.20.11",
]
[[package]]
name = "darling"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [
"darling_core",
"darling_macro",
"darling_core 0.23.0",
"darling_macro 0.23.0",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.117",
]
[[package]]
@@ -849,13 +917,24 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
"syn 2.0.117",
]
[[package]]
name = "darling_macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core",
"darling_core 0.23.0",
"quote",
"syn 2.0.117",
]
@@ -895,6 +974,37 @@ dependencies = [
"powerfmt",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.117",
]
[[package]]
name = "derive_more"
version = "2.1.1"
@@ -910,7 +1020,7 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case",
"convert_case 0.10.0",
"proc-macro2",
"quote",
"rustc_version",
@@ -1088,6 +1198,16 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -1100,6 +1220,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
@@ -1232,6 +1361,29 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "headless_chrome"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5754ca220578ad74a5d5174c8ca2ff956fd3b94025f870844de1e910b47dfee5"
dependencies = [
"anyhow",
"auto_generate_cdp",
"base64 0.22.1",
"derive_builder",
"log",
"rand 0.10.2",
"regex",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"tungstenite",
"url",
"which",
"winreg",
]
[[package]]
name = "heck"
version = "0.5.0"
@@ -1268,6 +1420,22 @@ dependencies = [
"digest",
]
[[package]]
name = "http"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "hybrid-array"
version = "0.4.14"
@@ -1277,12 +1445,116 @@ dependencies = [
"typenum",
]
[[package]]
name = "icu_collections"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
[[package]]
name = "icu_properties"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
[[package]]
name = "icu_provider"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "indexmap"
version = "2.14.0"
@@ -1327,7 +1599,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971"
dependencies = [
"darling",
"darling 0.23.0",
"indoc",
"proc-macro2",
"quote",
@@ -1448,6 +1720,12 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
[[package]]
name = "litrs"
version = "1.0.0"
@@ -1465,9 +1743,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.31"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f"
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "lru"
@@ -1524,6 +1802,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "mio"
version = "1.2.1"
@@ -1839,10 +2127,16 @@ version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
"base64",
"base64 0.22.1",
"serde_core",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pest"
version = "2.8.6"
@@ -1992,12 +2286,30 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "potential_utf"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
@@ -2046,6 +2358,16 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
]
[[package]]
name = "rand"
version = "0.10.2"
@@ -2057,12 +2379,31 @@ dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rand_core"
version = "0.10.1"
@@ -2199,9 +2540,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.12.3"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
@@ -2211,9 +2552,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.14"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -2222,9 +2563,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rhai"
@@ -2317,6 +2658,7 @@ dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -2482,6 +2824,17 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "sha1"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.9"
@@ -2530,6 +2883,12 @@ dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "siphasher"
version = "1.0.3"
@@ -2579,12 +2938,29 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "socks"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
dependencies = [
"byteorder",
"libc",
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -2724,7 +3100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
dependencies = [
"anyhow",
"base64",
"base64 0.22.1",
"bitflags 2.12.1",
"fancy-regex",
"filedescriptor",
@@ -2769,6 +3145,7 @@ dependencies = [
"clipboard",
"crossterm",
"fs_extra",
"headless_chrome",
"ipnet",
"keyring",
"rand 0.10.2",
@@ -2888,6 +3265,16 @@ dependencies = [
"crunchy",
]
[[package]]
name = "tinystr"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tokio"
version = "1.52.3"
@@ -2987,6 +3374,22 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tungstenite"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
dependencies = [
"bytes",
"data-encoding",
"http",
"httparse",
"log",
"rand 0.9.5",
"sha1",
"thiserror 2.0.18",
]
[[package]]
name = "typenum"
version = "1.20.1"
@@ -3061,6 +3464,60 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d"
dependencies = [
"base64 0.23.1",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"socks",
"ureq-proto",
"utf8-zero",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613"
dependencies = [
"base64 0.23.1",
"http",
"httparse",
"log",
]
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "utf8-zero"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
@@ -3175,6 +3632,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "wezterm-bidi"
version = "0.2.3"
@@ -3247,6 +3713,15 @@ dependencies = [
"wezterm-dynamic",
]
[[package]]
name = "which"
version = "8.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f"
dependencies = [
"libc",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -3492,12 +3967,28 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [
"cfg-if",
"windows-sys 0.61.2",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "writeable"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]]
name = "x11-clipboard"
version = "0.3.3"
@@ -3545,6 +4036,29 @@ dependencies = [
"time",
]
[[package]]
name = "yoke"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
"synstructure",
]
[[package]]
name = "zbus"
version = "5.16.0"
@@ -3637,12 +4151,66 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerotrie"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "zmij"
version = "1.0.21"
+1
View File
@@ -10,6 +10,7 @@ clap = { version = "4.6.1", features = ["derive"] }
clipboard = "0.5.0"
crossterm = "0.29.0"
fs_extra = "1.3.0"
headless_chrome = "1.0.22"
ipnet = "2.12.0"
keyring = "4.1.6"
rand = "0.10.2"
+55 -3
View File
@@ -49,6 +49,7 @@ pub fn startup(
let response = get_user_input("which config would you like to load?")?;
if let Some(path) = selections.get(&response) {
app.load_config(PathBuf::from(path), true)?;
println!("entering tui...");
run_tui(app, rx, handles.clone())?;
} else {
eprintln!("error invalid selection! exiting...");
@@ -56,11 +57,13 @@ pub fn startup(
}
} else {
app.load_config(state.config_file.clone(), true)?;
println!("entering tui...");
run_tui(app, rx, handles.clone())?;
}
if nested {
let (tx, rx) = channel();
state.main_tx = tx;
println!("returning to prevous tui...");
run_tui(state, rx, handles.clone())?;
}
Ok(())
@@ -475,6 +478,35 @@ pub fn run_tui(
);
}
"CMD" => {
if let Some((cmd, data)) =
data.split_once("|")
{
match cmd.trim() {
"SET_NAME" => {
locked_server
.name = data
.trim()
.to_string();
}
"LIST_PROJECTS" => {
let _ = ToolMessage::Input((locked_server.client_id.clone(), "list_projects".to_string()));
}
_ => {
let msg = format!(
"MSG FROM: {}",
data.trim()
);
let _ = tx_clone.send(
ToolMessage::Output((
locked_server
.client_id
.clone(),
msg,
)),
);
}
}
}
if data.contains("SET_NAME") {
if let Some((_, name)) =
data.split_once("|")
@@ -485,7 +517,7 @@ pub fn run_tui(
}
} else {
let _ = tx_clone.send(
ToolMessage::Input((
ToolMessage::Output((
locked_server
.client_id
.clone(),
@@ -680,18 +712,38 @@ pub fn run_tui(
}
} else {
if args == "" {
let _ = state.execute_command(command, None, 0);
match state.execute_command(command, None, 0) {
Ok(_) => {}
Err(e) => {
let _ = state.main_tx.send(
ToolMessage::Output((
0,
e.to_string(),
)),
);
}
}
} else {
let _ = state.execute_command(
match state.execute_command(
command,
Some(args.to_string()),
0,
) {
Ok(_) => {}
Err(e) => {
let _ = state.main_tx.send(
ToolMessage::Output((
0,
e.to_string(),
)),
);
}
}
}
}
}
}
}
state.curent_intput.clear();
}
history_index = state.history.len();
+642 -94
View File
@@ -14,13 +14,17 @@ use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::IpAddr;
use std::net::TcpStream;
use std::path::PathBuf;
use std::process::{Command, CommandArgs, Stdio};
use std::process::{Command, Stdio};
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel};
use std::time::Duration;
use std::time::{self, Instant};
use walkdir::WalkDir;
use crate::rust_tools::rustwitness;
pub mod funcs;
pub mod rust_tools;
pub mod server;
pub mod victim;
@@ -242,6 +246,10 @@ impl AppState {
.to_string(),
);
self.help.push("parse_scope\nparse the projects general.md notes file for the scope copied from the workbook\n".to_string());
self.help.push("parse_portscan | pps\nParse a services.tsv file exported from cobalt stirke in your current project's files directory".to_string());
self.help
.push("stop_db\nrestart this project's distrobox".to_string());
self.help.push("rustwitness\nRun a scan on the hosts in the urls.txt file in yoru project files directory that captures screenshots of those urls, optionally through a proxy.".to_string());
self.help.push("exit\nquit the tool\n".to_string());
self.initialize_modules();
return Ok(());
@@ -392,6 +400,10 @@ impl AppState {
} else {
out_vec.push(format!("status: upcoming"));
}
out_vec.push(format!(
"Num hosts: {}",
&self.projects[self.selected_project].hosts.iter().count()
));
for line in out_vec {
let _ = self.main_tx.send(ToolMessage::Output((rid, line)));
}
@@ -438,7 +450,7 @@ impl AppState {
let project = self.projects[self.selected_project].clone();
let mut config_file = project.config_folder.clone();
config_file.pop();
if let Err(e) = remove_dir_all(&project.config_folder) {
if let Err(e) = remove_dir_all(config_file) {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
@@ -947,6 +959,75 @@ impl AppState {
});
}
}
"collab_with" => {
if let Some(args) = command_args {
if let Ok(id) = args.trim().parse::<usize>() {
if let Some(server) = self.servers.get(self.selected_server) {
if let Ok(mut lock) = server.lock() {
lock.action_que.push(format!("COLLAB|{}", id));
}
}
}
}
}
"collab" => {
if let Some(args) = command_args {
if let Some(server) = self.servers.get(self.selected_server) {
if let Ok(mut lock) = server.lock() {
lock.action_que.push(format!("CMD|{}", args));
}
}
}
}
"parse_port_scan" | "pps" => {
self.parse_cs_portscan()?;
}
"stop_db" => {
if let Some(project) = self.projects.get_mut(self.selected_project) {
if let Some(db) = project.db.as_mut() {
db.stop(self.main_tx.clone(), rid)?;
}
}
}
"rustwitness" => {
if let Some(project) = self.projects.get(self.selected_project) {
if let Some(args) = command_args {
if let Some((outfile, proxy)) = args.split_once(" ") {
let mut proxy_string = None;
if let Some((ip, port)) = proxy.split_once(" ") {
proxy_string = Some(format!("socks5://{}:{}", ip, port));
}
WalkDir::new(&project.files)
.into_iter()
.filter(|res| res.is_ok())
.for_each(|res| {
if let Ok(entry) = res {
if entry.file_name().to_string_lossy().contains("urls.txt")
{
let input = PathBuf::from(entry.path());
let mut output = project.files.clone();
output.push(outfile.trim());
let proxy_intput = proxy_string.clone();
let tx = self.main_tx.clone();
self.workers.spawn(move || {
rustwitness(input, output, proxy_intput, tx)
});
}
}
});
}
} else {
let _ = self.main_tx.send(ToolMessage::Output((
0,
"[error] incorrect command usage!".to_string(),
)));
let _ = self.main_tx.send(ToolMessage::Output((
0,
"usage: rustwitness output_file_name optional_proxy_ip optional_proxy_port".to_string(),
)));
}
}
}
"exit" => {
self.save_all()?;
let _ = self.main_tx.send(ToolMessage::AppStateExit);
@@ -966,7 +1047,7 @@ impl AppState {
.module_loader
.asts
.get(command_name)
.ok_or_else(|| format!("Command not found: {}", command_name))?
.ok_or_else(|| format!("[error] Command not found: {}", command_name))?
.clone();
let engine = Arc::clone(&self.module_loader.engine);
let cmd_meta = self
@@ -1001,12 +1082,28 @@ impl AppState {
}
scope.push("config", map);
}
"distrobox" => {
if let Some(db) = self.projects[self.selected_project].db.clone() {
scope.push("distrobox", db);
}
}
_ if arg_requirement.starts_with("string") => {
let input_val = command_args
.clone()
.unwrap_or_else(|| self.curent_intput.clone());
scope.push("input_string", input_val);
}
_ if arg_requirement.starts_with("bool") => {
let mut input_val = false;
if command_args
.clone()
.unwrap_or_else(|| self.curent_intput.clone())
.contains("true")
{
input_val = true;
}
scope.push("input_bool", input_val);
}
_ => {}
}
}
@@ -1053,7 +1150,7 @@ impl AppState {
Err(err) => {
let _ = tx.send(ToolMessage::Output((
rid,
format!("Script [error] in execution: {}", err),
format!("[error] in script execution: {}", err),
)));
}
}
@@ -1079,20 +1176,44 @@ impl AppState {
name: String,
rid: usize,
) -> Result<(), Box<dyn Error>> {
let mut main_config_file = self.config_file.clone();
main_config_file.pop();
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("{} is the main config folder.", main_config_file.display()),
)));
let template_box = self.config.get("template_box").unwrap();
let project_files = PathBuf::from(self.config.get("upcoming_files").unwrap())
.join(format!("{}/{}", org, name));
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("{} is the project_files folder", project_files.display()),
)));
let project_notes = PathBuf::from(self.config.get("upcoming_notes").unwrap())
.join(format!("{}/{}", org, name));
let mut template_path = self.config_file.clone();
let mut project_conf_folder = self.config_file.clone();
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("{} is the project_notes folder", project_notes.display()),
)));
let mut template_path = main_config_file.clone();
let mut project_conf_folder = main_config_file.clone();
let tools = self.config.get("tools").unwrap();
template_path.pop();
template_path.push("note_templates");
project_conf_folder.pop();
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("{} is the note template folder", template_path.display()),
)));
project_conf_folder.push(format!("projects/{}-{}", org, name));
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"{} is the project config folder",
project_conf_folder.display()
),
)));
let mut options = CopyOptions::new();
options.overwrite = true;
if !project_files.exists() {
match create_dir_all(&project_files) {
Ok(_) => {}
Err(e) => {
@@ -1105,6 +1226,8 @@ impl AppState {
)));
}
}
}
if !project_notes.exists() {}
match create_dir_all(&project_notes) {
Ok(_) => {}
Err(e) => {
@@ -1117,6 +1240,7 @@ impl AppState {
)));
}
}
if !project_conf_folder.exists() {
match create_dir_all(&project_conf_folder) {
Ok(_) => {}
Err(e) => {
@@ -1129,6 +1253,9 @@ impl AppState {
)));
}
}
}
let mut project_config_file = project_conf_folder.clone();
project_config_file.push("project.conf");
for entry in read_dir(template_path)? {
let entry = entry?;
let template_name_os = entry.file_name();
@@ -1179,11 +1306,20 @@ impl AppState {
notes: project_notes,
files: project_files,
hosts: Vec::new(),
config_folder: project_conf_folder,
config_folder: project_config_file,
current: false,
db: Some(db),
scope: Vec::new(),
};
match new_project.save_config() {
Ok(_) => {}
Err(e) => {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!("[error] saving project! {e}"),
)));
}
}
new_project.save_config()?;
self.projects.push(new_project);
return Ok(());
@@ -1458,6 +1594,381 @@ impl AppState {
self.prompt.execute_command.clear();
Ok(())
}
pub fn parse_cs_portscan(&mut self) -> Result<(), Box<dyn Error>> {
if let Some(project) = self.projects.get_mut(self.selected_project) {
let walk = WalkDir::new(project.files.clone());
walk.into_iter().for_each(|res| {
if let Ok(entry) = res {
if entry.file_name().to_string_lossy().contains("services.tsv") {
if let Ok(scan) = read_to_string(entry.path()) {
let mut url_lines = Vec::new();
let mut web_host_lines = Vec::new();
let mut rdp_host_lines = Vec::new();
let mut ftp_host_lines = Vec::new();
let mut telnet_host_lines = Vec::new();
let mut winrm_host_lines = Vec::new();
let mut mysql_host_lines = Vec::new();
let mut mssql_host_lines = Vec::new();
let mut snmp_host_lines = Vec::new();
let mut kerberos_host_lines = Vec::new();
let mut ldap_host_lines = Vec::new();
let mut smb_host_lines = Vec::new();
let mut unknonw_host_lines = Vec::new();
let mut ssh_host_lines = Vec::new();
scan.lines().into_iter().for_each(|line| {
if line.len() > 0 {
let data: Vec<&str> = line.split_whitespace().collect();
match data[1].trim() {
"80" | "443" | "8080" | "8443" | "8000" | "4433" => {
let http_line =
format!("http://{}:{}", data[0], data[1]);
let https_line =
format!("https://{}:{}", data[0], data[1]);
url_lines.push(http_line);
url_lines.push(https_line);
web_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"3389" => {
rdp_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"21" => {
ftp_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"23" => {
telnet_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"22" => {
ssh_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"5985" | "5986" => {
winrm_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"3306" => {
mysql_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"1433" => {
mssql_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"161" => {
snmp_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"88" => {
kerberos_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"389" | "636" => {
ldap_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
"445" | "139" => {
smb_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
_ => {
unknonw_host_lines.push(data[0].to_string());
let mut existing = false;
project
.hosts
.iter_mut()
.filter(|h| h.ip == data[0].to_string())
.for_each(|h| {
h.add_port(data[1]);
existing = true;
});
if !existing {
let mut new_host = Host::new();
new_host.ip = data[0].trim().to_string();
new_host.add_port(data[1]);
project.add_host(new_host);
}
}
}
}
});
if !url_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("urls.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", url_lines.join("\n"));
}
}
if !web_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("web_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", web_host_lines.join("\n"));
}
}
if !rdp_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("rdp_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", rdp_host_lines.join("\n"));
}
}
if !ftp_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("ftp_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", ftp_host_lines.join("\n"));
}
}
if !telnet_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("telnet_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", telnet_host_lines.join("\n"));
}
}
if !winrm_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("winrm_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", winrm_host_lines.join("\n"));
}
}
if !mysql_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("mysql_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", mysql_host_lines.join("\n"));
}
}
if !mssql_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("mssql_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", mssql_host_lines.join("\n"));
}
}
if !snmp_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("snmp_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", snmp_host_lines.join("\n"));
}
}
if !kerberos_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("kerberos_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", kerberos_host_lines.join("\n"));
}
}
if !ldap_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("ldap_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", ldap_host_lines.join("\n"));
}
}
if !smb_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("smb_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", smb_host_lines.join("\n"));
}
}
if !unknonw_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("unknown_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", unknonw_host_lines.join("\n"));
}
}
if !ssh_host_lines.is_empty() {
let mut file_path = project.files.clone();
file_path.push("ssh_hosts.txt");
if let Ok(mut file) = File::create(file_path) {
let _ = write!(file, "{}", ssh_host_lines.join("\n"));
}
}
}
}
}
});
project.save_config()?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
@@ -1632,9 +2143,19 @@ impl Project {
self.hosts = hosts;
}
pub fn add_host(&mut self, host: Host) {
pub fn add_host(&mut self, mut host: Host) {
let mut new_id = 0;
self.hosts.iter().for_each(|h| {
if h.id > new_id {
new_id = h.id.clone();
}
});
new_id += 1;
if !self.hosts.iter().any(|h| h.ip == host.ip) {
host.id = new_id;
self.hosts.push(host);
}
}
pub fn config_folder(&mut self, path: PathBuf) {
self.config_folder = path;
@@ -1681,11 +2202,11 @@ impl Project {
println!("{} | {} config loaded!", self.org_name, self.name);
println!("loading hosts...");
}
let mut hosts_folder = self.config_folder.clone();
let mut users_folder = self.config_folder.clone();
hosts_folder.pop();
let mut conf_folder = self.config_folder.clone();
conf_folder.pop();
let mut hosts_folder = conf_folder.clone();
let mut users_folder = conf_folder.clone();
hosts_folder.push("hosts");
users_folder.pop();
users_folder.push("users");
let mut users = HashMap::new();
if users_folder.exists() {
@@ -1710,20 +2231,25 @@ impl Project {
}
}
if hosts_folder.exists() {
for entry in read_dir(hosts_folder)? {
let entry = entry?;
read_dir(hosts_folder)?.into_iter().for_each(|res| {
if let Ok(entry) = res {
let host_path = entry.path();
let mut new_host = Host::new();
let host_conf_text = read_to_string(host_path)?;
for line in host_conf_text.lines() {
println!("loading {}...", host_path.display());
if let Ok(host_conf_text) = read_to_string(host_path) {
host_conf_text.lines().into_iter().for_each(|line| {
if line.contains(": ") {
let (setting, data) = line.split_once(": ").unwrap();
match setting.trim() {
"ip" => new_host.ip = data.trim().to_string(),
"hostname" => new_host.hostname = data.trim().to_string(),
"control_port" => new_host.control_port = data.trim().parse()?,
"pwned" => new_host.pwned = data.trim().parse()?,
"id" => new_host.id = data.trim().parse()?,
"control_port" => {
new_host.control_port = data.trim().parse().unwrap();
}
"pwned" => {
new_host.pwned = data.trim().parse().unwrap();
}
"id" => new_host.id = data.trim().parse().unwrap(),
"findings" => {
new_host.findings = data
.trim()
@@ -1736,7 +2262,7 @@ impl Project {
.trim()
.split(",")
.into_iter()
.map(|port| port.parse().unwrap())
.map(|port| port.parse().unwrap_or_default())
.collect::<Vec<usize>>()
}
"users" => {
@@ -1750,8 +2276,11 @@ impl Project {
_ => {}
}
}
});
self.add_host(new_host);
}
}
});
}
if display {
println!("{} | {} loaded!", self.org_name, self.name);
@@ -1760,16 +2289,29 @@ impl Project {
}
pub fn save_config(&mut self) -> Result<(), Box<dyn Error>> {
create_dir_all(&self.config_folder)?;
let mut hosts_folder = self.config_folder.clone();
let mut conf_folder = self.config_folder.clone();
conf_folder.pop();
if !conf_folder.exists() {
match create_dir_all(&conf_folder) {
Ok(_) => {}
Err(e) => {
return Err(
format!("save_config: couldn't create project conf folder! {e}").into(),
);
}
}
}
let mut hosts_folder = conf_folder.clone();
hosts_folder.push("hosts");
let mut users_folder = self.config_folder.clone();
let mut users_folder = conf_folder.clone();
users_folder.push("users");
if !hosts_folder.exists() {
create_dir_all(&hosts_folder)?;
}
if !users_folder.exists() {
create_dir_all(&users_folder)?;
let mut conf_file = self.config_folder.clone();
conf_file.push("project.conf");
let mut file = File::create(conf_file)?;
}
let mut file = File::create(&self.config_folder)?;
let mut out_string = format!(
"org_name: {}\nname: {}\nnotes: {}\nfiles: {}\nscope: {}\n",
self.org_name,
@@ -1784,67 +2326,13 @@ impl Project {
out_string.push_str("stage: upcoming");
}
file.write_all(out_string.as_bytes())?;
if !self.hosts.is_empty() {
for host in &self.hosts {
let mut host_conf_file =
File::create(hosts_folder.join(format!("{}.conf", host.id)))?;
let mut out_string = format!(
"
ip: {}
hostname: {}
control_port: {}
pwned: {}
id: {}",
host.ip, host.hostname, host.control_port, host.pwned, host.id,
);
if !host.findings.is_empty() {
out_string.push_str(&format!("\nfindings: "));
out_string.push_str(&host.findings.join(","));
}
if !host.open_ports.is_empty() {
out_string.push_str(&format!("\nopen_ports: "));
out_string.push_str(
&host
.open_ports
.iter()
.map(|port| port.to_string())
.collect::<Vec<String>>()
.join(","),
);
}
if !host.users.is_empty() {
out_string.push_str(&format!("\nusers: "));
out_string.push_str(
&host
.users
.iter()
.map(|user| user.name.clone())
.collect::<Vec<String>>()
.join(","),
);
for user in &host.users {
let mut user_conf_file =
File::create(users_folder.join(format!("{}.conf", user.name)))?;
let mut user_string = format!("username: {}", &user.name);
user_string.push_str(&format!("\ncompromised: {}", user.compromised));
if user.hash.is_some() {
let hash = user.hash.clone().unwrap();
user_string.push_str(&format!("hash: {hash}"));
}
if user.password.is_some() {
let password = user.password.clone().unwrap();
user_string.push_str(&format!("password: {password}"));
}
if user.ticket.is_some() {
let ticket = user.ticket.clone().unwrap().display().to_string();
user_string.push_str(&format!("ticket: {ticket}"));
}
user_conf_file.write_all(user_string.as_bytes())?;
}
}
host_conf_file.write_all(out_string.as_bytes())?;
}
}
self.hosts
.iter_mut()
.try_for_each(|h| -> Result<(), Box<dyn Error>> {
let mut host_path = hosts_folder.clone();
host_path.push(format!("{}.conf", h.id));
h.save_config_file(host_path)
})?;
return Ok(());
}
}
@@ -1922,9 +2410,13 @@ impl Host {
self.findings = findings;
}
pub fn add_port(&mut self, port: usize) {
pub fn add_port(&mut self, port: &str) {
if let Ok(port) = port.trim().parse() {
if !self.open_ports.contains(&port) {
self.open_ports.push(port);
}
}
}
pub fn add_user(&mut self, user: User) {
self.users.push(user);
@@ -1933,6 +2425,37 @@ impl Host {
pub fn add_finding(&mut self, finding: String) {
self.findings.push(finding);
}
pub fn save_config_file(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> {
let mut host_file = File::create(path)?;
let mut out_string = format!(
"
ip: {}
hostname: {}
control_port: {}
pwned: {}
id: {}",
self.ip, self.hostname, self.control_port, self.pwned, self.id
);
if !self.findings.is_empty() {
out_string.push_str(&format!("\nfindings: "));
out_string.push_str(&self.findings.join(","));
}
if !self.open_ports.is_empty() {
out_string.push_str("\nopen_ports: ");
out_string.push_str(
&self
.open_ports
.iter()
.map(|port| port.to_string())
.collect::<Vec<String>>()
.join(","),
);
}
write!(host_file, "{}", out_string)?;
host_file.sync_all()?;
Ok(())
}
}
#[derive(Clone, Debug)]
@@ -2034,12 +2557,12 @@ impl ModuleLoader {
engine.register_fn("get_host", |hosts: &mut Vec<Host>, index: i64| {
hosts.get(index as usize).cloned().unwrap_or_else(Host::new)
});
engine.register_fn("Host", Host::new);
engine.register_fn("new_host", Host::new);
engine.register_fn("open_file", |path: &str| {
std::fs::read_to_string(path).unwrap_or_else(|_| String::new())
});
engine.register_fn("add_port", |host: &mut Host, port: i64| {
host.add_port(port as usize);
host.add_port(format!("{}", port).as_str());
});
engine.register_fn("expand_target", |s: &str| -> rhai::Array {
let mut results = rhai::Array::new();
@@ -2096,6 +2619,13 @@ impl ModuleLoader {
}
return out_string;
});
engine.register_fn("db_run", |p: &mut Project, command: String| -> String {
let mut out_string = format!("No distrobox configured for selected project.");
if let Some(db) = p.db.clone() {
out_string = db.run_command(&command);
}
return out_string;
});
Self {
engine: Arc::new(engine),
commands: HashMap::new(),
@@ -2317,7 +2847,6 @@ impl DistroBox {
let reader = BufReader::new(stdout);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
@@ -2327,7 +2856,6 @@ impl DistroBox {
let reader = BufReader::new(stderr);
for line in reader.lines().flatten() {
let _ = tx_out.send(ToolMessage::Output((rid, line.clone())));
println!("{}", line);
}
});
}
@@ -2382,6 +2910,26 @@ impl DistroBox {
let _ = term_cmd.spawn();
}
}
pub fn run_command(&self, cmd: &str) -> String {
let output = Command::new("distrobox")
.arg("enter")
.arg("-e")
.arg(format!("\"{}\"", cmd))
.output();
match output {
Ok(output) => {
if output.status.success() {
return String::from_utf8_lossy(&output.stdout).to_string();
} else {
return format!("[error] {}", String::from_utf8_lossy(&output.stdout));
}
}
Err(e) => {
return format!("[error] {e}");
}
}
}
}
#[derive(Clone)]
+88
View File
@@ -0,0 +1,88 @@
use crate::*;
use anyhow::Context;
use headless_chrome::{Browser, LaunchOptions};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
pub fn rustwitness(
input: PathBuf,
output: PathBuf,
proxy: Option<String>,
tx: Sender<ToolMessage>,
) {
fn capture_screenshot(
url: &str,
proxy: Option<String>,
output: PathBuf,
) -> Result<(), Box<dyn Error>> {
if !output.exists() {
create_dir_all(&output)?;
}
let mut launch_options = LaunchOptions::default();
let file_name = url.split("://").collect::<Vec<&str>>()[1].to_string();
if let Some(ref proxy_url) = proxy {
let proxy_arg = format!("--proxy-server={}", proxy_url);
launch_options
.args
.push(std::ffi::OsStr::new(Box::leak(proxy_arg.into_boxed_str())));
}
let browser = Browser::new(launch_options).context("Browser launch failed")?;
let tab = browser.new_tab().context("Failed to open tab")?;
tab.navigate_to(url).context("Navigation failed")?;
tab.wait_until_navigated()
.context("Waiting for load failed")?;
tab.wait_for_element("body")?;
tab.evaluate("document.readyState === 'complete'", false)?;
let mut file_path = output.clone();
let mut log_path = output.clone();
log_path.push("log.txt");
file_path.push(format!("{}.png", file_name));
let png_data = tab
.capture_screenshot(
headless_chrome::protocol::cdp::Page::CaptureScreenshotFormatOption::Png,
None,
None,
true,
)
.context("Screenshot capture failed")?;
fs::write(&file_path, png_data)
.context("Failed to write file")
.context(format!(
"failed to write png file! {}",
&file_path.display()
))?;
println!("Successfully captured: {}", url);
fs::write(&log_path, format!("{}\n", url).as_bytes())
.context("failed to write log file!")?;
Ok(())
}
match File::open(&input) {
Ok(file) => {
let urls: Vec<String> = BufReader::new(file)
.lines()
.filter_map(|line| line.ok())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
let _ = tx.send(ToolMessage::Output((
0,
format!("Starting capture of {} urls...", urls.len()),
)));
urls.par_iter().enumerate().for_each(|(_index, url)| {
if let Err(e) = capture_screenshot(url, proxy.clone(), output.clone()) {
let _ = tx.send(ToolMessage::Output((
0,
format!("[error] rustwitness: Failed to process {}: {:?}", url, e),
)));
}
});
let _ = tx.send(ToolMessage::Output((
0,
"[success] all urls scanned for screenshots!".to_string(),
)));
}
Err(e) => {
let _ = tx.send(ToolMessage::Output((0, format!("[error] rustwitness {e}"))));
}
}
}
+45
View File
@@ -22,6 +22,7 @@ use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
enum ClientAction {
Output(String),
Cmd(String),
Collab(String),
}
pub struct Client {
@@ -31,6 +32,7 @@ pub struct Client {
actions: Vec<ClientAction>,
controlling: usize,
controlled: usize,
collabing: Vec<usize>,
output_que: Vec<String>,
logged_in: bool,
timer: Duration,
@@ -201,6 +203,7 @@ where
actions: Vec::new(),
controlled: 0,
controlling: 0,
collabing: Vec::new(),
output_que: Vec::new(),
logged_in: false,
timer: Duration::from_secs(5),
@@ -312,6 +315,26 @@ where
dest_client.id, cmd
)));
}
else{
let mut colabs = Vec::new();
lock.clients.iter_mut().filter(|c| c.id == source_id).for_each(|c|{
if c.collabing.is_empty(){
c.actions.push(ClientAction::Output("Error no controlled or collabed clients associated with you!".to_string()));
}
else{
c.collabing.iter().for_each(|id|{
colabs.push(id.clone());
});
}
});
if !colabs.is_empty(){
colabs.iter().for_each(|id|{
lock.clients.iter_mut().filter(|c| &c.id == id).for_each(|c|{
c.actions.push(ClientAction::Cmd(data.to_string()));
});
});
}
}
}
"CONTROL" => {
let new_control_id = data.trim().parse::<usize>().unwrap();
@@ -429,6 +452,22 @@ where
c.remove = true;
});
}
"COLLAB" => {
let dest_id: usize = data.trim().parse().unwrap();
lock.clients
.iter_mut()
.filter(|c| c.id == source_id)
.for_each(|c| {
c.collabing.push(dest_id.clone());
});
lock.clients
.iter_mut()
.filter(|d| d.id == dest_id)
.for_each(|d| {
d.collabing.push(dest_id.clone());
});
println!("{} is collabing with {}", source_id, dest_id);
}
"SLEEP" => {
if let Ok(secs) = data.parse::<u64>() {
lock.clients
@@ -455,6 +494,9 @@ where
println!("adding {} to output", text);
messages.push(format!("OUTPUT|{}", text));
}
ClientAction::Collab(text) => {
println!("todo");
}
}
}
let full_message = messages.join("||");
@@ -523,6 +565,9 @@ where
println!("adding {} to output", text);
messages.push(format!("OUTPUT|{}", text));
}
ClientAction::Collab(text) => {
println!("todo");
}
}
}
let full_message = messages.join("||");