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 pub file_indicators: Vec<String>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147pub struct ServiceAnalysis {
148 pub name: String,
149 pub path: PathBuf,
150 pub languages: Vec<DetectedLanguage>,
151 pub technologies: Vec<DetectedTechnology>,
152 pub entry_points: Vec<EntryPoint>,
153 pub ports: Vec<Port>,
154 pub environment_variables: Vec<EnvVar>,
155 pub build_scripts: Vec<BuildScript>,
156 pub service_type: ProjectType,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161pub struct EntryPoint {
162 pub file: PathBuf,
163 pub function: Option<String>,
164 pub command: Option<String>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
169pub struct Port {
170 pub number: u16,
171 pub protocol: Protocol,
172 pub description: Option<String>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
176pub enum Protocol {
177 Tcp,
178 Udp,
179 Http,
180 Https,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
185pub struct EnvVar {
186 pub name: String,
187 pub default_value: Option<String>,
188 pub required: bool,
189 pub description: Option<String>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
194pub enum ProjectType {
195 WebApplication,
196 ApiService,
197 CliTool,
198 Library,
199 MobileApp,
200 DesktopApp,
201 Microservice,
202 StaticSite,
203 Hybrid, Unknown,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
209pub struct BuildScript {
210 pub name: String,
211 pub command: String,
212 pub description: Option<String>,
213 pub is_default: bool,
214}
215
216pub type DependencyMap = HashMap<String, String>;
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
221pub enum ArchitectureType {
222 Monolithic,
224 Microservices,
226 Hybrid,
228}
229
230pub type DetectedFramework = DetectedTechnology;
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235pub struct ProjectAnalysis {
236 pub project_root: PathBuf,
237 pub languages: Vec<DetectedLanguage>,
238 pub technologies: Vec<DetectedTechnology>,
240 #[deprecated(note = "Use technologies field instead")]
242 pub frameworks: Vec<DetectedFramework>,
243 pub dependencies: DependencyMap,
244 pub entry_points: Vec<EntryPoint>,
245 pub ports: Vec<Port>,
246 pub environment_variables: Vec<EnvVar>,
247 pub project_type: ProjectType,
248 pub build_scripts: Vec<BuildScript>,
249 pub services: Vec<ServiceAnalysis>,
251 pub architecture_type: ArchitectureType,
253 pub docker_analysis: Option<DockerAnalysis>,
255 pub analysis_metadata: AnalysisMetadata,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260pub struct AnalysisMetadata {
261 pub timestamp: String,
262 pub analyzer_version: String,
263 pub analysis_duration_ms: u64,
264 pub files_analyzed: usize,
265 pub confidence_score: f32,
266}
267
268#[derive(Debug, Clone)]
270pub struct AnalysisConfig {
271 pub include_dev_dependencies: bool,
272 pub deep_analysis: bool,
273 pub ignore_patterns: Vec<String>,
274 pub max_file_size: usize,
275}
276
277impl Default for AnalysisConfig {
278 fn default() -> Self {
279 Self {
280 include_dev_dependencies: false,
281 deep_analysis: true,
282 ignore_patterns: vec![
283 "node_modules".to_string(),
284 ".git".to_string(),
285 "target".to_string(),
286 "build".to_string(),
287 ".next".to_string(),
288 "dist".to_string(),
289 ],
290 max_file_size: 1024 * 1024, }
292 }
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
297pub struct ProjectInfo {
298 pub path: PathBuf,
300 pub name: String,
302 pub project_category: ProjectCategory,
304 pub analysis: ProjectAnalysis,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
310pub enum ProjectCategory {
311 Frontend,
312 Backend,
313 Api,
314 Service,
315 Library,
316 Tool,
317 Documentation,
318 Infrastructure,
319 Unknown,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
324pub struct MonorepoAnalysis {
325 pub root_path: PathBuf,
327 pub is_monorepo: bool,
329 pub projects: Vec<ProjectInfo>,
331 pub metadata: AnalysisMetadata,
333 pub technology_summary: TechnologySummary,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
339pub struct TechnologySummary {
340 pub languages: Vec<String>,
341 pub frameworks: Vec<String>,
342 pub databases: Vec<String>,
343 pub total_projects: usize,
344 pub architecture_pattern: ArchitecturePattern,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
349pub enum ArchitecturePattern {
350 Monolithic,
352 Fullstack,
354 Microservices,
356 ApiFirst,
358 EventDriven,
360 Mixed,
362}
363
364pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
384 analyze_project_with_config(path, &AnalysisConfig::default())
385}
386
387pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
389 let start_time = std::time::Instant::now();
390
391 let project_root = crate::common::file_utils::validate_project_path(path)?;
393
394 log::info!("Starting analysis of project: {}", project_root.display());
395
396 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
398 log::debug!("Found {} files to analyze", files.len());
399
400 let languages = language_detector::detect_languages(&files, config)?;
402 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
403 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
404 let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
405
406 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
408
409 let duration = start_time.elapsed();
410 let confidence = calculate_confidence_score(&languages, &frameworks);
411
412 #[allow(deprecated)]
413 let analysis = ProjectAnalysis {
414 project_root,
415 languages,
416 technologies: frameworks.clone(), frameworks, dependencies,
419 entry_points: context.entry_points,
420 ports: context.ports,
421 environment_variables: context.environment_variables,
422 project_type: context.project_type,
423 build_scripts: context.build_scripts,
424 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
427 analysis_metadata: AnalysisMetadata {
428 timestamp: Utc::now().to_rfc3339(),
429 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
430 analysis_duration_ms: duration.as_millis() as u64,
431 files_analyzed: files.len(),
432 confidence_score: confidence,
433 },
434 };
435
436 log::info!("Analysis completed in {}ms", duration.as_millis());
437 Ok(analysis)
438}
439
440fn calculate_confidence_score(
442 languages: &[DetectedLanguage],
443 frameworks: &[DetectedFramework],
444) -> f32 {
445 if languages.is_empty() {
446 return 0.0;
447 }
448
449 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
450 let framework_confidence: f32 = if frameworks.is_empty() {
451 0.5 } else {
453 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
454 };
455
456 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462
463 #[test]
464 fn test_confidence_calculation() {
465 let languages = vec![
466 DetectedLanguage {
467 name: "Rust".to_string(),
468 version: Some("1.70.0".to_string()),
469 confidence: 0.9,
470 files: vec![],
471 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
472 dev_dependencies: vec!["assert_cmd".to_string()],
473 package_manager: Some("cargo".to_string()),
474 }
475 ];
476
477 let technologies = vec![
478 DetectedTechnology {
479 name: "Actix Web".to_string(),
480 version: Some("4.0".to_string()),
481 category: TechnologyCategory::BackendFramework,
482 confidence: 0.8,
483 requires: vec!["serde".to_string(), "tokio".to_string()],
484 conflicts_with: vec![],
485 is_primary: true,
486 file_indicators: vec![],
487 }
488 ];
489
490 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
493 assert!(score > 0.8);
494 assert!(score <= 1.0);
495 }
496
497 #[test]
498 fn test_empty_analysis() {
499 let languages = vec![];
500 let frameworks = vec![];
501 let score = calculate_confidence_score(&languages, &frameworks);
502 assert_eq!(score, 0.0);
503 }
504}