Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use cryptotrace::analyzers::file::analyze_bytes;
use cryptotrace::types::SourceType;

Expand Down
2 changes: 1 addition & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ skip-tree = [
[sources]
unknown-registry = "deny"
unknown-git = "deny"
allow-org = []
allow-org = { github = [], gitlab = [] }
allow-git = []
55 changes: 31 additions & 24 deletions src/analyzers/file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::Result;
use crate::providers::AiProvider;
use crate::sanitization::sandbox::Sandbox;
use crate::signatures::{default_registry, match_signatures, MagicEntry};
use crate::signatures::{MagicEntry, default_registry, match_signatures};
use crate::types::DetectionResult;

/// Analyze a file by reading its contents and running the full detection pipeline.
Expand All @@ -12,7 +12,10 @@ pub fn analyze_file(path: &std::path::Path) -> Result<DetectionResult> {
}

/// Analyze raw bytes through the detection pipeline.
pub fn analyze_bytes(data: &[u8], source_type: crate::types::SourceType) -> Result<DetectionResult> {
pub fn analyze_bytes(
data: &[u8],
source_type: crate::types::SourceType,
) -> Result<DetectionResult> {
// Entropy analysis
let (entropy, _freq) = crate::core::entropy::shannon_entropy(data);
let sliding = crate::core::sliding_entropy::sliding_window_entropy(data, None, None, None);
Expand Down Expand Up @@ -50,7 +53,10 @@ pub fn analyze_bytes(data: &[u8], source_type: crate::types::SourceType) -> Resu
);

// Overlay signature registry info (strongest signal)
if let Some(best) = matched_signatures.iter().max_by_key(|e| e.magic_bytes.len()) {
if let Some(best) = matched_signatures
.iter()
.max_by_key(|e| e.magic_bytes.len())
{
if result.algorithm.is_none() && result.detected_type == "plaintext" {
result.detected_type = best.category.clone();
result.algorithm = Some(best.id.clone());
Expand All @@ -71,22 +77,26 @@ pub fn analyze_bytes(data: &[u8], source_type: crate::types::SourceType) -> Resu
/// Run detection through the sandboxed worker subprocess.
/// The worker performs the actual analysis; if it crashes, we fall back to
/// in-process analysis and log a warning.
pub fn analyze_file_sandboxed(path: &std::path::Path, sandbox: &Sandbox) -> Result<DetectionResult> {
pub fn analyze_file_sandboxed(
path: &std::path::Path,
sandbox: &Sandbox,
) -> Result<DetectionResult> {
let guard = crate::sanitization::InputGuard::new();
let sanitized = guard.sanitize_file(path)?;

// Try sandboxed detection
match sandbox.run_worker("detect", &sanitized.raw_bytes) {
Ok(output) => {
serde_json::from_slice(&output).map_err(|e| {
crate::error::CryptoTraceError::Other(format!(
"Failed to parse worker output as DetectionResult: {}",
e
))
})
}
Ok(output) => serde_json::from_slice(&output).map_err(|e| {
crate::error::CryptoTraceError::Other(format!(
"Failed to parse worker output as DetectionResult: {}",
e
))
}),
Err(e) => {
tracing::warn!("Sandboxed detection failed, falling back to in-process: {}", e);
tracing::warn!(
"Sandboxed detection failed, falling back to in-process: {}",
e
);
analyze_bytes(&sanitized.raw_bytes, crate::types::SourceType::File)
}
}
Expand All @@ -95,14 +105,9 @@ pub fn analyze_file_sandboxed(path: &std::path::Path, sandbox: &Sandbox) -> Resu
/// Run byte analysis through the sandboxed worker subprocess.
pub fn analyze_bytes_sandboxed(data: &[u8], sandbox: &Sandbox) -> Result<DetectionResult> {
match sandbox.run_worker("detect", data) {
Ok(output) => {
serde_json::from_slice(&output).map_err(|e| {
crate::error::CryptoTraceError::Other(format!(
"Failed to parse worker output: {}",
e
))
})
}
Ok(output) => serde_json::from_slice(&output).map_err(|e| {
crate::error::CryptoTraceError::Other(format!("Failed to parse worker output: {}", e))
}),
Err(e) => {
tracing::warn!("Sandboxed byte analysis failed, falling back: {}", e);
analyze_bytes(data, crate::types::SourceType::Binary)
Expand Down Expand Up @@ -133,9 +138,11 @@ mod tests {

#[test]
fn test_analyze_bytes_md5() {
let result =
analyze_bytes(b"5f4dcc3b5aa765d61d8327deb882cf99", crate::types::SourceType::String)
.unwrap();
let result = analyze_bytes(
b"5f4dcc3b5aa765d61d8327deb882cf99",
crate::types::SourceType::String,
)
.unwrap();
assert_eq!(result.detected_type, "hash");
assert_eq!(result.algorithm.as_deref(), Some("MD5"));
}
Expand Down
2 changes: 1 addition & 1 deletion src/analyzers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod file;
pub mod string;
pub mod recursive;
pub mod string;
13 changes: 10 additions & 3 deletions src/analyzers/recursive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,16 @@ pub fn analyze_recursive(data: &[u8], config: &RecursiveConfig) -> Result<Vec<La
let encoding_detection = crate::core::encoding::detect_encoding(&input_str);
let compression_detection = crate::core::compression::detect_compression(&current_data);
let (entropy, _) = crate::core::entropy::shannon_entropy(&current_data);
let encryption_detection = crate::core::encryption::detect_encryption(&current_data, entropy);
let encryption_detection =
crate::core::encryption::detect_encryption(&current_data, entropy);

// Determine if we should try to unwrap
let (detected_type, algorithm, confidence) = if let Some(e) = encoding_detection.as_ref() {
("encoding".to_string(), e.encoding_type.clone(), e.confidence)
(
"encoding".to_string(),
e.encoding_type.clone(),
e.confidence,
)
} else if let Some(c) = compression_detection.as_ref() {
("compression".to_string(), c.format.clone(), c.confidence)
} else if let Some(e) = encryption_detection.as_ref() {
Expand Down Expand Up @@ -115,7 +120,9 @@ pub fn analyze_recursive(data: &[u8], config: &RecursiveConfig) -> Result<Vec<La
}
});

let preview = decoded.as_ref().map(|d| d.iter().take(64).cloned().collect());
let preview = decoded
.as_ref()
.map(|d| d.iter().take(64).cloned().collect());

let layer = Layer {
depth,
Expand Down
19 changes: 12 additions & 7 deletions src/bin/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ fn main() {
"decompress" => run_decompress(&input),
"passthrough" => Ok(input),
_ => {
emit_error("unknown_operation", &format!("Unknown operation: {}", operation));
emit_error(
"unknown_operation",
&format!("Unknown operation: {}", operation),
);
std::process::exit(1);
}
};
Expand All @@ -54,11 +57,9 @@ fn main() {

/// Run the full detection pipeline and output JSON.
fn run_detect(input: &[u8]) -> Result<Vec<u8>, String> {
let result = cryptotrace::analyzers::file::analyze_bytes(
input,
cryptotrace::types::SourceType::Binary,
)
.map_err(|e| format!("Detection failed: {}", e))?;
let result =
cryptotrace::analyzers::file::analyze_bytes(input, cryptotrace::types::SourceType::Binary)
.map_err(|e| format!("Detection failed: {}", e))?;

serde_json::to_vec(&result).map_err(|e| format!("JSON serialization: {}", e))
}
Expand Down Expand Up @@ -89,5 +90,9 @@ fn emit_error(code: &str, message: &str) {
"error": code,
"message": message,
});
let _ = writeln!(io::stderr(), "{}", serde_json::to_string(&err).unwrap_or_default());
let _ = writeln!(
io::stderr(),
"{}",
serde_json::to_string(&err).unwrap_or_default()
);
}
11 changes: 7 additions & 4 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ impl<V> LruCache<V> {
self.entries.remove(&evict_key);
}
}
self.entries.insert(key, CacheEntry {
value,
last_access: Instant::now(),
});
self.entries.insert(
key,
CacheEntry {
value,
last_access: Instant::now(),
},
);
}

pub fn contains(&self, key: &str) -> bool {
Expand Down
Loading
Loading