threat_intel/
lib.rs

1//! # Red Asgard Threat Intelligence
2//!
3//! A comprehensive threat intelligence framework for Rust applications with multi-source
4//! aggregation, CVE integration, and risk assessment.
5//!
6//! ## Features
7//!
8//! - **Multi-Source Aggregation**: Combine intelligence from MITRE ATT&CK, CVE databases, OSINT sources
9//! - **HTTP Fetching**: Built-in support for authenticated API calls with retry logic
10//! - **Multiple Auth Methods**: API Key, Bearer token, Basic auth support
11//! - **Format Parsers**: JSON, XML (future), STIX (future) support
12//! - **Configurable Updates**: Realtime, hourly, daily, weekly, or manual sync
13//! - **Priority Management**: Source prioritization for conflict resolution
14//! - **Capability-Based**: Query sources by capability (vulnerabilities, IOCs, tactics, etc.)
15//! - **Risk Assessment**: Built-in risk scoring and assessment
16//!
17//! ## Quick Start
18//!
19//! ```rust,no_run
20//! use threat_intel::{ThreatIntelConfig, ThreatIntelEngine};
21//!
22//! # async fn example() -> anyhow::Result<()> {
23//! // Create config with default sources (MITRE ATT&CK, CVE, Abuse.ch)
24//! let config = ThreatIntelConfig::default();
25//!
26//! // Create engine
27//! let mut engine = ThreatIntelEngine::new(config);
28//!
29//! // Initialize (fetches from sources)
30//! engine.initialize().await?;
31//!
32//! // Query for vulnerabilities
33//! let vulns = engine.query_vulnerabilities("apache", "2.4").await?;
34//! println!("Found {} vulnerabilities", vulns.len());
35//! # Ok(())
36//! # }
37//! ```
38
39pub mod assessment;
40pub mod config;
41pub mod constants;
42pub mod feeds;
43pub mod sources;
44pub mod types;
45
46pub use assessment::*;
47pub use config::*;
48pub use constants::*;
49pub use feeds::*;
50pub use sources::*;
51pub use types::*;
52
53use anyhow::Result;
54use chrono::{DateTime, Utc};
55use std::collections::HashMap;
56
57// Optional tracing support
58#[cfg(feature = "tracing")]
59use tracing::{error, info, warn};
60
61#[cfg(not(feature = "tracing"))]
62macro_rules! info {
63    ($($arg:tt)*) => {};
64}
65
66#[cfg(not(feature = "tracing"))]
67macro_rules! warn {
68    ($($arg:tt)*) => {
69        eprintln!("WARN: {}", format!($($arg)*));
70    };
71}
72
73/// Main threat intelligence engine
74pub struct ThreatIntelEngine {
75    config: ThreatIntelConfig,
76    sources: HashMap<String, Box<dyn ThreatSource>>,
77    last_sync: Option<DateTime<Utc>>,
78    cache: ThreatCache,
79}
80
81impl ThreatIntelEngine {
82    /// Create a new threat intelligence engine with the given configuration
83    pub fn new(config: ThreatIntelConfig) -> Self {
84        Self {
85            config,
86            sources: HashMap::new(),
87            last_sync: None,
88            cache: ThreatCache::new(),
89        }
90    }
91
92    /// Initialize the engine by loading all enabled sources
93    pub async fn initialize(&mut self) -> Result<()> {
94        info!("Initializing threat intelligence engine...");
95
96        for source_config in self.config.get_enabled_sources() {
97            info!("Initializing source: {}", source_config.name);
98
99            match self.create_source(source_config).await {
100                Ok(source) => {
101                    self.sources.insert(source_config.id.clone(), source);
102                }
103                Err(e) => {
104                    warn!("Failed to initialize source {}: {}", source_config.name, e);
105                    // Continue with other sources
106                }
107            }
108        }
109
110        info!(
111            "Threat intelligence engine initialized with {} sources",
112            self.sources.len()
113        );
114
115        // Perform initial sync
116        self.sync().await?;
117
118        Ok(())
119    }
120
121    /// Sync all sources to get latest intelligence
122    pub async fn sync(&mut self) -> Result<()> {
123        info!("Syncing threat intelligence sources...");
124
125        for (id, source) in &mut self.sources {
126            match source.fetch().await {
127                Ok(data) => {
128                    info!("Successfully synced source: {}", id);
129                    self.cache.update(id, data);
130                }
131                Err(e) => {
132                    warn!("Failed to sync source {}: {}", id, e);
133                    // Continue with other sources
134                }
135            }
136        }
137
138        self.last_sync = Some(Utc::now());
139        Ok(())
140    }
141
142    /// Query for vulnerabilities matching a product and version
143    pub async fn query_vulnerabilities(
144        &self,
145        product: &str,
146        version: &str,
147    ) -> Result<Vec<Vulnerability>> {
148        let sources = self
149            .config
150            .get_sources_by_capability(SourceCapability::Vulnerabilities);
151
152        let mut results = Vec::new();
153
154        for source_config in sources {
155            if let Some(data) = self.cache.get(&source_config.id) {
156                let vulns = data.vulnerabilities.iter()
157                    .filter(|v| {
158                        v.affected_products.iter().any(|p| {
159                            p.product.to_lowercase().contains(&product.to_lowercase())
160                                && p.version.contains(version)
161                        })
162                    })
163                    .cloned()
164                    .collect::<Vec<_>>();
165
166                results.extend(vulns);
167            }
168        }
169
170        Ok(results)
171    }
172
173    /// Query for IOCs (Indicators of Compromise)
174    pub async fn query_iocs(&self, ioc_type: IOCType) -> Result<Vec<IOC>> {
175        let sources = self
176            .config
177            .get_sources_by_capability(SourceCapability::Ioc);
178
179        let mut results = Vec::new();
180
181        for source_config in sources {
182            if let Some(data) = self.cache.get(&source_config.id) {
183                let iocs = data.iocs.iter()
184                    .filter(|ioc| ioc.ioc_type == ioc_type)
185                    .cloned()
186                    .collect::<Vec<_>>();
187
188                results.extend(iocs);
189            }
190        }
191
192        Ok(results)
193    }
194
195    /// Get threat actors by name or alias
196    pub async fn query_threat_actors(&self, query: &str) -> Result<Vec<ThreatActor>> {
197        let sources = self
198            .config
199            .get_sources_by_capability(SourceCapability::ThreatActors);
200
201        let mut results = Vec::new();
202        let query_lower = query.to_lowercase();
203
204        for source_config in sources {
205            if let Some(data) = self.cache.get(&source_config.id) {
206                let actors = data.threat_actors.iter()
207                    .filter(|actor| {
208                        actor.name.to_lowercase().contains(&query_lower)
209                            || actor.aliases.iter().any(|a| a.to_lowercase().contains(&query_lower))
210                    })
211                    .cloned()
212                    .collect::<Vec<_>>();
213
214                results.extend(actors);
215            }
216        }
217
218        Ok(results)
219    }
220
221    /// Assess risk for a given context
222    pub fn assess_risk(&self, vulnerabilities: &[Vulnerability]) -> RiskAssessment {
223        assessment::assess_risk(vulnerabilities)
224    }
225
226    /// Get statistics about cached intelligence
227    pub fn get_stats(&self) -> ThreatIntelStats {
228        let mut total_vulnerabilities = 0;
229        let mut total_iocs = 0;
230        let mut total_threat_actors = 0;
231
232        for data in self.cache.cache.values() {
233            total_vulnerabilities += data.vulnerabilities.len();
234            total_iocs += data.iocs.len();
235            total_threat_actors += data.threat_actors.len();
236        }
237
238        ThreatIntelStats {
239            sources_count: self.sources.len(),
240            total_vulnerabilities,
241            total_iocs,
242            total_threat_actors,
243            last_sync: self.last_sync,
244        }
245    }
246
247    // Private helper to create source instances
248    async fn create_source(&self, config: &SourceConfig) -> Result<Box<dyn ThreatSource>> {
249        match config.source_type {
250            SourceType::MitreAttack => {
251                Ok(Box::new(sources::MitreAttackSource::new(config.clone())))
252            }
253            SourceType::Cve => {
254                Ok(Box::new(sources::CVESource::new(config.clone())))
255            }
256            SourceType::Osint => {
257                Ok(Box::new(sources::OSINTSource::new(config.clone())))
258            }
259            SourceType::Commercial | SourceType::Custom => {
260                Ok(Box::new(sources::GenericSource::new(config.clone())))
261            }
262        }
263    }
264}
265
266// Internal cache for threat intelligence data
267struct ThreatCache {
268    cache: HashMap<String, ThreatData>,
269}
270
271impl ThreatCache {
272    fn new() -> Self {
273        Self {
274            cache: HashMap::new(),
275        }
276    }
277
278    fn update(&mut self, source_id: &str, data: ThreatData) {
279        self.cache.insert(source_id.to_string(), data);
280    }
281
282    fn get(&self, source_id: &str) -> Option<&ThreatData> {
283        self.cache.get(source_id)
284    }
285}