debugging and fixing cicd

This commit is contained in:
pandaadir05
2025-11-21 01:32:33 +02:00
parent d5e39529fa
commit 407bba8ae5
5 changed files with 73 additions and 44 deletions

View File

@@ -14,7 +14,17 @@
"Bash(Select-String \"warning\")", "Bash(Select-String \"warning\")",
"Bash(Select-Object -First 30)", "Bash(Select-Object -First 30)",
"Bash(cargo fmt:*)", "Bash(cargo fmt:*)",
"Bash(git config:*)" "Bash(git config:*)",
"Bash(git push:*)",
"Bash(rustc:*)",
"Bash(cargo --version:*)",
"Bash(rustup install:*)",
"Bash(rustup override set:*)",
"Bash(cargo generate-lockfile:*)",
"Bash(rustup override unset:*)",
"Bash(Select-String -Pattern \"Checking|Finished|error\")",
"Bash(Select-Object -First 20)",
"Bash(git restore:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@@ -586,24 +586,24 @@ fn parse_pe_sections(data: &[u8]) -> Result<Vec<PESection>> {
use crate::GhostError; use crate::GhostError;
if data.len() < 0x40 { if data.len() < 0x40 {
return Err(GhostError::ParseError("Buffer too small".to_string())); return Err(GhostError::InvalidInput { message: "Buffer too small".to_string() });
} }
// Check DOS signature // Check DOS signature
if &data[0..2] != b"MZ" { if &data[0..2] != b"MZ" {
return Err(GhostError::ParseError("Invalid DOS signature".to_string())); return Err(GhostError::InvalidInput { message: "Invalid DOS signature".to_string() });
} }
// Get PE offset // Get PE offset
let pe_offset = u32::from_le_bytes([data[0x3c], data[0x3d], data[0x3e], data[0x3f]]) as usize; let pe_offset = u32::from_le_bytes([data[0x3c], data[0x3d], data[0x3e], data[0x3f]]) as usize;
if pe_offset + 0x18 >= data.len() { if pe_offset + 0x18 >= data.len() {
return Err(GhostError::ParseError("Invalid PE offset".to_string())); return Err(GhostError::InvalidInput { message: "Invalid PE offset".to_string() });
} }
// Check PE signature // Check PE signature
if &data[pe_offset..pe_offset + 4] != b"PE\0\0" { if &data[pe_offset..pe_offset + 4] != b"PE\0\0" {
return Err(GhostError::ParseError("Invalid PE signature".to_string())); return Err(GhostError::InvalidInput { message: "Invalid PE signature".to_string() });
} }
// Parse COFF header // Parse COFF header

View File

@@ -73,12 +73,11 @@ impl LiveThreatFeeds {
pub async fn update_feeds(&mut self) -> Result<usize, GhostError> { pub async fn update_feeds(&mut self) -> Result<usize, GhostError> {
let mut updated_count = 0; let mut updated_count = 0;
for feed in &mut self.feeds { // Clone feeds to avoid borrow checker issues
if !feed.enabled { let feeds: Vec<ThreatFeed> = self.feeds.iter().filter(|f| f.enabled).cloned().collect();
continue;
}
match self.fetch_feed_data(feed).await { for feed in feeds {
match self.fetch_feed_data(&feed).await {
Ok(iocs) => { Ok(iocs) => {
log::info!("Updated {} with {} IOCs", feed.name, iocs.len()); log::info!("Updated {} with {} IOCs", feed.name, iocs.len());
@@ -87,7 +86,10 @@ impl LiveThreatFeeds {
self.ioc_cache.insert(ioc.value.clone(), ioc); self.ioc_cache.insert(ioc.value.clone(), ioc);
} }
feed.last_updated = SystemTime::now(); // Update feed's last_updated time
if let Some(f) = self.feeds.iter_mut().find(|f| f.name == feed.name) {
f.last_updated = SystemTime::now();
}
updated_count += 1; updated_count += 1;
} }
Err(e) => { Err(e) => {
@@ -114,7 +116,7 @@ impl LiveThreatFeeds {
async fn fetch_abuseipdb(&self, feed: &ThreatFeed) -> Result<Vec<CachedIOC>, GhostError> { async fn fetch_abuseipdb(&self, feed: &ThreatFeed) -> Result<Vec<CachedIOC>, GhostError> {
let api_key = feed.api_key.as_ref().ok_or_else(|| { let api_key = feed.api_key.as_ref().ok_or_else(|| {
GhostError::ConfigurationError("AbuseIPDB requires API key".to_string()) GhostError::Configuration { message: "AbuseIPDB requires API key".to_string() }
})?; })?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -127,17 +129,17 @@ impl LiveThreatFeeds {
.query(&[("confidenceMinimum", "90")]) .query(&[("confidenceMinimum", "90")])
.send() .send()
.await .await
.map_err(|e| GhostError::NetworkError(format!("AbuseIPDB request failed: {}", e)))?; .map_err(|e| GhostError::ThreatIntel { message: format!("AbuseIPDB request failed: {}", e) })?;
if !response.status().is_success() { if !response.status().is_success() {
return Err(GhostError::NetworkError(format!( return Err(GhostError::ThreatIntel { message: format!(
"AbuseIPDB API returned status: {}", "AbuseIPDB API returned status: {}",
response.status() response.status()
))); ) });
} }
let data: serde_json::Value = response.json().await.map_err(|e| { let data: serde_json::Value = response.json().await.map_err(|e| {
GhostError::ParseError(format!("Failed to parse AbuseIPDB response: {}", e)) GhostError::Serialization { message: format!("Failed to parse AbuseIPDB response: {}", e) }
})?; })?;
let mut iocs = Vec::new(); let mut iocs = Vec::new();
@@ -186,18 +188,18 @@ impl LiveThreatFeeds {
.send() .send()
.await .await
.map_err(|e| { .map_err(|e| {
GhostError::NetworkError(format!("MalwareBazaar request failed: {}", e)) GhostError::ThreatIntel { message: format!("MalwareBazaar request failed: {}", e) }
})?; })?;
if !response.status().is_success() { if !response.status().is_success() {
return Err(GhostError::NetworkError(format!( return Err(GhostError::ThreatIntel { message: format!(
"MalwareBazaar API returned status: {}", "MalwareBazaar API returned status: {}",
response.status() response.status()
))); ) });
} }
let data: serde_json::Value = response.json().await.map_err(|e| { let data: serde_json::Value = response.json().await.map_err(|e| {
GhostError::ParseError(format!("Failed to parse MalwareBazaar response: {}", e)) GhostError::Serialization { message: format!("Failed to parse MalwareBazaar response: {}", e) }
})?; })?;
let mut iocs = Vec::new(); let mut iocs = Vec::new();
@@ -235,7 +237,7 @@ impl LiveThreatFeeds {
async fn fetch_alienvault(&self, feed: &ThreatFeed) -> Result<Vec<CachedIOC>, GhostError> { async fn fetch_alienvault(&self, feed: &ThreatFeed) -> Result<Vec<CachedIOC>, GhostError> {
let api_key = feed.api_key.as_ref().ok_or_else(|| { let api_key = feed.api_key.as_ref().ok_or_else(|| {
GhostError::ConfigurationError("AlienVault OTX requires API key".to_string()) GhostError::Configuration { message: "AlienVault OTX requires API key".to_string() }
})?; })?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -248,17 +250,17 @@ impl LiveThreatFeeds {
.query(&[("limit", "50")]) .query(&[("limit", "50")])
.send() .send()
.await .await
.map_err(|e| GhostError::NetworkError(format!("AlienVault request failed: {}", e)))?; .map_err(|e| GhostError::ThreatIntel { message: format!("AlienVault request failed: {}", e) })?;
if !response.status().is_success() { if !response.status().is_success() {
return Err(GhostError::NetworkError(format!( return Err(GhostError::ThreatIntel { message: format!(
"AlienVault API returned status: {}", "AlienVault API returned status: {}",
response.status() response.status()
))); ) });
} }
let data: serde_json::Value = response.json().await.map_err(|e| { let data: serde_json::Value = response.json().await.map_err(|e| {
GhostError::ParseError(format!("Failed to parse AlienVault response: {}", e)) GhostError::Serialization { message: format!("Failed to parse AlienVault response: {}", e) }
})?; })?;
let mut iocs = Vec::new(); let mut iocs = Vec::new();

View File

@@ -7,7 +7,6 @@
///! - Function address resolution ///! - Function address resolution
use crate::{GhostError, Result}; use crate::{GhostError, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// PE data directory indices /// PE data directory indices
pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0; pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0;
@@ -435,9 +434,7 @@ pub fn parse_iat_from_memory(
_base_address: usize, _base_address: usize,
_memory_reader: impl Fn(u32, usize, usize) -> Result<Vec<u8>>, _memory_reader: impl Fn(u32, usize, usize) -> Result<Vec<u8>>,
) -> Result<Vec<ImportEntry>> { ) -> Result<Vec<ImportEntry>> {
Err(GhostError::NotImplemented( Err(GhostError::PlatformNotSupported { feature: "IAT parsing not implemented for this platform".to_string() })
"IAT parsing not implemented for this platform".to_string(),
))
} }
#[cfg(not(windows))] #[cfg(not(windows))]
@@ -447,7 +444,5 @@ pub fn detect_iat_hooks(
_disk_path: &str, _disk_path: &str,
_memory_reader: impl Fn(u32, usize, usize) -> Result<Vec<u8>>, _memory_reader: impl Fn(u32, usize, usize) -> Result<Vec<u8>>,
) -> Result<IATHookResult> { ) -> Result<IATHookResult> {
Err(GhostError::NotImplemented( Err(GhostError::PlatformNotSupported { feature: "IAT hook detection not implemented for this platform".to_string() })
"IAT hook detection not implemented for this platform".to_string(),
))
} }

View File

@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use std::time::SystemTime; use std::time::SystemTime;
use yara::{Compiler, Rules, Scanner}; use yara::{Compiler, Rules, Scanner};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct DynamicYaraEngine { pub struct DynamicYaraEngine {
rules_path: Option<PathBuf>, rules_path: Option<PathBuf>,
#[serde(skip)] #[serde(skip)]
@@ -15,6 +15,28 @@ pub struct DynamicYaraEngine {
scan_cache: HashMap<String, CachedScanResult>, scan_cache: HashMap<String, CachedScanResult>,
} }
impl std::fmt::Debug for DynamicYaraEngine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynamicYaraEngine")
.field("rules_path", &self.rules_path)
.field("has_compiled_rules", &self.compiled_rules.is_some())
.field("rule_metadata", &self.rule_metadata)
.field("scan_cache", &self.scan_cache)
.finish()
}
}
impl Clone for DynamicYaraEngine {
fn clone(&self) -> Self {
DynamicYaraEngine {
rules_path: self.rules_path.clone(),
compiled_rules: None, // Rules cannot be cloned, will need to recompile
rule_metadata: self.rule_metadata.clone(),
scan_cache: self.scan_cache.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct YaraRuleMetadata { pub struct YaraRuleMetadata {
pub name: String, pub name: String,
@@ -97,7 +119,7 @@ impl DynamicYaraEngine {
/// Compile all YARA rules from the rules directory /// Compile all YARA rules from the rules directory
pub fn compile_rules(&mut self) -> Result<usize, GhostError> { pub fn compile_rules(&mut self) -> Result<usize, GhostError> {
let rules_dir = self.rules_path.as_ref().ok_or_else(|| { let rules_dir = self.rules_path.as_ref().ok_or_else(|| {
GhostError::ConfigurationError("No rules directory configured".to_string()) GhostError::Configuration { message: "No rules directory configured".to_string( })
})?; })?;
if !rules_dir.exists() { if !rules_dir.exists() {
@@ -108,7 +130,7 @@ impl DynamicYaraEngine {
} }
let mut compiler = Compiler::new() let mut compiler = Compiler::new()
.map_err(|e| GhostError::ConfigurationError(format!("YARA compiler error: {}", e)))?; .map_err(|e| GhostError::Configuration { message: format!("YARA compiler error: {}", e }))?;
let mut rule_count = 0; let mut rule_count = 0;
self.rule_metadata.clear(); self.rule_metadata.clear();
@@ -155,7 +177,7 @@ impl DynamicYaraEngine {
} }
self.compiled_rules = Some(compiler.compile_rules().map_err(|e| { self.compiled_rules = Some(compiler.compile_rules().map_err(|e| {
GhostError::ConfigurationError(format!("Rule compilation failed: {}", e)) GhostError::Configuration { message: format!("Rule compilation failed: {}", e })
})?); })?);
log::info!("Successfully compiled {} YARA rules", rule_count); log::info!("Successfully compiled {} YARA rules", rule_count);
@@ -171,7 +193,7 @@ impl DynamicYaraEngine {
} }
let entries = fs::read_dir(dir).map_err(|e| { let entries = fs::read_dir(dir).map_err(|e| {
GhostError::ConfigurationError(format!("Failed to read rules directory: {}", e)) GhostError::Configuration { message: format!("Failed to read rules directory: {}", e })
})?; })?;
for entry in entries.flatten() { for entry in entries.flatten() {
@@ -203,7 +225,7 @@ impl DynamicYaraEngine {
let rules = self let rules = self
.compiled_rules .compiled_rules
.as_ref() .as_ref()
.ok_or_else(|| GhostError::ConfigurationError("YARA rules not compiled".to_string()))?; .ok_or_else(|| GhostError::Configuration { message: "YARA rules not compiled".to_string( }))?;
let mut all_matches = Vec::new(); let mut all_matches = Vec::new();
let mut bytes_scanned = 0u64; let mut bytes_scanned = 0u64;
@@ -266,11 +288,11 @@ impl DynamicYaraEngine {
base_address: usize, base_address: usize,
) -> Result<Vec<RuleMatch>, GhostError> { ) -> Result<Vec<RuleMatch>, GhostError> {
let mut scanner = Scanner::new(rules) let mut scanner = Scanner::new(rules)
.map_err(|e| GhostError::ScanError(format!("Scanner creation failed: {}", e)))?; .map_err(|e| GhostError::Detection { message: format!("Scanner creation failed: {}", e }))?;
let scan_results = scanner let scan_results = scanner
.scan_mem(data) .scan_mem(data)
.map_err(|e| GhostError::ScanError(format!("Scan failed: {}", e)))?; .map_err(|e| GhostError::Detection { message: format!("Scan failed: {}", e }))?;
let mut matches = Vec::new(); let mut matches = Vec::new();
@@ -328,7 +350,7 @@ impl DynamicYaraEngine {
unsafe { unsafe {
let handle = OpenProcess(PROCESS_VM_READ, false, pid) let handle = OpenProcess(PROCESS_VM_READ, false, pid)
.map_err(|e| GhostError::MemoryReadError(format!("OpenProcess failed: {}", e)))?; .map_err(|e| GhostError::MemoryEnumeration { reason: format!("OpenProcess failed: {}", e }))?;
let mut buffer = vec![0u8; region.size]; let mut buffer = vec![0u8; region.size];
let mut bytes_read = 0; let mut bytes_read = 0;
@@ -362,15 +384,15 @@ impl DynamicYaraEngine {
let mem_path = format!("/proc/{}/mem", pid); let mem_path = format!("/proc/{}/mem", pid);
let mut file = File::open(&mem_path).map_err(|e| { let mut file = File::open(&mem_path).map_err(|e| {
GhostError::MemoryReadError(format!("Failed to open {}: {}", mem_path, e)) GhostError::MemoryEnumeration { reason: format!("Failed to open {}: {}", mem_path, e })
})?; })?;
file.seek(SeekFrom::Start(region.base_address as u64)) file.seek(SeekFrom::Start(region.base_address as u64))
.map_err(|e| GhostError::MemoryReadError(format!("Seek failed: {}", e)))?; .map_err(|e| GhostError::MemoryEnumeration { reason: format!("Seek failed: {}", e }))?;
let mut buffer = vec![0u8; region.size]; let mut buffer = vec![0u8; region.size];
file.read_exact(&mut buffer) file.read_exact(&mut buffer)
.map_err(|e| GhostError::MemoryReadError(format!("Read failed: {}", e)))?; .map_err(|e| GhostError::MemoryEnumeration { reason: format!("Read failed: {}", e }))?;
Ok(buffer) Ok(buffer)
} }