diff --git a/Cargo.lock b/Cargo.lock
index bd8e7f5b3..c7b230785 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -261,13 +261,12 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.1.3"
+version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950"
+checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
dependencies = [
"jobserver",
"libc",
- "once_cell",
]
[[package]]
@@ -345,6 +344,15 @@ dependencies = [
"strsim 0.11.1",
]
+[[package]]
+name = "clap_complete"
+version = "4.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae"
+dependencies = [
+ "clap 4.5.9",
+]
+
[[package]]
name = "clap_derive"
version = "4.5.8"
@@ -832,6 +840,7 @@ dependencies = [
"chrono",
"cidr-utils",
"clap 4.5.9",
+ "clap_complete",
"comfy-table",
"compact_str",
"console",
@@ -1472,9 +1481,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "portable-atomic"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
[[package]]
name = "ppv-lite86"
@@ -1572,9 +1581,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags 2.6.0",
]
@@ -1963,18 +1972,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]]
name = "thiserror"
-version = "1.0.62"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.62"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
@@ -1998,9 +2007,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.38.0"
+version = "1.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
+checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
dependencies = [
"backtrace",
"bytes",
diff --git a/Cargo.toml b/Cargo.toml
index 5fef1b804..acef2f51c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ bytesize = "1.*"
chrono = "0.4.*"
cidr-utils = "0.6.*"
clap = { version = "4.*", features = ["derive", "cargo", "color"]}
+clap_complete = "*"
comfy-table = "7.*"
compact_str = "0.7.*"
console = "0.15.*"
diff --git a/rules b/rules
index 0a43d7115..718ca07c0 160000
--- a/rules
+++ b/rules
@@ -1 +1 @@
-Subproject commit 0a43d711543ed209af9c901ad7e6baa26373d222
+Subproject commit 718ca07c0352b5d7075c112098f171dabfc33abb
diff --git a/src/detections/configs.rs b/src/detections/configs.rs
index cdd9e5229..4a5df2dff 100644
--- a/src/detections/configs.rs
+++ b/src/detections/configs.rs
@@ -7,7 +7,9 @@ use crate::options::pivot::PIVOT_KEYWORD;
use crate::options::profile::{load_profile, Profile};
use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind};
use chrono::{DateTime, Days, Duration, Local, Months, Utc};
-use clap::{ArgAction, ArgGroup, Args, ColorChoice, Command, CommandFactory, Parser, Subcommand};
+use clap::{
+ ArgAction, ArgGroup, Args, ColorChoice, Command, CommandFactory, Parser, Subcommand, ValueHint,
+};
use compact_str::CompactString;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
@@ -111,6 +113,7 @@ impl StoredStatic {
_ => false,
};
let common_options = match &input_config.as_ref().unwrap().action {
+ Some(Action::AutoComplete(opt)) => opt.common_options,
Some(Action::CsvTimeline(opt)) => opt.output_options.common_options,
Some(Action::JsonTimeline(opt)) => opt.output_options.common_options,
Some(Action::LevelTuning(opt)) => opt.common_options,
@@ -296,6 +299,7 @@ impl StoredStatic {
Some(Action::LogonSummary(opt)) => opt.output.as_ref(),
Some(Action::Search(opt)) => opt.output.as_ref(),
Some(Action::ComputerMetrics(opt)) => opt.output.as_ref(),
+ Some(Action::AutoComplete(opt)) => opt.output.as_ref(),
_ => None,
};
let general_ch_abbr = create_output_filter_config(
@@ -884,6 +888,10 @@ pub enum Action {
/// List the output profiles
ListProfiles(CommonOptions),
+ #[clap(display_order = 383)]
+ /// Make the auto complete
+ AutoComplete(AutoCompleteOptions),
+
#[clap(
author = "Yamato Security (https://github.com/Yamato-Security/hayabusa - @SecurityYamato)",
help_template = "\nHayabusa v2.17.0 - Dev Build\n{author-with-newline}\n{usage-heading}\n hayabusa.exe computer-metrics [OPTIONS]\n\n{all-args}",
@@ -911,6 +919,7 @@ impl Action {
Action::ListProfiles(_) => 9,
Action::Search(_) => 10,
Action::ComputerMetrics(_) => 11,
+ Action::AutoComplete(_) => 12,
}
} else {
100
@@ -931,6 +940,7 @@ impl Action {
Action::ListProfiles(_) => "list-profiles",
Action::Search(_) => "search",
Action::ComputerMetrics(_) => "computer-metrics",
+ Action::AutoComplete(_) => "auto-complete",
}
} else {
""
@@ -938,6 +948,16 @@ impl Action {
}
}
+#[derive(Args, Clone, Debug)]
+pub struct AutoCompleteOptions {
+ #[clap(flatten)]
+ pub common_options: CommonOptions,
+
+ /// Save the auto complete in shell format (ex: auto-complete.sh)
+ #[arg(help_heading = Some("Output"), short = 'o', long, value_name = "FILE", display_order = 410)]
+ pub output: Option,
+}
+
#[derive(Args, Clone, Debug)]
pub struct DetectCommonOption {
/// Scan JSON formatted logs instead of .evtx (.json or .jsonl)
@@ -945,7 +965,7 @@ pub struct DetectCommonOption {
pub json_input: bool,
/// Specify additional evtx file extensions (ex: evtx_data)
- #[arg(help_heading = Some("General Options"), long = "target-file-ext", value_name = "FILE-EXT...", use_value_delimiter = true, value_delimiter = ',', display_order = 460)]
+ #[arg(help_heading = Some("General Options"), long = "target-file-ext", value_name = "FILE-EXT...", use_value_delimiter = true, value_delimiter = ',', display_order = 460, value_hint(ValueHint::FilePath))]
pub evtx_file_ext: Option>,
/// Number of threads (default: optimal number for performance)
@@ -970,8 +990,8 @@ pub struct DetectCommonOption {
default_value = "./rules/config",
hide_default_value = true,
value_name = "DIR",
- display_order = 442
- )]
+ display_order = 442,
+ value_hint(ValueHint::FilePath))]
pub config: PathBuf,
/// Output verbose information
diff --git a/src/main.rs b/src/main.rs
index 1bed3e2d4..d39c0463a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -57,6 +57,7 @@ use hayabusa::detections::utils::{
check_setting_path, get_writable_color, output_and_data_stack_for_html, output_profile_name,
};
use hayabusa::filter::create_channel_filter;
+use hayabusa::options::auto_complete::auto_complete;
use hayabusa::options::htmlreport::{self, HTML_REPORTER};
use hayabusa::options::pivot::create_output;
use hayabusa::options::pivot::PIVOT_KEYWORD;
@@ -154,6 +155,13 @@ impl App {
println!();
return;
}
+
+ //ロゴと時間が表示さないように実行したい
+ if let Action::AutoComplete(_) = &stored_static.config.action.as_ref().unwrap() {
+ auto_complete(app, stored_static.output_path.as_ref());
+ return;
+ }
+
if !stored_static.common_options.quiet {
self.output_logo(stored_static);
write_color_buffer(&BufferWriter::stdout(ColorChoice::Always), None, "", true).ok();
@@ -352,6 +360,11 @@ impl App {
self.print_contributors();
return;
}
+
+ Action::AutoComplete(_) => {
+ panic!("This should not be called here.");
+ }
+
Action::LogonSummary(_) => {
let mut target_output_path = Nested::::new();
if let Some(path) = &stored_static.output_path {
diff --git a/src/options/auto_complete.rs b/src/options/auto_complete.rs
new file mode 100644
index 000000000..25423ceb7
--- /dev/null
+++ b/src/options/auto_complete.rs
@@ -0,0 +1,34 @@
+use std::{env, path::PathBuf};
+
+use clap_complete::{generate_to, Generator, Shell};
+use dialoguer::Select;
+
+pub fn auto_complete(app: &mut clap::Command, output_path: Option<&PathBuf>) {
+ let shell = select_shell();
+ print_completer(shell, app, output_path);
+}
+
+fn select_shell() -> Shell {
+ let items: Vec = vec![Shell::Bash, Shell::Elvish, Shell::Fish, Shell::PowerShell];
+
+ let selection = Select::new()
+ .with_prompt("Which shell are you using?")
+ .items(&items)
+ .interact()
+ .unwrap();
+
+ items[selection]
+}
+fn print_completer(
+ generator: G,
+ app: &mut clap::Command,
+ output_path: Option<&PathBuf>,
+) {
+ let mut name = "auto-complete".to_string();
+ if output_path.is_some() {
+ name = output_path.unwrap().to_str().unwrap().to_string();
+ }
+ let out_dir: PathBuf = env::current_dir().expect("can't get current directory");
+
+ let _ = generate_to(generator, app, name, out_dir);
+}
diff --git a/src/options/mod.rs b/src/options/mod.rs
index acf43098b..3ad324935 100644
--- a/src/options/mod.rs
+++ b/src/options/mod.rs
@@ -1,3 +1,4 @@
+pub mod auto_complete;
pub mod geoip_search;
pub mod htmlreport;
pub mod level_tuning;