1use crate::error::Result;
10use chrono::Utc;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::{Path, PathBuf};
14
15pub mod dependency_parser;
16pub mod framework_detector;
17pub mod frameworks;
18pub mod language_detector;
19pub mod project_context;
20pub mod vulnerability_checker;
21pub mod security_analyzer;
22pub mod security;
23pub mod tool_installer;
24pub mod monorepo_detector;
25pub mod docker_analyzer;
26pub mod display;
27
28pub use dependency_parser::{
30 DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
31 Vulnerability, VulnerabilitySeverity
32};
33
34pub use security_analyzer::{
36 SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
37 SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
38};
39
40pub use security::{
42 SecretPatternManager
43};
44pub use security::config::SecurityConfigPreset;
45
46pub use monorepo_detector::{
48 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
49};
50
51pub use docker_analyzer::{
53 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
54 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
55 analyze_docker_infrastructure
56};
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct DetectedLanguage {
61 pub name: String,
62 pub version: Option<String>,
63 pub confidence: f32,
64 pub files: Vec<PathBuf>,
65 pub main_dependencies: Vec<String>,
66 pub dev_dependencies: Vec<String>,
67 pub package_manager: Option<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
72pub enum TechnologyCategory {
73 MetaFramework,
75 FrontendFramework,
77 BackendFramework,
79 Library(LibraryType),
81 BuildTool,
83 Database,
85 Testing,
87 Runtime,
89 PackageManager,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
95pub enum LibraryType {
96 UI,
98 StateManagement,
100 DataFetching,
102 Routing,
104 Styling,
106 Utility,
108 HttpClient,
110 Authentication,
112 CLI,
114 Other(String),
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120pub struct DetectedTechnology {
121 pub name: String,
122 pub version: Option<String>,
123 pub category: TechnologyCategory,
124 pub confidence: f32,
125 pub requires: Vec<String>,
127 pub conflicts_with: Vec<String>,
129 pub is_primary: bool,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
135pub struct ServiceAnalysis {
136 pub name: String,
137 pub path: PathBuf,
138 pub languages: Vec<DetectedLanguage>,
139 pub technologies: Vec<DetectedTechnology>,
140 pub entry_points: Vec<EntryPoint>,
141 pub ports: Vec<Port>,
142 pub environment_variables: Vec<EnvVar>,
143 pub build_scripts: Vec<BuildScript>,
144 pub service_type: ProjectType,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149pub struct EntryPoint {
150 pub file: PathBuf,
151 pub function: Option<String>,
152 pub command: Option<String>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
157pub struct Port {
158 pub number: u16,
159 pub protocol: Protocol,
160 pub description: Option<String>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
164pub enum Protocol {
165 Tcp,
166 Udp,
167 Http,
168 Https,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
173pub struct EnvVar {
174 pub name: String,
175 pub default_value: Option<String>,
176 pub required: bool,
177 pub description: Option<String>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
182pub enum ProjectType {
183 WebApplication,
184 ApiService,
185 CliTool,
186 Library,
187 MobileApp,
188 DesktopApp,
189 Microservice,
190 StaticSite,
191 Hybrid, Unknown,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
197pub struct BuildScript {
198 pub name: String,
199 pub command: String,
200 pub description: Option<String>,
201 pub is_default: bool,
202}
203
204pub type DependencyMap = HashMap<String, String>;
206
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
209pub enum ArchitectureType {
210 Monolithic,
212 Microservices,
214 Hybrid,
216}
217
218pub type DetectedFramework = DetectedTechnology;
220
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub struct ProjectAnalysis {
224 pub project_root: PathBuf,
225 pub languages: Vec<DetectedLanguage>,
226 pub technologies: Vec<DetectedTechnology>,
228 #[deprecated(note = "Use technologies field instead")]
230 pub frameworks: Vec<DetectedFramework>,
231 pub dependencies: DependencyMap,
232 pub entry_points: Vec<EntryPoint>,
233 pub ports: Vec<Port>,
234 pub environment_variables: Vec<EnvVar>,
235 pub project_type: ProjectType,
236 pub build_scripts: Vec<BuildScript>,
237 pub services: Vec<ServiceAnalysis>,
239 pub architecture_type: ArchitectureType,
241 pub docker_analysis: Option<DockerAnalysis>,
243 pub analysis_metadata: AnalysisMetadata,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
248pub struct AnalysisMetadata {
249 pub timestamp: String,
250 pub analyzer_version: String,
251 pub analysis_duration_ms: u64,
252 pub files_analyzed: usize,
253 pub confidence_score: f32,
254}
255
256#[derive(Debug, Clone)]
258pub struct AnalysisConfig {
259 pub include_dev_dependencies: bool,
260 pub deep_analysis: bool,
261 pub ignore_patterns: Vec<String>,
262 pub max_file_size: usize,
263}
264
265impl Default for AnalysisConfig {
266 fn default() -> Self {
267 Self {
268 include_dev_dependencies: false,
269 deep_analysis: true,
270 ignore_patterns: vec![
271 "node_modules".to_string(),
272 ".git".to_string(),
273 "target".to_string(),
274 "build".to_string(),
275 ".next".to_string(),
276 "dist".to_string(),
277 ],
278 max_file_size: 1024 * 1024, }
280 }
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
285pub struct ProjectInfo {
286 pub path: PathBuf,
288 pub name: String,
290 pub project_category: ProjectCategory,
292 pub analysis: ProjectAnalysis,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
298pub enum ProjectCategory {
299 Frontend,
300 Backend,
301 Api,
302 Service,
303 Library,
304 Tool,
305 Documentation,
306 Infrastructure,
307 Unknown,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
312pub struct MonorepoAnalysis {
313 pub root_path: PathBuf,
315 pub is_monorepo: bool,
317 pub projects: Vec<ProjectInfo>,
319 pub metadata: AnalysisMetadata,
321 pub technology_summary: TechnologySummary,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
327pub struct TechnologySummary {
328 pub languages: Vec<String>,
329 pub frameworks: Vec<String>,
330 pub databases: Vec<String>,
331 pub total_projects: usize,
332 pub architecture_pattern: ArchitecturePattern,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub enum ArchitecturePattern {
338 Monolithic,
340 Fullstack,
342 Microservices,
344 ApiFirst,
346 EventDriven,
348 Mixed,
350}
351
352pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
372 analyze_project_with_config(path, &AnalysisConfig::default())
373}
374
375pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
377 let start_time = std::time::Instant::now();
378
379 let project_root = crate::common::file_utils::validate_project_path(path)?;
381
382 log::info!("Starting analysis of project: {}", project_root.display());
383
384 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
386 log::debug!("Found {} files to analyze", files.len());
387
388 let languages = language_detector::detect_languages(&files, config)?;
390 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
391 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
392 let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
393
394 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
396
397 let duration = start_time.elapsed();
398 let confidence = calculate_confidence_score(&languages, &frameworks);
399
400 #[allow(deprecated)]
401 let analysis = ProjectAnalysis {
402 project_root,
403 languages,
404 technologies: frameworks.clone(), frameworks, dependencies,
407 entry_points: context.entry_points,
408 ports: context.ports,
409 environment_variables: context.environment_variables,
410 project_type: context.project_type,
411 build_scripts: context.build_scripts,
412 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
415 analysis_metadata: AnalysisMetadata {
416 timestamp: Utc::now().to_rfc3339(),
417 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
418 analysis_duration_ms: duration.as_millis() as u64,
419 files_analyzed: files.len(),
420 confidence_score: confidence,
421 },
422 };
423
424 log::info!("Analysis completed in {}ms", duration.as_millis());
425 Ok(analysis)
426}
427
428fn calculate_confidence_score(
430 languages: &[DetectedLanguage],
431 frameworks: &[DetectedFramework],
432) -> f32 {
433 if languages.is_empty() {
434 return 0.0;
435 }
436
437 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
438 let framework_confidence: f32 = if frameworks.is_empty() {
439 0.5 } else {
441 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
442 };
443
444 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
452 fn test_confidence_calculation() {
453 let languages = vec![
454 DetectedLanguage {
455 name: "Rust".to_string(),
456 version: Some("1.70.0".to_string()),
457 confidence: 0.9,
458 files: vec![],
459 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
460 dev_dependencies: vec!["assert_cmd".to_string()],
461 package_manager: Some("cargo".to_string()),
462 }
463 ];
464
465 let technologies = vec![
466 DetectedTechnology {
467 name: "Actix Web".to_string(),
468 version: Some("4.0".to_string()),
469 category: TechnologyCategory::BackendFramework,
470 confidence: 0.8,
471 requires: vec!["serde".to_string(), "tokio".to_string()],
472 conflicts_with: vec![],
473 is_primary: true,
474 }
475 ];
476
477 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
480 assert!(score > 0.8);
481 assert!(score <= 1.0);
482 }
483
484 #[test]
485 fn test_empty_analysis() {
486 let languages = vec![];
487 let frameworks = vec![];
488 let score = calculate_confidence_score(&languages, &frameworks);
489 assert_eq!(score, 0.0);
490 }
491}