Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 70 additions & 14 deletions src/agentsight/src/bin/cli/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ pub struct AuditCommand {
/// Show summary statistics
#[structopt(long)]
pub summary: bool,

/// Hide process_action events whose command/args contain any of these
/// substrings. Repeatable. Useful for filtering shell-startup noise, e.g.
/// `--exclude "command -v" --exclude grepconf`. The hidden count is reported.
#[structopt(long)]
pub exclude: Vec<String>,
}

impl AuditCommand {
Expand All @@ -44,9 +50,17 @@ impl AuditCommand {
}
};

let event_type = self.event_type.as_ref().and_then(|t| t.parse::<AuditEventType>().ok());
let event_type = self
.event_type
.as_ref()
.and_then(|t| t.parse::<AuditEventType>().ok());

if self.summary {
if !self.exclude.is_empty() {
eprintln!(
"Note: --exclude is not applied to --summary (summary always reflects the full dataset)."
);
}
self.print_summary(&store);
return;
}
Expand Down Expand Up @@ -75,25 +89,67 @@ impl AuditCommand {
}
}

fn is_excluded(&self, record: &agentsight::AuditRecord) -> bool {
use agentsight::AuditExtra;
if self.exclude.is_empty() {
return false;
}
if let AuditExtra::ProcessAction { filename, args, .. } = &record.extra {
let fname = filename.as_deref().unwrap_or("");
let a = args.as_deref().unwrap_or("");
return self
.exclude
.iter()
.filter(|p| !p.trim().is_empty())
.any(|p| fname.contains(p.as_str()) || a.contains(p.as_str()));
}
false
}

fn output_records(&self, records: &[agentsight::AuditRecord], scope: &str) {
let total = records.len();
let filtered: Vec<&agentsight::AuditRecord> =
records.iter().filter(|r| !self.is_excluded(r)).collect();
let hidden = total - filtered.len();

if self.json {
let json_records: Vec<serde_json::Value> = records.iter().map(|r| {
serde_json::json!({
"id": r.id,
"event_type": r.event_type.to_string(),
"timestamp_ns": r.timestamp_ns,
"pid": r.pid,
"ppid": r.ppid,
"comm": r.comm,
"duration_ns": r.duration_ns,
"extra": r.extra,
let json_records: Vec<serde_json::Value> = filtered
.iter()
.map(|r| {
serde_json::json!({
"id": r.id,
"event_type": r.event_type.to_string(),
"timestamp_ns": r.timestamp_ns,
"pid": r.pid,
"ppid": r.ppid,
"comm": r.comm,
"duration_ns": r.duration_ns,
"extra": r.extra,
})
})
}).collect();
.collect();
println!("{}", serde_json::to_string_pretty(&json_records).unwrap());
if hidden > 0 {
eprintln!(
"{} events hidden by --exclude ({} shown, {} total)",
hidden,
filtered.len(),
total
);
}
} else {
println!("{}: {} audit events", scope, records.len());
if hidden > 0 {
println!(
"{}: {} audit events ({} hidden by --exclude)",
scope,
filtered.len(),
hidden
);
} else {
println!("{}: {} audit events", scope, filtered.len());
}
println!();
for record in records {
for record in &filtered {
let json_record = serde_json::json!({
"id": record.id,
"event_type": record.event_type.to_string(),
Expand Down
Loading