Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/1067 word wrap in cell when event title is long #1094

Merged
1 change: 1 addition & 0 deletions CHANGELOG-Japanese.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- `csv-timeline`、`json-timeline`、`metrics`、`logon-summary`、`search`コマンドに対して、出力ファイルを上書きするための`-C, --clobber`オプションを追加した。 (#1063) (@YamatoSecurity, @hitenkoku)
- HTML内にCSSと画像を組み込んだ。 (#1078) (@hitenkoku, 提案者: @joswr1ght)
- 出力時の速度向上。 (#1088) (@hitenkoku, @fukusuket)
- `metrics`コマンドは、テーブルが正しくレンダリングされるように、ワードラップを行うようになった。 (#1067) (@garigariganzy)
- `search`コマンドでJSON/JSONLの出力できるようにした。 (#1041) (@hitenkoku)

**バグ修正:**
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- Added the `-C, --clobber` option to overwrite existing output files in `csv-timeline`, `json-timeline`, `metrics`, `logon-summary`, and `search` commands. (#1063) (@YamatoSecurity, @hitenkoku)
- Made the HTML report portable by embedding the images and inlining CSS. (#1078) (@hitenkoku, thanks for the suggestion from @joswr1ght)
- Speed improvements in the output. (#1088) (@hitenkoku, @fukusuket)
- The `metrics` command now performs word wrapping to make sure the table gets rendered correctly. (#1067) (@garigariganzy)
- `search` command results can now be outputted to JSON/JSONL. (#1041) (@hitenkoku)

**Bug Fixes:**
Expand Down
33 changes: 30 additions & 3 deletions src/timeline/timelines.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cmp;
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
Expand All @@ -9,12 +10,17 @@ use crate::detections::utils::{self, make_ascii_titlecase, write_color_buffer};
use crate::timeline::search::search_result_dsp_msg;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use comfy_table::ColumnConstraint::LowerBoundary;
use comfy_table::ColumnConstraint::UpperBoundary;
use comfy_table::Width::Fixed;
use comfy_table::*;
use compact_str::CompactString;
use csv::WriterBuilder;
use downcast_rs::__std::process;
use nested::Nested;
use termcolor::{BufferWriter, Color, ColorChoice};
use terminal_size::terminal_size;
use terminal_size::Width;

use super::metrics::EventMetrics;
use super::search::EventSearch;
Expand Down Expand Up @@ -140,7 +146,11 @@ impl Timeline {
}
}

let header = vec!["Count", "Percent", "Channel", "ID", "Event"];
let header = vec!["Total", "%", "Channel", "ID", "Event"];
let mut header_cells = vec![];
for header_str in &header {
header_cells.push(Cell::new(header_str).set_alignment(CellAlignment::Center));
}
if let Some(ref mut w) = wtr {
w.write_record(&header).ok();
}
Expand All @@ -149,7 +159,8 @@ impl Timeline {
stats_tb
.load_preset(UTF8_FULL)
.apply_modifier(UTF8_ROUND_CORNERS);
stats_tb.set_header(header);

stats_tb.set_header(header_cells);

// 集計件数でソート
let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect();
Expand All @@ -170,6 +181,22 @@ impl Timeline {
}
}
stats_tb.add_rows(stats_msges.iter());
let terminal_width = match terminal_size() {
Some((Width(w), _)) => w,
None => 100,
};

let constraints = vec![
LowerBoundary(Fixed(7)), // Minimum number of characters for "Total"
UpperBoundary(Fixed(9)), // Maximum number of characters for "percent"
UpperBoundary(Fixed(20)), // Maximum number of characters for "Channel"
UpperBoundary(Fixed(12)), // Maximum number of characters for "ID"
UpperBoundary(Fixed(cmp::max(terminal_width - 55, 45))), // Maximum number of characters for "Event"
];
for (column_index, column) in stats_tb.column_iter_mut().enumerate() {
let constraint = constraints.get(column_index).unwrap();
column.set_constraint(*constraint);
}
println!("{stats_tb}");
}

Expand Down Expand Up @@ -682,7 +709,7 @@ mod tests {
);
// Event column is defined in rules/config/channel_eid_info.txt
let expect_records = vec![vec!["1", "100.0%", "Sec", "4625", "Logon failure"]];
let expect = "Count,Percent,Channel,ID,Event\n".to_owned()
let expect = "Total,%,Channel,ID,Event\n".to_owned()
+ &expect_records.join(&"\n").join(",").replace(",\n,", "\n")
+ "\n";
match read_to_string("./test_tm_stats.csv") {
Expand Down