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 context;
20pub mod vulnerability;
21pub mod security_analyzer;
22pub mod security;
23pub mod tool_management;
24pub mod runtime;
25pub mod monorepo;
26pub mod docker_analyzer;
27pub mod display;
28
29pub use dependency_parser::{
31 DependencyInfo, DependencyAnalysis, DetailedDependencyMap
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 tool_management::{ToolInstaller, ToolDetector, ToolStatus, InstallationSource};
48
49pub use runtime::{RuntimeDetector, JavaScriptRuntime, PackageManager, RuntimeDetectionResult, DetectionConfidence};
51
52pub use vulnerability::{VulnerabilityChecker, VulnerabilityInfo, VulnerabilityReport, VulnerableDependency};
54pub use vulnerability::types::VulnerabilitySeverity as VulnSeverity;
55
56pub use monorepo::{
58 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
59};
60
61pub use docker_analyzer::{
63 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
64 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
65 analyze_docker_infrastructure
66};
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
70pub struct DetectedLanguage {
71 pub name: String,
72 pub version: Option<String>,
73 pub confidence: f32,
74 pub files: Vec<PathBuf>,
75 pub main_dependencies: Vec<String>,
76 pub dev_dependencies: Vec<String>,
77 pub package_manager: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
82pub enum TechnologyCategory {
83 MetaFramework,
85 FrontendFramework,
87 BackendFramework,
89 Library(LibraryType),
91 BuildTool,
93 Database,
95 Testing,
97 Runtime,
99 PackageManager,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
105pub enum LibraryType {
106 UI,
108 StateManagement,
110 DataFetching,
112 Routing,
114 Styling,
116 Utility,
118 HttpClient,
120 Authentication,
122 CLI,
124 Other(String),
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
130pub struct DetectedTechnology {
131 pub name: String,
132 pub version: Option<String>,
133 pub category: TechnologyCategory,
134 pub confidence: f32,
135 pub requires: Vec<String>,
137 pub conflicts_with: Vec<String>,
139 pub is_primary: bool,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
145pub struct ServiceAnalysis {
146 pub name: String,
147 pub path: PathBuf,
148 pub languages: Vec<DetectedLanguage>,
149 pub technologies: Vec<DetectedTechnology>,
150 pub entry_points: Vec<EntryPoint>,
151 pub ports: Vec<Port>,
152 pub environment_variables: Vec<EnvVar>,
153 pub build_scripts: Vec<BuildScript>,
154 pub service_type: ProjectType,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub struct EntryPoint {
160 pub file: PathBuf,
161 pub function: Option<String>,
162 pub command: Option<String>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
167pub struct Port {
168 pub number: u16,
169 pub protocol: Protocol,
170 pub description: Option<String>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
174pub enum Protocol {
175 Tcp,
176 Udp,
177 Http,
178 Https,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
183pub struct EnvVar {
184 pub name: String,
185 pub default_value: Option<String>,
186 pub required: bool,
187 pub description: Option<String>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192pub enum ProjectType {
193 WebApplication,
194 ApiService,
195 CliTool,
196 Library,
197 MobileApp,
198 DesktopApp,
199 Microservice,
200 StaticSite,
201 Hybrid, Unknown,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
207pub struct BuildScript {
208 pub name: String,
209 pub command: String,
210 pub description: Option<String>,
211 pub is_default: bool,
212}
213
214pub type DependencyMap = HashMap<String, String>;
216
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
219pub enum ArchitectureType {
220 Monolithic,
222 Microservices,
224 Hybrid,
226}
227
228pub type DetectedFramework = DetectedTechnology;
230
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233pub struct ProjectAnalysis {
234 pub project_root: PathBuf,
235 pub languages: Vec<DetectedLanguage>,
236 pub technologies: Vec<DetectedTechnology>,
238 #[deprecated(note = "Use technologies field instead")]
240 pub frameworks: Vec<DetectedFramework>,
241 pub dependencies: DependencyMap,
242 pub entry_points: Vec<EntryPoint>,
243 pub ports: Vec<Port>,
244 pub environment_variables: Vec<EnvVar>,
245 pub project_type: ProjectType,
246 pub build_scripts: Vec<BuildScript>,
247 pub services: Vec<ServiceAnalysis>,
249 pub architecture_type: ArchitectureType,
251 pub docker_analysis: Option<DockerAnalysis>,
253 pub analysis_metadata: AnalysisMetadata,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
258pub struct AnalysisMetadata {
259 pub timestamp: String,
260 pub analyzer_version: String,
261 pub analysis_duration_ms: u64,
262 pub files_analyzed: usize,
263 pub confidence_score: f32,
264}
265
266#[derive(Debug, Clone)]
268pub struct AnalysisConfig {
269 pub include_dev_dependencies: bool,
270 pub deep_analysis: bool,
271 pub ignore_patterns: Vec<String>,
272 pub max_file_size: usize,
273}
274
275impl Default for AnalysisConfig {
276 fn default() -> Self {
277 Self {
278 include_dev_dependencies: false,
279 deep_analysis: true,
280 ignore_patterns: vec![
281 "node_modules".to_string(),
282 ".git".to_string(),
283 "target".to_string(),
284 "build".to_string(),
285 ".next".to_string(),
286 "dist".to_string(),
287 ],
288 max_file_size: 1024 * 1024, }
290 }
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
295pub struct ProjectInfo {
296 pub path: PathBuf,
298 pub name: String,
300 pub project_category: ProjectCategory,
302 pub analysis: ProjectAnalysis,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
308pub enum ProjectCategory {
309 Frontend,
310 Backend,
311 Api,
312 Service,
313 Library,
314 Tool,
315 Documentation,
316 Infrastructure,
317 Unknown,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
322pub struct MonorepoAnalysis {
323 pub root_path: PathBuf,
325 pub is_monorepo: bool,
327 pub projects: Vec<ProjectInfo>,
329 pub metadata: AnalysisMetadata,
331 pub technology_summary: TechnologySummary,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub struct TechnologySummary {
338 pub languages: Vec<String>,
339 pub frameworks: Vec<String>,
340 pub databases: Vec<String>,
341 pub total_projects: usize,
342 pub architecture_pattern: ArchitecturePattern,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
347pub enum ArchitecturePattern {
348 Monolithic,
350 Fullstack,
352 Microservices,
354 ApiFirst,
356 EventDriven,
358 Mixed,
360}
361
362pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
382 analyze_project_with_config(path, &AnalysisConfig::default())
383}
384
385pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
387 let start_time = std::time::Instant::now();
388
389 let project_root = crate::common::file_utils::validate_project_path(path)?;
391
392 log::info!("Starting analysis of project: {}", project_root.display());
393
394 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
396 log::debug!("Found {} files to analyze", files.len());
397
398 let languages = language_detector::detect_languages(&files, config)?;
400 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
401 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
402 let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
403
404 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
406
407 let duration = start_time.elapsed();
408 let confidence = calculate_confidence_score(&languages, &frameworks);
409
410 #[allow(deprecated)]
411 let analysis = ProjectAnalysis {
412 project_root,
413 languages,
414 technologies: frameworks.clone(), frameworks, dependencies,
417 entry_points: context.entry_points,
418 ports: context.ports,
419 environment_variables: context.environment_variables,
420 project_type: context.project_type,
421 build_scripts: context.build_scripts,
422 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
425 analysis_metadata: AnalysisMetadata {
426 timestamp: Utc::now().to_rfc3339(),
427 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
428 analysis_duration_ms: duration.as_millis() as u64,
429 files_analyzed: files.len(),
430 confidence_score: confidence,
431 },
432 };
433
434 log::info!("Analysis completed in {}ms", duration.as_millis());
435 Ok(analysis)
436}
437
438fn calculate_confidence_score(
440 languages: &[DetectedLanguage],
441 frameworks: &[DetectedFramework],
442) -> f32 {
443 if languages.is_empty() {
444 return 0.0;
445 }
446
447 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
448 let framework_confidence: f32 = if frameworks.is_empty() {
449 0.5 } else {
451 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
452 };
453
454 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 #[test]
462 fn test_confidence_calculation() {
463 let languages = vec![
464 DetectedLanguage {
465 name: "Rust".to_string(),
466 version: Some("1.70.0".to_string()),
467 confidence: 0.9,
468 files: vec![],
469 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
470 dev_dependencies: vec!["assert_cmd".to_string()],
471 package_manager: Some("cargo".to_string()),
472 }
473 ];
474
475 let technologies = vec![
476 DetectedTechnology {
477 name: "Actix Web".to_string(),
478 version: Some("4.0".to_string()),
479 category: TechnologyCategory::BackendFramework,
480 confidence: 0.8,
481 requires: vec!["serde".to_string(), "tokio".to_string()],
482 conflicts_with: vec![],
483 is_primary: true,
484 }
485 ];
486
487 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
490 assert!(score > 0.8);
491 assert!(score <= 1.0);
492 }
493
494 #[test]
495 fn test_empty_analysis() {
496 let languages = vec![];
497 let frameworks = vec![];
498 let score = calculate_confidence_score(&languages, &frameworks);
499 assert_eq!(score, 0.0);
500 }
501}