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 ModularSecurityAnalyzer, JavaScriptSecurityAnalyzer,
43 SecretPatternManager
44};
45pub use security::config::SecurityConfigPreset;
46
47pub use monorepo_detector::{
49 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
50};
51
52pub use docker_analyzer::{
54 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
55 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
56 analyze_docker_infrastructure
57};
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct DetectedLanguage {
62 pub name: String,
63 pub version: Option<String>,
64 pub confidence: f32,
65 pub files: Vec<PathBuf>,
66 pub main_dependencies: Vec<String>,
67 pub dev_dependencies: Vec<String>,
68 pub package_manager: Option<String>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
73pub enum TechnologyCategory {
74 MetaFramework,
76 FrontendFramework,
78 BackendFramework,
80 Library(LibraryType),
82 BuildTool,
84 Database,
86 Testing,
88 Runtime,
90 PackageManager,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
96pub enum LibraryType {
97 UI,
99 StateManagement,
101 DataFetching,
103 Routing,
105 Styling,
107 Utility,
109 HttpClient,
111 Authentication,
113 CLI,
115 Other(String),
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121pub struct DetectedTechnology {
122 pub name: String,
123 pub version: Option<String>,
124 pub category: TechnologyCategory,
125 pub confidence: f32,
126 pub requires: Vec<String>,
128 pub conflicts_with: Vec<String>,
130 pub is_primary: bool,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
136pub struct ServiceAnalysis {
137 pub name: String,
138 pub path: PathBuf,
139 pub languages: Vec<DetectedLanguage>,
140 pub technologies: Vec<DetectedTechnology>,
141 pub entry_points: Vec<EntryPoint>,
142 pub ports: Vec<Port>,
143 pub environment_variables: Vec<EnvVar>,
144 pub build_scripts: Vec<BuildScript>,
145 pub service_type: ProjectType,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150pub struct EntryPoint {
151 pub file: PathBuf,
152 pub function: Option<String>,
153 pub command: Option<String>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
158pub struct Port {
159 pub number: u16,
160 pub protocol: Protocol,
161 pub description: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
165pub enum Protocol {
166 Tcp,
167 Udp,
168 Http,
169 Https,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
174pub struct EnvVar {
175 pub name: String,
176 pub default_value: Option<String>,
177 pub required: bool,
178 pub description: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
183pub enum ProjectType {
184 WebApplication,
185 ApiService,
186 CliTool,
187 Library,
188 MobileApp,
189 DesktopApp,
190 Microservice,
191 StaticSite,
192 Hybrid, Unknown,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
198pub struct BuildScript {
199 pub name: String,
200 pub command: String,
201 pub description: Option<String>,
202 pub is_default: bool,
203}
204
205pub type DependencyMap = HashMap<String, String>;
207
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
210pub enum ArchitectureType {
211 Monolithic,
213 Microservices,
215 Hybrid,
217}
218
219pub type DetectedFramework = DetectedTechnology;
221
222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
224pub struct ProjectAnalysis {
225 pub project_root: PathBuf,
226 pub languages: Vec<DetectedLanguage>,
227 pub technologies: Vec<DetectedTechnology>,
229 #[deprecated(note = "Use technologies field instead")]
231 pub frameworks: Vec<DetectedFramework>,
232 pub dependencies: DependencyMap,
233 pub entry_points: Vec<EntryPoint>,
234 pub ports: Vec<Port>,
235 pub environment_variables: Vec<EnvVar>,
236 pub project_type: ProjectType,
237 pub build_scripts: Vec<BuildScript>,
238 pub services: Vec<ServiceAnalysis>,
240 pub architecture_type: ArchitectureType,
242 pub docker_analysis: Option<DockerAnalysis>,
244 pub analysis_metadata: AnalysisMetadata,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
249pub struct AnalysisMetadata {
250 pub timestamp: String,
251 pub analyzer_version: String,
252 pub analysis_duration_ms: u64,
253 pub files_analyzed: usize,
254 pub confidence_score: f32,
255}
256
257#[derive(Debug, Clone)]
259pub struct AnalysisConfig {
260 pub include_dev_dependencies: bool,
261 pub deep_analysis: bool,
262 pub ignore_patterns: Vec<String>,
263 pub max_file_size: usize,
264}
265
266impl Default for AnalysisConfig {
267 fn default() -> Self {
268 Self {
269 include_dev_dependencies: false,
270 deep_analysis: true,
271 ignore_patterns: vec![
272 "node_modules".to_string(),
273 ".git".to_string(),
274 "target".to_string(),
275 "build".to_string(),
276 ".next".to_string(),
277 "dist".to_string(),
278 ],
279 max_file_size: 1024 * 1024, }
281 }
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
286pub struct ProjectInfo {
287 pub path: PathBuf,
289 pub name: String,
291 pub project_category: ProjectCategory,
293 pub analysis: ProjectAnalysis,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
299pub enum ProjectCategory {
300 Frontend,
301 Backend,
302 Api,
303 Service,
304 Library,
305 Tool,
306 Documentation,
307 Infrastructure,
308 Unknown,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
313pub struct MonorepoAnalysis {
314 pub root_path: PathBuf,
316 pub is_monorepo: bool,
318 pub projects: Vec<ProjectInfo>,
320 pub metadata: AnalysisMetadata,
322 pub technology_summary: TechnologySummary,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
328pub struct TechnologySummary {
329 pub languages: Vec<String>,
330 pub frameworks: Vec<String>,
331 pub databases: Vec<String>,
332 pub total_projects: usize,
333 pub architecture_pattern: ArchitecturePattern,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
338pub enum ArchitecturePattern {
339 Monolithic,
341 Fullstack,
343 Microservices,
345 ApiFirst,
347 EventDriven,
349 Mixed,
351}
352
353pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
373 analyze_project_with_config(path, &AnalysisConfig::default())
374}
375
376pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
378 let start_time = std::time::Instant::now();
379
380 let project_root = crate::common::file_utils::validate_project_path(path)?;
382
383 log::info!("Starting analysis of project: {}", project_root.display());
384
385 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
387 log::debug!("Found {} files to analyze", files.len());
388
389 let languages = language_detector::detect_languages(&files, config)?;
391 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
392 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
393 let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
394
395 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
397
398 let duration = start_time.elapsed();
399 let confidence = calculate_confidence_score(&languages, &frameworks);
400
401 #[allow(deprecated)]
402 let analysis = ProjectAnalysis {
403 project_root,
404 languages,
405 technologies: frameworks.clone(), frameworks, dependencies,
408 entry_points: context.entry_points,
409 ports: context.ports,
410 environment_variables: context.environment_variables,
411 project_type: context.project_type,
412 build_scripts: context.build_scripts,
413 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
416 analysis_metadata: AnalysisMetadata {
417 timestamp: Utc::now().to_rfc3339(),
418 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
419 analysis_duration_ms: duration.as_millis() as u64,
420 files_analyzed: files.len(),
421 confidence_score: confidence,
422 },
423 };
424
425 log::info!("Analysis completed in {}ms", duration.as_millis());
426 Ok(analysis)
427}
428
429fn calculate_confidence_score(
431 languages: &[DetectedLanguage],
432 frameworks: &[DetectedFramework],
433) -> f32 {
434 if languages.is_empty() {
435 return 0.0;
436 }
437
438 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
439 let framework_confidence: f32 = if frameworks.is_empty() {
440 0.5 } else {
442 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
443 };
444
445 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451
452 #[test]
453 fn test_confidence_calculation() {
454 let languages = vec![
455 DetectedLanguage {
456 name: "Rust".to_string(),
457 version: Some("1.70.0".to_string()),
458 confidence: 0.9,
459 files: vec![],
460 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
461 dev_dependencies: vec!["assert_cmd".to_string()],
462 package_manager: Some("cargo".to_string()),
463 }
464 ];
465
466 let technologies = vec![
467 DetectedTechnology {
468 name: "Actix Web".to_string(),
469 version: Some("4.0".to_string()),
470 category: TechnologyCategory::BackendFramework,
471 confidence: 0.8,
472 requires: vec!["serde".to_string(), "tokio".to_string()],
473 conflicts_with: vec![],
474 is_primary: true,
475 }
476 ];
477
478 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
481 assert!(score > 0.8);
482 assert!(score <= 1.0);
483 }
484
485 #[test]
486 fn test_empty_analysis() {
487 let languages = vec![];
488 let frameworks = vec![];
489 let score = calculate_confidence_score(&languages, &frameworks);
490 assert_eq!(score, 0.0);
491 }
492}