From d89444a26802a4f7b995fdfd8f7cffaaa6f9146a Mon Sep 17 00:00:00 2001 From: Adir Shitrit Date: Sat, 8 Nov 2025 12:40:55 +0200 Subject: [PATCH] Add JSON output format support --- Cargo.toml | 2 + ghost-cli/Cargo.toml | 2 + ghost-cli/src/main.rs | 69 ++++++++++++++++++++++------------ ghost-core/src/detection.rs | 5 ++- ghost-core/src/evasion.rs | 7 ++-- ghost-core/src/process.rs | 3 +- ghost-core/src/threat_intel.rs | 2 +- 7 files changed, 59 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a99e9e..66a9aba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ anyhow = "1.0" thiserror = "1.0" log = "0.4" env_logger = "0.11" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/ghost-cli/Cargo.toml b/ghost-cli/Cargo.toml index f77fd44..6348c88 100644 --- a/ghost-cli/Cargo.toml +++ b/ghost-cli/Cargo.toml @@ -10,3 +10,5 @@ ghost-core = { path = "../ghost-core" } anyhow.workspace = true env_logger.workspace = true log.workspace = true +serde.workspace = true +serde_json.workspace = true diff --git a/ghost-cli/src/main.rs b/ghost-cli/src/main.rs index abaaa69..0bdeb61 100644 --- a/ghost-cli/src/main.rs +++ b/ghost-cli/src/main.rs @@ -2,6 +2,7 @@ use anyhow::Result; use clap::{Arg, Command}; use ghost_core::{memory, process, thread, DetectionEngine, ThreatLevel}; use log::{debug, error, info, warn}; +use serde_json; use std::time::Instant; fn main() -> Result<()> { @@ -190,32 +191,52 @@ fn main() -> Result<()> { info!("Scan completed: {} processes scanned, {} suspicious processes found", scanned_count, detections.len()); // Handle output - let output_content = if detections.is_empty() { - "No suspicious activity detected.".to_string() - } else { - let mut content = format!("Found {} suspicious processes:\n\n", detections.len()); - - for detection in detections { - let level_str = match detection.threat_level { - ThreatLevel::Suspicious => "SUSPICIOUS", - ThreatLevel::Malicious => "MALICIOUS", - _ => "CLEAN", - }; - - content.push_str(&format!( - "[{}] {} (PID: {}) - Confidence: {:.1}%\n", - level_str, - detection.process.name, - detection.process.pid, - detection.confidence * 100.0 - )); - - for indicator in &detection.indicators { - content.push_str(&format!(" - {}\n", indicator)); + let output_content = match format.as_str() { + "json" => { + if detections.is_empty() { + serde_json::json!({ + "status": "clean", + "message": "No suspicious activity detected", + "detections": [] + }).to_string() + } else { + serde_json::json!({ + "status": "suspicious", + "message": format!("Found {} suspicious processes", detections.len()), + "detections": &detections + }).to_string() + } + } + _ => { + // Default table format + if detections.is_empty() { + "No suspicious activity detected.".to_string() + } else { + let mut content = format!("Found {} suspicious processes:\n\n", detections.len()); + + for detection in &detections { + let level_str = match detection.threat_level { + ThreatLevel::Suspicious => "SUSPICIOUS", + ThreatLevel::Malicious => "MALICIOUS", + _ => "CLEAN", + }; + + content.push_str(&format!( + "[{}] {} (PID: {}) - Confidence: {:.1}%\n", + level_str, + detection.process.name, + detection.process.pid, + detection.confidence * 100.0 + )); + + for indicator in &detection.indicators { + content.push_str(&format!(" - {}\n", indicator)); + } + content.push('\n'); + } + content } - content.push('\n'); } - content }; if let Some(output_path) = output_file { diff --git a/ghost-core/src/detection.rs b/ghost-core/src/detection.rs index 224bc22..fb80552 100644 --- a/ghost-core/src/detection.rs +++ b/ghost-core/src/detection.rs @@ -5,16 +5,17 @@ use crate::{ }; #[cfg(target_os = "linux")] use crate::EbpfDetector; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum ThreatLevel { Clean, Suspicious, Malicious, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct DetectionResult { pub process: ProcessInfo, pub threat_level: ThreatLevel, diff --git a/ghost-core/src/evasion.rs b/ghost-core/src/evasion.rs index c096fd1..cd568eb 100644 --- a/ghost-core/src/evasion.rs +++ b/ghost-core/src/evasion.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::time::{SystemTime, Duration}; +use serde::{Deserialize, Serialize}; use crate::{ProcessInfo, MemoryRegion, ThreadInfo, MemoryProtection}; /// Advanced Evasion Detection Module @@ -11,7 +12,7 @@ pub struct EvasionDetector { obfuscation_detector: ObfuscationDetector, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct EvasionResult { pub evasion_techniques: Vec, pub confidence: f32, @@ -19,7 +20,7 @@ pub struct EvasionResult { pub anti_analysis_indicators: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct EvasionTechnique { pub technique_name: String, pub mitre_id: String, @@ -29,7 +30,7 @@ pub struct EvasionTechnique { pub severity: EvasionSeverity, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum EvasionSeverity { Low, // Basic evasion attempts Medium, // Moderate sophistication diff --git a/ghost-core/src/process.rs b/ghost-core/src/process.rs index 0ee8bb4..74525e1 100644 --- a/ghost-core/src/process.rs +++ b/ghost-core/src/process.rs @@ -1,6 +1,7 @@ +use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProcessInfo { pub pid: u32, pub ppid: u32, diff --git a/ghost-core/src/threat_intel.rs b/ghost-core/src/threat_intel.rs index 21cddc7..307e079 100644 --- a/ghost-core/src/threat_intel.rs +++ b/ghost-core/src/threat_intel.rs @@ -38,7 +38,7 @@ pub enum IocType { Mutex, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThreatContext { pub matched_iocs: Vec, pub threat_actor: Option,