diff --git a/Cargo.lock b/Cargo.lock index 3dc322c..2e516f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,6 +378,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -1087,7 +1093,7 @@ checksum = "6120e7e5dcd1c90098e349def389e2797377cdbe6b465ad8fdd115c0c99d5cf2" dependencies = [ "cfg-if", "libc", - "socket2", + "socket2 0.6.4", "windows-sys 0.61.2", ] @@ -1273,6 +1279,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[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 = "form_urlencoded" version = "1.2.2" @@ -1420,6 +1441,25 @@ dependencies = [ "polyval", ] +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.19" @@ -1431,7 +1471,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.5.0", "indexmap", "slab", "tokio", @@ -1476,7 +1516,7 @@ dependencies = [ "tungstenite", "url", "which", - "winreg", + "winreg 0.56.0", ] [[package]] @@ -1515,6 +1555,17 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.5.0" @@ -1525,6 +1576,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.1.0" @@ -1532,7 +1594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", - "http", + "http 1.5.0", ] [[package]] @@ -1543,8 +1605,8 @@ checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", ] @@ -1554,6 +1616,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hybrid-array" version = "0.4.14" @@ -1563,6 +1631,30 @@ dependencies = [ "typenum", ] +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.11.1" @@ -1573,9 +1665,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", "httparse", "itoa", "pin-project-lite", @@ -1584,21 +1676,48 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http", - "hyper", + "http 1.5.0", + "hyper 1.11.1", "hyper-util", - "rustls", + "rustls 0.23.43", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1609,15 +1728,15 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.5.0", + "http-body 1.1.0", + "hyper 1.11.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2", - "system-configuration", + "socket2 0.6.4", + "system-configuration 0.7.0", "tokio", "tower-service", "tracing", @@ -1801,7 +1920,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2", + "socket2 0.6.4", "widestring", "windows-registry", "windows-result", @@ -2102,6 +2221,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.29.0" @@ -2351,12 +2487,49 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl" +version = "0.10.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" +dependencies = [ + "bitflags 2.12.1", + "cfg-if", + "foreign-types", + "libc", + "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.117", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "4.6.0" @@ -2624,8 +2797,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", - "socket2", + "rustls 0.23.43", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -2646,7 +2819,7 @@ dependencies = [ "rand_pcg", "ring", "rustc-hash", - "rustls", + "rustls 0.23.43", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -2664,7 +2837,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.4", "tracing", "windows-sys 0.61.2", ] @@ -2932,6 +3105,50 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg 0.50.0", +] + [[package]] name = "reqwest" version = "0.13.4" @@ -2944,12 +3161,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.11.1", + "hyper-rustls 0.27.9", "hyper-util", "js-sys", "log", @@ -2957,12 +3174,12 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.43", "rustls-pki-types", "rustls-platform-verifier", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower", "tower-http", "tower-service", @@ -3060,6 +3277,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.43" @@ -3071,7 +3300,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.13", "subtle", "zeroize", ] @@ -3088,6 +3317,15 @@ dependencies = [ "security-framework", ] +[[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 = "rustls-pemfile" version = "2.2.0" @@ -3118,10 +3356,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls", + "rustls 0.23.43", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki", + "rustls-webpki 0.103.13", "security-framework", "security-framework-sys", "webpki-root-certs", @@ -3134,6 +3372,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.13" @@ -3182,6 +3430,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "secret-service" version = "5.1.0" @@ -3232,9 +3490,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -3242,29 +3500,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -3284,6 +3542,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.7" @@ -3312,6 +3582,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "shodan-rust" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9429dacaebdc9c4fb30d435b07c489ac7bfcbfbfade4735eeed7b53f61d0d79e" +dependencies = [ + "reqwest 0.11.27", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "signal-hook" version = "0.3.18" @@ -3404,6 +3686,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.4" @@ -3509,6 +3801,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3544,6 +3842,17 @@ dependencies = [ "windows", ] +[[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 0.9.4", + "system-configuration-sys 0.5.0", +] + [[package]] name = "system-configuration" version = "0.7.0" @@ -3552,7 +3861,17 @@ checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ "bitflags 2.12.1", "core-foundation 0.9.4", - "system-configuration-sys", + "system-configuration-sys 0.6.0", +] + +[[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]] @@ -3659,15 +3978,19 @@ dependencies = [ "ratatui", "rayon", "rcgen", - "reqwest", + "reqwest 0.13.4", "rhai", "rustc-hash", - "rustls", - "rustls-pemfile", + "rustls 0.23.43", + "rustls-pemfile 2.2.0", + "serde", + "serde_derive", + "serde_json", + "shodan-rust", "sysinfo", "textwrap", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "trust-dns-resolver", "walkdir", ] @@ -3811,7 +4134,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] @@ -3827,13 +4150,33 @@ dependencies = [ "syn 2.0.117", ] +[[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-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls", + "rustls 0.23.43", "tokio", ] @@ -3890,7 +4233,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -3905,8 +4248,8 @@ dependencies = [ "bitflags 2.12.1", "bytes", "futures-util", - "http", - "http-body", + "http 1.5.0", + "http-body 1.1.0", "pin-project-lite", "tower", "tower-layer", @@ -4017,7 +4360,7 @@ checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.5.0", "httparse", "log", "rand 0.9.5", @@ -4124,12 +4467,12 @@ dependencies = [ "flate2", "log", "percent-encoding", - "rustls", + "rustls 0.23.43", "rustls-pki-types", "socks", "ureq-proto", "utf8-zero", - "webpki-roots", + "webpki-roots 1.0.9", ] [[package]] @@ -4139,7 +4482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" dependencies = [ "base64 0.23.1", - "http", + "http 1.5.0", "httparse", "log", ] @@ -4187,6 +4530,12 @@ dependencies = [ "wasm-bindgen", ] +[[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.5" @@ -4320,6 +4669,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "1.0.9" @@ -4572,13 +4927,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -4590,20 +4954,35 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -4615,18 +4994,36 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4639,24 +5036,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4672,6 +5093,16 @@ dependencies = [ "memchr", ] +[[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 = "winreg" version = "0.56.0" diff --git a/Cargo.toml b/Cargo.toml index faadecf..8a15381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ rhai = { version = "1.24.0", features = ["metadata", "sync"] } rustc-hash = "2.1.2" rustls = "0.23.43" rustls-pemfile = "2.2.0" +serde = "1.0.229" +serde_derive = "1.0.229" +serde_json = "1.0.151" +shodan-rust = "1.0.1" sysinfo = "0.39.6" textwrap = "0.16.2" tokio = { version = "1.52.3", features = ["full"] } diff --git a/src/funcs.rs b/src/funcs.rs index fc1a354..e0da816 100644 --- a/src/funcs.rs +++ b/src/funcs.rs @@ -88,6 +88,7 @@ pub fn run_tui( let (event_tx, event_rx) = channel::(); let input_tx = event_tx.clone(); let mut project_list_state = ListState::default(); + let mut host_list_state = ListState::default(); let mut system = System::new_all(); let system_timer = Duration::from_secs(1); let mut last_print = Instant::now(); @@ -97,6 +98,7 @@ pub fn run_tui( project_list_state.select(Some(0)); state.selected_project = 0; } + host_list_state.select(Some(0)); std::thread::spawn(move || { loop { if !state.server_broker_running && !state.app_state_running { @@ -143,245 +145,293 @@ pub fn run_tui( last_print = Instant::now(); } let mut text_area_width = 0; - terminal.draw(|f| { - let main_chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(0), Constraint::Max(3)]) - .split(f.area()); - let top_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage(20), - Constraint::Percentage(45), - Constraint::Percentage(35), - ]) - .split(main_chunks[0]); - text_area_width = top_chunks[1].width.saturating_sub(2); - let projects: Vec = state - .projects - .iter() - .map(|p| ListItem::new(format!(" {} | {}", p.org_name, p.name))) - .collect(); - let projects_list = List::new(projects) - .block( - Block::default() - .borders(Borders::ALL) - .title(" Projects (ctrl + arrow keys to select) "), - ) - .highlight_style( - Style::default() - .bg(Color::Blue) - .fg(Color::White) - .add_modifier(Modifier::BOLD), - ) - .highlight_symbol(">> "); - f.render_stateful_widget(projects_list, top_chunks[0], &mut project_list_state); - let mut info_lines: Vec = Vec::new(); - let project = state.projects[state.selected_project].clone(); - info_lines.push(Line::from(format!( - "|CPU: {}% | RAM: {}GB / {}GB |", - system.global_cpu_usage().floor(), - system.used_memory() / BYTES_PER_GB, - system.total_memory() / BYTES_PER_GB, - ))); - info_lines.push(Line::from(Span::styled( - "--- PROJECT INFORMATION ---", - Style::default().fg(Color::Green), - ))); - info_lines.push(Line::from(format!("ORG: {}", project.org_name))); - info_lines.push(Line::from(format!("NAME: {}", project.name))); - info_lines.push(Line::from(format!("NOTES: {}", project.notes.display()))); - info_lines.push(Line::from(format!("Files: {}", project.files.display()))); - if let Some(db) = project.db { - info_lines.push(Line::from(format!("DISTROBOX: {}", db.name))); - } - if project.current { - info_lines.push(Line::from("STATUS: CURRENT")); + if state.selecting_host { + if !state.projects[state.selected_project].hosts.is_empty() { + terminal.draw(|f| { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Max(3)]) + .split(f.area()); + let project_name = Paragraph::new(format!( + "{} {}", + state.projects[state.selected_project].org_name, + state.projects[state.selected_project].name + )); + let hosts: Vec = state.projects[state.selected_project] + .hosts + .iter() + .map(|h| ListItem::new(format!("{}: {}", h.ip, h.hostname))) + .collect(); + let hosts_list = List::new(hosts) + .block( + Block::default() + .borders(Borders::ALL) + .title("Select a host."), + ) + .highlight_style( + Style::default() + .bg(Color::Blue) + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ) + .highlight_symbol(">> "); + f.render_stateful_widget(hosts_list, chunks[0], &mut host_list_state); + f.render_widget(project_name, chunks[1]); + })?; } else { - info_lines.push(Line::from("STATUS: UPCOMING")); + let _ = state.main_tx.send(ToolMessage::Output(( + 0, + "No hosts in the current project to select!".to_string(), + ))); + state.selecting_host = false; } - if !project.hosts.is_empty() { + } else { + terminal.draw(|f| { + let main_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Max(3)]) + .split(f.area()); + let top_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage(20), + Constraint::Percentage(45), + Constraint::Percentage(35), + ]) + .split(main_chunks[0]); + text_area_width = top_chunks[1].width.saturating_sub(2); + let projects: Vec = state + .projects + .iter() + .map(|p| ListItem::new(format!(" {} | {}", p.org_name, p.name))) + .collect(); + let projects_list = List::new(projects) + .block( + Block::default() + .borders(Borders::ALL) + .title(" Projects (ctrl + arrow keys to select) "), + ) + .highlight_style( + Style::default() + .bg(Color::Blue) + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ) + .highlight_symbol(">> "); + f.render_stateful_widget(projects_list, top_chunks[0], &mut project_list_state); + let mut info_lines: Vec = Vec::new(); + let project = state.projects[state.selected_project].clone(); + info_lines.push(Line::from(format!( + "|CPU: {}% | RAM: {}GB / {}GB |", + system.global_cpu_usage().floor(), + system.used_memory() / BYTES_PER_GB, + system.total_memory() / BYTES_PER_GB, + ))); info_lines.push(Line::from(Span::styled( - "--- PROJECT HOST INFORMATION ---", + "--- PROJECT INFORMATION ---", Style::default().fg(Color::Green), ))); - for host in &project.hosts { - info_lines.push(Line::from(format!("- {}:{}", host.hostname, host.ip))); - info_lines.push(Line::from(format!(" - PWNED: {}", host.pwned))); - info_lines.push(Line::from(format!( - " - CONTROL PORT: {}", - host.control_port + info_lines.push(Line::from(format!("ORG: {}", project.org_name))); + info_lines.push(Line::from(format!("NAME: {}", project.name))); + info_lines.push(Line::from(format!("NOTES: {}", project.notes.display()))); + info_lines.push(Line::from(format!("Files: {}", project.files.display()))); + if let Some(db) = project.db { + info_lines.push(Line::from(format!("DISTROBOX: {}", db.name))); + } + if project.current { + info_lines.push(Line::from("STATUS: CURRENT")); + } else { + info_lines.push(Line::from("STATUS: UPCOMING")); + } + if !project.hosts.is_empty() { + info_lines.push(Line::from(Span::styled( + "--- PROJECT HOST INFORMATION ---", + Style::default().fg(Color::Green), ))); - if !host.open_ports.is_empty() { - info_lines.push(Line::from(format!(" - PORTS:"))); - for port in &host.open_ports { - info_lines.push(Line::from(format!(" - {}", port))); + for host in &project.hosts { + info_lines.push(Line::from(format!("- {}:{}", host.hostname, host.ip))); + info_lines.push(Line::from(format!(" - PWNED: {}", host.pwned))); + info_lines.push(Line::from(format!( + " - CONTROL PORT: {}", + host.control_port + ))); + if !host.open_ports.is_empty() { + info_lines.push(Line::from(format!(" - PORTS:"))); + for port in &host.open_ports { + info_lines.push(Line::from(format!(" - {}", port))); + } } - } - if !host.users.is_empty() { - info_lines.push(Line::from(format!(" - USERS:"))); - for user in &host.users { - info_lines.push(Line::from(format!( - " - {} - PWNED: {}", - user.name, user.compromised - ))); - } - } - } - } - let spacer = format!("{}", "+".repeat(top_chunks[2].width as usize - 2)); - info_lines.push(Line::from(Span::styled( - &spacer, - Style::default().fg(Color::Green), - ))); - info_lines.push(Line::from(Span::styled( - "--- Tool Information ---", - Style::default().fg(Color::Green), - ))); - info_lines.push(Line::from(format!( - "CONFIG FILE: {}", - state.config_file.display() - ))); - for setting in state.config.keys() { - info_lines.push(Line::from(format!( - "{}: {}", - setting.to_uppercase(), - state.config.get(setting).unwrap() - ))); - } - if state.progress_bars.is_empty() { - let info_area_height = top_chunks[2].height.saturating_sub(2) as usize; - if state.info_scroll as usize > info_lines.len().saturating_sub(info_area_height) { - state.info_scroll = info_lines.len().saturating_sub(info_area_height) as u16; - } - let info_paragraph = Paragraph::new(info_lines) - .block( - Block::default() - .borders(Borders::ALL) - .title("Selected Project Information"), - ) - .scroll((state.info_scroll, 0)) - .wrap(ratatui::widgets::Wrap { trim: false }); - f.render_widget(info_paragraph, top_chunks[2]); - } else { - let progress_section = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(60), Constraint::Percentage(40)]) - .split(top_chunks[2]); - let info_area_height = top_chunks[2].height.saturating_sub(2) as usize; - if state.info_scroll as usize > info_lines.len().saturating_sub(info_area_height) { - state.info_scroll = info_lines.len().saturating_sub(info_area_height) as u16; - } - let info_paragraph = Paragraph::new(info_lines) - .block( - Block::default() - .borders(Borders::ALL) - .title("Selected Project Information"), - ) - .scroll((state.info_scroll, 0)) - .wrap(ratatui::widgets::Wrap { trim: false }); - f.render_widget(info_paragraph, progress_section[0]); - let mut progress_lines = Vec::new(); - let mut color_differnt = false; - state.progress_bars.iter().for_each(|progress| { - if let Ok(status) = progress.read() { - let percentage = status.complete / status.total * 100; - if percentage == 100 { - progress_lines.push(Line::from(Span::styled( - format!("{}:)", status.name), - Style::default().fg(Color::Green), - ))); - progress_lines.push(Line::from(Span::styled( - format!( - " {}/{} ({}%)", - status.complete, status.total, percentage - ), - Style::default().fg(Color::Green), - ))); - } else { - match color_differnt { - true => { - progress_lines.push(Line::from(Span::styled( - format!("{}:)", status.name), - Style::default().fg(Color::Cyan), - ))); - progress_lines.push(Line::from(Span::styled( - format!( - " {}/{} ({}%)", - status.complete, status.total, percentage - ), - Style::default().fg(Color::Cyan), - ))); - color_differnt = false; - } - false => { - progress_lines.push(Line::from(Span::styled( - format!("{}:)", status.name), - Style::default().fg(Color::Magenta), - ))); - progress_lines.push(Line::from(Span::styled( - format!( - " {}/{} ({}%)", - status.complete, status.total, percentage - ), - Style::default().fg(Color::Magenta), - ))); - color_differnt = true; - } + if !host.users.is_empty() { + info_lines.push(Line::from(format!(" - USERS:"))); + for user in &host.users { + info_lines.push(Line::from(format!( + " - {} - PWNED: {}", + user.name, user.compromised + ))); } } } + } + let spacer = format!("{}", "+".repeat(top_chunks[2].width as usize - 2)); + info_lines.push(Line::from(Span::styled( + &spacer, + Style::default().fg(Color::Green), + ))); + info_lines.push(Line::from(Span::styled( + "--- Tool Information ---", + Style::default().fg(Color::Green), + ))); + info_lines.push(Line::from(format!( + "CONFIG FILE: {}", + state.config_file.display() + ))); + for setting in state.config.keys() { + info_lines.push(Line::from(format!( + "{}: {}", + setting.to_uppercase(), + state.config.get(setting).unwrap() + ))); + } + if state.progress_bars.is_empty() { + let info_area_height = top_chunks[2].height.saturating_sub(2) as usize; + if state.info_scroll as usize + > info_lines.len().saturating_sub(info_area_height) + { + state.info_scroll = + info_lines.len().saturating_sub(info_area_height) as u16; + } + let info_paragraph = Paragraph::new(info_lines) + .block( + Block::default() + .borders(Borders::ALL) + .title("Selected Project Information"), + ) + .scroll((state.info_scroll, 0)) + .wrap(ratatui::widgets::Wrap { trim: false }); + f.render_widget(info_paragraph, top_chunks[2]); + } else { + let progress_section = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(60), Constraint::Percentage(40)]) + .split(top_chunks[2]); + let info_area_height = top_chunks[2].height.saturating_sub(2) as usize; + if state.info_scroll as usize + > info_lines.len().saturating_sub(info_area_height) + { + state.info_scroll = + info_lines.len().saturating_sub(info_area_height) as u16; + } + let info_paragraph = Paragraph::new(info_lines) + .block( + Block::default() + .borders(Borders::ALL) + .title("Selected Project Information"), + ) + .scroll((state.info_scroll, 0)) + .wrap(ratatui::widgets::Wrap { trim: false }); + f.render_widget(info_paragraph, progress_section[0]); + let mut progress_lines = Vec::new(); + let mut color_differnt = false; + state.progress_bars.iter().for_each(|progress| { + if let Ok(status) = progress.read() { + let percentage = status.complete / status.total * 100; + if percentage == 100 { + progress_lines.push(Line::from(Span::styled( + format!("{}:)", status.name), + Style::default().fg(Color::Green), + ))); + progress_lines.push(Line::from(Span::styled( + format!( + " {}/{} ({}%)", + status.complete, status.total, percentage + ), + Style::default().fg(Color::Green), + ))); + } else { + match color_differnt { + true => { + progress_lines.push(Line::from(Span::styled( + format!("{}:)", status.name), + Style::default().fg(Color::Cyan), + ))); + progress_lines.push(Line::from(Span::styled( + format!( + " {}/{} ({}%)", + status.complete, status.total, percentage + ), + Style::default().fg(Color::Cyan), + ))); + color_differnt = false; + } + false => { + progress_lines.push(Line::from(Span::styled( + format!("{}:)", status.name), + Style::default().fg(Color::Magenta), + ))); + progress_lines.push(Line::from(Span::styled( + format!( + " {}/{} ({}%)", + status.complete, status.total, percentage + ), + Style::default().fg(Color::Magenta), + ))); + color_differnt = true; + } + } + } + } + }); + let progress_paragraph = Paragraph::new(progress_lines).block( + Block::default() + .borders(Borders::ALL) + .title("Operations in progress"), + ); + f.render_widget(progress_paragraph, progress_section[1]); + } + let mut output_lines = Vec::new(); + state.output.iter().for_each(|text| { + let line = Line::from(Span::raw(text)); + if text.to_lowercase().contains("error") { + let line = line.style(Style::default().fg(Color::Red)); + output_lines.push(line); + } else if text.to_lowercase().contains("success") { + let line = line.style(Style::default().fg(Color::Green)); + output_lines.push(line); + } else { + output_lines.push(line); + } }); - let progress_paragraph = Paragraph::new(progress_lines).block( + let text_area_height = top_chunks[1].height.saturating_sub(2) as usize; + let max_scroll = state.output.len().saturating_sub(text_area_height); + if state.output_follow { + state.output_scroll = max_scroll as u16; + } + state.output_scroll = state.output_scroll.min(max_scroll as u16); + if state.output_scroll == u16::MAX { + if state.output.len() > text_area_height { + state.output_scroll = (state.output.len() - text_area_height) as u16; + } else { + state.output_scroll = 0; + } + } + let output_paragraph = Paragraph::new(output_lines) + .block( + Block::default() + .borders(Borders::ALL) + .title(" Script Engine Output "), + ) + .scroll((state.output_scroll, 0)); + + f.render_widget(output_paragraph, top_chunks[1]); + let input_paragraph = Paragraph::new(state.curent_intput.as_str()).block( Block::default() .borders(Borders::ALL) - .title("Operations in progress"), + .title(" What is thy bidding, my master? "), ); - f.render_widget(progress_paragraph, progress_section[1]); - } - let mut output_lines = Vec::new(); - state.output.iter().for_each(|text| { - let line = Line::from(Span::raw(text)); - if text.to_lowercase().contains("error") { - let line = line.style(Style::default().fg(Color::Red)); - output_lines.push(line); - } else if text.to_lowercase().contains("success") { - let line = line.style(Style::default().fg(Color::Green)); - output_lines.push(line); - } else { - output_lines.push(line); - } - }); - let text_area_height = top_chunks[1].height.saturating_sub(2) as usize; - let max_scroll = state.output.len().saturating_sub(text_area_height); - if state.output_follow { - state.output_scroll = max_scroll as u16; - } - state.output_scroll = state.output_scroll.min(max_scroll as u16); - if state.output_scroll == u16::MAX { - if state.output.len() > text_area_height { - state.output_scroll = (state.output.len() - text_area_height) as u16; - } else { - state.output_scroll = 0; - } - } - let output_paragraph = Paragraph::new(output_lines) - .block( - Block::default() - .borders(Borders::ALL) - .title(" Script Engine Output "), - ) - .scroll((state.output_scroll, 0)); - f.render_widget(output_paragraph, top_chunks[1]); - let input_paragraph = Paragraph::new(state.curent_intput.as_str()).block( - Block::default() - .borders(Borders::ALL) - .title(" What is thy bidding, my master? "), - ); - - f.render_widget(input_paragraph, main_chunks[1]); - })?; + f.render_widget(input_paragraph, main_chunks[1]); + })?; + } if let Ok(event) = event_rx.recv_timeout(Duration::from_millis(100)) { match event { AppEvent::Worker(msg) => match msg { @@ -697,134 +747,152 @@ pub fn run_tui( break; } KeyCode::Enter => { - let trimmed = state.curent_intput.trim().to_string(); - if !trimmed.is_empty() { - state.history.push(trimmed.clone()); - let _ = state - .main_tx - .send(ToolMessage::Output((0, "\n".to_string()))); - let _ = state.main_tx.send(ToolMessage::Output(( - 0, - format!("[user input] > {}", trimmed), - ))); - let _ = state - .main_tx - .send(ToolMessage::Output((0, "\n".to_string()))); - let prompt = state.prompt.clone(); - if prompt.action.is_some() { - state.prompt.responses.push(PromptResponse { - query: prompt.last_prompted.clone().unwrap(), - response: trimmed.clone(), - }); - if state.prompt.responses.len() == state.prompt.num_responses { - state.execute_command( - prompt.execute_command.as_str(), - None, - 0, - )?; - let _ = state.main_tx.send(ToolMessage::Output(( - 0, - format!("{}", prompt.execute_command), - ))); - } else { - state.execute_command("prompt", None, 0)?; - } - } else { - let (command, args) = - trimmed.split_once(' ').unwrap_or((&trimmed, "")); - match command { - "exit" | "quit" => break, - "reload-modules" => { - state.initialize_modules(); + if state.selecting_host { + state.selecting_host = false; + } else { + let trimmed = state.curent_intput.trim().to_string(); + if !trimmed.is_empty() { + state.history.push(trimmed.clone()); + let _ = state + .main_tx + .send(ToolMessage::Output((0, "\n".to_string()))); + let _ = state.main_tx.send(ToolMessage::Output(( + 0, + format!("[user input] > {}", trimmed), + ))); + let _ = state + .main_tx + .send(ToolMessage::Output((0, "\n".to_string()))); + let prompt = state.prompt.clone(); + if prompt.action.is_some() { + state.prompt.responses.push(PromptResponse { + query: prompt.last_prompted.clone().unwrap(), + response: trimmed.clone(), + }); + if state.prompt.responses.len() + == state.prompt.num_responses + { + state.execute_command( + prompt.execute_command.as_str(), + None, + 0, + )?; let _ = state.main_tx.send(ToolMessage::Output(( 0, - "Reloading Module Paths...".to_string(), + format!("{}", prompt.execute_command), ))); + } else { + state.execute_command("prompt", None, 0)?; } - "help" => { - let help_text = state.help.clone().join("\n"); - for line in help_text.lines() { + } else { + let (command, args) = + trimmed.split_once(' ').unwrap_or((&trimmed, "")); + match command { + "exit" | "quit" => break, + "reload-modules" => { + state.initialize_modules(); let _ = state.main_tx.send(ToolMessage::Output(( 0, - line.to_string(), + "Reloading Module Paths...".to_string(), ))); } - } - "new_project" | "np" => { - if args.split_once(' ').is_some() { - let _ = state.execute_command( - command, - Some(args.to_string()), - 0, - ); - } else { - state.output.push( - "[error]: USAGE -> np ".into(), - ); + "help" => { + let help_text = state.help.clone().join("\n"); + for line in help_text.lines() { + let _ = state.main_tx.send( + ToolMessage::Output((0, line.to_string())), + ); + } } - } - "switch_config" => { - switch_config = true; - state.curent_intput.clear(); - break; - } - command_name => { - if state.module_loader.commands.contains_key(command) { - let _ = state.main_tx.send(ToolMessage::Output(( - 0, - format!( - "[Worker] Executing script {}...", - command_name - ), - ))); - if let Err(e) = state.execute_command( - command, - Some(args.to_string()), - 0, - ) { + "new_project" | "np" => { + if args.split_once(' ').is_some() { + let _ = state.execute_command( + command, + Some(args.to_string()), + 0, + ); + } else { + state.output.push( + "[error]: USAGE -> np ".into(), + ); + } + } + "switch_config" => { + switch_config = true; + state.curent_intput.clear(); + break; + } + "select_host" => { + state.selecting_host = true; + } + command_name => { + if state + .module_loader + .commands + .contains_key(command) + { let _ = state.main_tx.send(ToolMessage::Output(( 0, - format!("[error] Pipeline faile: {e}"), + format!( + "[Worker] Executing script {}...", + command_name + ), ))); - } - } else { - if args == "" { - match state.execute_command(command, None, 0) { - Ok(_) => {} - Err(e) => { - let _ = state.main_tx.send( - ToolMessage::Output(( - 0, - e.to_string(), - )), - ); - } - } - } else { - match state.execute_command( + if let Err(e) = state.execute_command( command, Some(args.to_string()), 0, ) { - Ok(_) => {} - Err(e) => { - let _ = state.main_tx.send( - ToolMessage::Output(( - 0, - e.to_string(), - )), - ); + let _ = state.main_tx.send( + ToolMessage::Output(( + 0, + format!( + "[error] Pipeline faile: {e}" + ), + )), + ); + } + } else { + if args == "" { + match state + .execute_command(command, None, 0) + { + Ok(_) => {} + Err(e) => { + let _ = state.main_tx.send( + ToolMessage::Output(( + 0, + e.to_string(), + )), + ); + } + } + } else { + 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(); } - state.curent_intput.clear(); + history_index = state.history.len(); } - history_index = state.history.len(); } KeyCode::Char(c) => { state.curent_intput.push(c); @@ -833,18 +901,40 @@ pub fn run_tui( state.curent_intput.pop(); } KeyCode::Up if key.modifiers.is_empty() => { - if !state.history.is_empty() && history_index > 0 { - history_index -= 1; - state.curent_intput = state.history[history_index].clone(); + if state.selecting_host { + if let Some(selected) = host_list_state.selected() { + if selected > 0 { + let new_index = selected - 1; + host_list_state.select(Some(new_index)); + state.selected_host = new_index; + } + } + } else { + if !state.history.is_empty() && history_index > 0 { + history_index -= 1; + state.curent_intput = state.history[history_index].clone(); + } } } KeyCode::Down if key.modifiers.is_empty() => { - if history_index < state.history.len() { - history_index += 1; - if history_index == state.history.len() { - state.curent_intput.clear(); // Clear back to a fresh prompt - } else { - state.curent_intput = state.history[history_index].clone(); + if state.selecting_host { + if let Some(selected) = host_list_state.selected() { + if selected + 1 + < state.projects[state.selected_project].hosts.len() + { + let new_index = selected + 1; + host_list_state.select(Some(new_index)); + state.selected_host = new_index; + } + } + } else { + if history_index < state.history.len() { + history_index += 1; + if history_index == state.history.len() { + state.curent_intput.clear(); // Clear back to a fresh prompt + } else { + state.curent_intput = state.history[history_index].clone(); + } } } } diff --git a/src/lib.rs b/src/lib.rs index 486449f..0be4d2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,9 @@ use rayon::prelude::*; use rhai::{AST, Dynamic, Engine, Scope}; use rust_tools::{BusterTarget, rustbuster}; use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName}; +use serde_derive::Deserialize; +use serde_json; +use shodan_rust::ShodanClient; use std::collections::HashMap; use std::env; use std::error::Error; @@ -23,6 +26,7 @@ use std::process::{Command, Stdio}; use std::sync::RwLock; use std::sync::mpsc::Receiver; use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel}; +use std::thread; use std::time::Duration; use std::time::{self, Instant}; use walkdir::WalkDir; @@ -91,6 +95,7 @@ pub struct AppState { pub output: Vec, pub selected_server: usize, pub selected_project: usize, + pub selected_host: usize, pub curent_intput: String, pub module_loader: ModuleLoader, pub output_scroll: u16, @@ -103,6 +108,7 @@ pub struct AppState { pub help: Vec, pub name: String, pub progress_bars: Vec>>, + pub selecting_host: bool, } impl AppState { @@ -124,6 +130,7 @@ impl AppState { output: Vec::new(), selected_server: 0, selected_project: 0, + selected_host: 0, curent_intput: String::new(), module_loader: ModuleLoader::new(), output_scroll: 0, @@ -143,6 +150,7 @@ impl AppState { help: Vec::new(), name: String::new(), progress_bars: Vec::new(), + selecting_host: false, } } @@ -223,6 +231,47 @@ impl AppState { } } } + let mut server_folder_path = self.config_file.clone(); + server_folder_path.pop(); + server_folder_path.push("client_servers"); + if server_folder_path.exists() { + let server_confs = read_dir(server_folder_path)?; + server_confs.into_iter().for_each(|entry| { + if let Ok(entry) = entry { + if let Ok(text) = read_to_string(entry.path()) { + let mut name = String::new(); + let mut address = String::new(); + let mut cert = String::new(); + text.lines().into_iter().for_each(|line| { + if let Some((setting, data)) = line.split_once(": ") { + match setting.trim() { + "name" => name = data.trim().to_string(), + "address" => address = data.trim().to_string(), + "cert" => cert = data.trim().to_string(), + _ => {} + } + } + }); + let new_server = Server { + address, + connected: false, + timer: Duration::from_secs(90), + config: None, + last_check: Instant::now(), + message_que: Vec::new(), + action_que: Vec::new(), + client_id: 0, + selected_client: 0, + logged_in: false, + password: String::new(), + name, + cert_text: cert, + }; + self.servers.push(Arc::new(Mutex::new(new_server))); + } + } + }); + } self.help.push("help\nThat's MEEEEEEE\n".to_string()); self.help.push("new_project\nadd a project to the upcoming project pool and create default notes based on templates\nusage:\nnew_project organization_name, project_name\n".to_string()); self.help.push( @@ -260,6 +309,20 @@ impl AppState { self.help.push("rustbuster\nRun the Subdomain/Subdirectory bruteforcetool RustBuster (included in tetanus now!) against a given domain name or url.\nrustbuster target,target,target subs=/path/to/subwordlist dirs=/path/to/dirwordlist\n".to_string()); self.help .push("clear_operations\nClear the completed operation statuses.\n".to_string()); + self.help + .push("shodan\nGet the shodan info for the currently selected host\n".to_string()); + self.help + .push("select_host\nSelect a host in the current project.\n".to_string()); + self.help.push( + "shodan_all\nGet the shodan info for all hosts in the current project.\n".to_string(), + ); + self.help.push( + "set_shodan_key\nSet your shodan api key, and save it to your system's keyring.\n" + .to_string(), + ); + self.help.push( + "save_server\nsave the server and password to your system's keyring.\n".to_string(), + ); self.help.push("exit\nquit the tool\n".to_string()); self.initialize_modules(); return Ok(()); @@ -1362,6 +1425,107 @@ impl AppState { !lock.finished }); } + "shodan" => { + if let Some(project) = self.projects.get(self.selected_project) { + if let Some(host) = project.hosts.get(self.selected_host) { + if let Ok(entry) = Entry::new("tetanus", "shodan_key") { + if let Ok(key) = entry.get_password() { + let mut host_clone = host.clone(); + let tx_clone = self.main_tx.clone(); + thread::spawn(move || host_clone.get_shodan(key, tx_clone)); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't get shodan key from keyring!"), + ))); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't find shodan key in keyring!"), + ))); + } + } + } + } + "shodan_all" => { + if let Some(project) = self.projects.get(self.selected_project) { + if !project.hosts.is_empty() { + if let Ok(entry) = Entry::new("tetanus", "shodan_key") { + if let Ok(key) = entry.get_password() { + project.hosts.iter().for_each(|h| { + let mut host_clone = h.clone(); + let tx_clone = self.main_tx.clone(); + let key_clone = key.clone(); + thread::spawn(move || { + host_clone.get_shodan(key_clone, tx_clone) + }); + }); + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't get shodan key from keyring!"), + ))); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't find shodan key in keyring!"), + ))); + } + } + } + } + "set_shodan_key" => { + if let Some(args) = command_args { + if let Ok(_) = Entry::store_status() { + if let Ok(entry) = Entry::new("tetanus", "shodan_key") { + if let Err(e) = entry.set_password(args.trim()) { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't set shodan apikey!: {e}"), + ))); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] couldn't create new entry!"), + ))); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] store status bad!"), + ))); + } + } + } + "save_server" => { + if let Some(server) = self.servers.get(self.selected_server) { + let mut path = self.config_file.clone(); + path.pop(); + path.push("client_servers"); + if !path.exists() { + create_dir_all(&path).unwrap(); + } + if let Ok(mut lock) = server.lock() { + match lock.save(path) { + Ok(_) => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[success] {} saved!", lock.address), + ))); + } + Err(e) => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("[error] saving server: {e}"), + ))); + } + } + } + } + } "exit" => { self.save_all()?; let _ = self.main_tx.send(ToolMessage::AppStateExit); @@ -2352,6 +2516,10 @@ impl Server { } self.connected = true; self.last_check = Instant::now(); + let entry = Entry::new("tetanus", &self.address)?; + if let Ok(password) = entry.get_password() { + self.login(password)?; + } return Ok(self.client_id); } @@ -2433,6 +2601,27 @@ impl Server { self.connected = false; Ok(()) } + + fn save(&mut self, mut path: PathBuf) -> Result<(), Box> { + path.push(format!("{}.conf", self.address)); + let mut conf_file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path)?; + let outdata = format!( + " +name: {} +address: {} +cert: {} +", + self.name, self.address, self.cert_text + ); + write!(conf_file, "{}", outdata)?; + let entry = Entry::new("tetanus", &self.address)?; + entry.set_password(&self.password)?; + Ok(()) + } } #[derive(Clone, Debug)] @@ -2698,6 +2887,7 @@ pub struct Host { pub pwned: bool, pub id: usize, pub findings: Vec, + pub shodan_data: Option, } impl Host { @@ -2711,6 +2901,7 @@ impl Host { pwned: false, id: 0, findings: Vec::new(), + shodan_data: None, } } @@ -2812,6 +3003,41 @@ id: {}", } return Ok(()); } + + pub fn get_shodan(&mut self, key: String, tx: Sender) { + let client = ShodanClient::new(key); + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async { + if let Ok(data) = client.host_info(&self.ip).await { + if let Ok(new_data) = serde_json::from_str::(&data.to_string()) { + if let Some(ports) = new_data.ports { + ports.iter().for_each(|p| { + self.add_port(&p.to_string()); + }); + } + if let Some(vulns) = new_data.vulns { + vulns.iter().for_each(|v| { + self.findings.push(format!("shodan: {}", v)); + }); + } + } else { + let _ = tx.send(ToolMessage::Output(( + 0, + "[error] parsing shodand data!".to_string(), + ))); + } + } else { + let _ = tx.send(ToolMessage::Output(( + 0, + "[error] getting shodand data!".to_string(), + ))); + } + }); + let _ = tx.send(ToolMessage::Output(( + 0, + format!("shodan_function finished for {}", self.ip), + ))); + } } #[derive(Clone, Debug)] @@ -3321,9 +3547,16 @@ struct PromptQuery { query: String, } -struct ToolProgress { +pub struct ToolProgress { name: String, total: usize, complete: usize, finished: bool, } + +#[derive(Debug, Deserialize, Default, Clone)] +pub struct ShodanData { + pub ip_str: Option, + pub vulns: Option>, + pub ports: Option>, +} diff --git a/src/main.rs b/src/main.rs index 3cb2060..6ef0338 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use keyring::Entry; use rand::RngExt; use rand::distr::Alphanumeric; use std::sync::{Arc, Mutex}; @@ -190,6 +191,15 @@ async fn main() { .collect(); if let Some(pass) = args.password { server_pass = pass; + } else if let Ok(entry) = Entry::new("tetanus-server", &address) { + if let Ok(password) = entry.get_password() { + server_pass = password; + println!("loaded passwrod from keyring."); + } else { + if let Err(e) = entry.set_password(&server_pass) { + println!("error saving password to keyring! {e}"); + } + } } let new_server = server::Server { address, diff --git a/src/rust_tools.rs b/src/rust_tools.rs index e4ead79..9fad85e 100644 --- a/src/rust_tools.rs +++ b/src/rust_tools.rs @@ -153,3 +153,7 @@ pub fn rustbuster(target: BusterTarget) -> Option { } } } + +pub fn shodan(ip: String) -> String { + return "todo".to_string(); +} diff --git a/src/server.rs b/src/server.rs index d130b72..3c552b3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use keyring::Entry; use rcgen::generate_simple_self_signed; use std::error::Error; use std::fs; @@ -53,7 +54,7 @@ pub struct Server { } pub async fn start_server(server: Arc>) -> Result<(), Box> { - let lock = server.lock().unwrap(); + let mut lock = server.lock().unwrap(); let mut name_path = lock.certificate_path.clone(); name_path.pop(); name_path.push("names.txt"); @@ -90,6 +91,34 @@ pub async fn start_server(server: Arc>) -> Result<(), Box