1pub use cache::{AuditCache, Cache, CacheBucket, CacheEntry, DatabaseMetadata, Freshness};
4pub use config::{
5 CacheConfig, CiConfig, Config, ConfigLoader, DefaultConfig, IgnoreConfig, MaintenanceConfig,
6 OutputConfig, PackageIgnoreRule, ProjectConfig, ResolverConfig, SourcesConfig,
7};
8pub use dependency::scanner::{DependencyScanner, DependencyStats};
9pub use maintenance::{
10 MaintenanceCheckConfig, MaintenanceIssue, MaintenanceIssueType, MaintenanceSummary,
11 ProjectState, ProjectStatus, SimpleIndexClient,
12};
13pub use output::report::{AuditReport, AuditSummary, ReportGenerator};
14pub use providers::{VulnerabilityProvider, VulnerabilitySource};
15pub use types::{
16 AuditFormat, PackageName, ResolutionCacheEntry, ResolvedDependency, ResolverType,
17 SeverityLevel, Version, VulnerabilitySourceType,
18};
19pub use vulnerability::{
20 database::{Severity, VersionRange, Vulnerability, VulnerabilityDatabase, VulnerabilityMatch},
21 matcher::{DatabaseStats, FixAnalysis, FixSuggestion, MatcherConfig, VulnerabilityMatcher},
22};
23
24pub mod cache;
25pub mod cli;
26pub mod config;
27pub mod dependency;
28pub mod maintenance;
29pub mod output;
30pub mod parsers;
31pub mod providers;
32pub mod types;
33pub mod vulnerability;
34
35mod error;
36
37pub use error::{AuditError, Result};
38
39#[cfg(feature = "python")]
40mod python;
41
42pub struct AuditEngine {
50 scanner: DependencyScanner,
51 cache: Option<AuditCache>,
52}
53
54impl AuditEngine {
55 pub fn new() -> Self {
57 Self {
58 scanner: DependencyScanner::default(),
59 cache: None,
60 }
61 }
62
63 pub fn with_scanner(scanner: DependencyScanner) -> Self {
65 Self {
66 scanner,
67 cache: None,
68 }
69 }
70
71 pub fn with_cache(mut self, cache: AuditCache) -> Self {
73 self.cache = Some(cache);
74 self
75 }
76
77 pub async fn audit_project<P: AsRef<std::path::Path>>(
79 &self,
80 project_path: P,
81 source_type: VulnerabilitySourceType,
82 min_severity: SeverityLevel,
83 ignore_ids: &[String],
84 direct_only: bool,
85 include_withdrawn: bool,
86 ) -> Result<AuditReport> {
87 let project_path = project_path.as_ref();
88
89 let (dependencies, skipped_packages, parser_name) =
91 self.scanner.scan_project(project_path).await?;
92 let dependency_stats = self.scanner.get_stats(&dependencies);
93 let warnings =
94 self.scanner
95 .validate_dependencies(&dependencies, &skipped_packages, &parser_name);
96
97 let cache = self.cache.as_ref().cloned().unwrap_or_else(|| {
99 let temp_dir = std::env::temp_dir().join("pysentry-cache");
100 AuditCache::new(temp_dir)
101 });
102
103 let vuln_source = VulnerabilitySource::new(
104 source_type,
105 cache,
106 false,
107 crate::config::HttpConfig::default(),
108 );
109
110 let packages: Vec<(String, String)> = dependencies
112 .iter()
113 .map(|dep| (dep.name.to_string(), dep.version.to_string()))
114 .collect();
115
116 let database = vuln_source.fetch_vulnerabilities(&packages).await?;
117
118 let matcher_config = MatcherConfig::new(
120 min_severity,
121 ignore_ids.to_vec(),
122 vec![],
123 direct_only,
124 include_withdrawn,
125 );
126 let matcher = VulnerabilityMatcher::new(database, matcher_config);
127
128 let matches = matcher.find_vulnerabilities(&dependencies)?;
129 let filtered_matches = matcher.filter_matches(matches);
130
131 let database_stats = matcher.get_database_stats();
132 let fix_analysis = matcher.analyze_fixes(&filtered_matches);
133
134 let report = AuditReport::new(
136 dependency_stats,
137 database_stats,
138 filtered_matches,
139 fix_analysis,
140 warnings,
141 Vec::new(), );
143
144 Ok(report)
145 }
146}
147
148impl Default for AuditEngine {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_audit_engine_creation() {
160 let engine = AuditEngine::new();
161 assert!(engine.cache.is_none());
162 }
163
164 #[test]
165 fn test_audit_engine_with_cache() {
166 let cache = AuditCache::new(std::env::temp_dir().join("test-cache"));
167 let engine = AuditEngine::new().with_cache(cache);
168 assert!(engine.cache.is_some());
169 }
170}