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;
28pub mod hadolint;
29
30pub use dependency_parser::{
32 DependencyInfo, DependencyAnalysis, DetailedDependencyMap
33};
34
35pub use security_analyzer::{
37 SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
38 SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
39};
40
41pub use security::{
43 SecretPatternManager
44};
45pub use security::config::SecurityConfigPreset;
46
47pub use tool_management::{ToolInstaller, ToolDetector, ToolStatus, InstallationSource};
49
50pub use runtime::{RuntimeDetector, JavaScriptRuntime, PackageManager, RuntimeDetectionResult, DetectionConfidence};
52
53pub use vulnerability::{VulnerabilityChecker, VulnerabilityInfo, VulnerabilityReport, VulnerableDependency};
55pub use vulnerability::types::VulnerabilitySeverity as VulnSeverity;
56
57pub use monorepo::{
59 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
60};
61
62pub use docker_analyzer::{
64 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
65 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
66 analyze_docker_infrastructure
67};
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71pub struct DetectedLanguage {
72 pub name: String,
73 pub version: Option<String>,
74 pub confidence: f32,
75 pub files: Vec<PathBuf>,
76 pub main_dependencies: Vec<String>,
77 pub dev_dependencies: Vec<String>,
78 pub package_manager: Option<String>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
83pub enum TechnologyCategory {
84 MetaFramework,
86 FrontendFramework,
88 BackendFramework,
90 Library(LibraryType),
92 BuildTool,
94 Database,
96 Testing,
98 Runtime,
100 PackageManager,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
106pub enum LibraryType {
107 UI,
109 StateManagement,
111 DataFetching,
113 Routing,
115 Styling,
117 Utility,
119 HttpClient,
121 Authentication,
123 CLI,
125 Other(String),
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131pub struct DetectedTechnology {
132 pub name: String,
133 pub version: Option<String>,
134 pub category: TechnologyCategory,
135 pub confidence: f32,
136 pub requires: Vec<String>,
138 pub conflicts_with: Vec<String>,
140 pub is_primary: bool,
142 pub file_indicators: Vec<String>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
148pub struct ServiceAnalysis {
149 pub name: String,
150 pub path: PathBuf,
151 pub languages: Vec<DetectedLanguage>,
152 pub technologies: Vec<DetectedTechnology>,
153 pub entry_points: Vec<EntryPoint>,
154 pub ports: Vec<Port>,
155 pub environment_variables: Vec<EnvVar>,
156 pub build_scripts: Vec<BuildScript>,
157 pub service_type: ProjectType,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
162pub struct EntryPoint {
163 pub file: PathBuf,
164 pub function: Option<String>,
165 pub command: Option<String>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
170pub struct Port {
171 pub number: u16,
172 pub protocol: Protocol,
173 pub description: Option<String>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
177pub enum Protocol {
178 Tcp,
179 Udp,
180 Http,
181 Https,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
186pub struct EnvVar {
187 pub name: String,
188 pub default_value: Option<String>,
189 pub required: bool,
190 pub description: Option<String>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
195pub enum ProjectType {
196 WebApplication,
197 ApiService,
198 CliTool,
199 Library,
200 MobileApp,
201 DesktopApp,
202 Microservice,
203 StaticSite,
204 Hybrid, Unknown,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
210pub struct BuildScript {
211 pub name: String,
212 pub command: String,
213 pub description: Option<String>,
214 pub is_default: bool,
215}
216
217pub type DependencyMap = HashMap<String, String>;
219
220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
222pub enum ArchitectureType {
223 Monolithic,
225 Microservices,
227 Hybrid,
229}
230
231pub type DetectedFramework = DetectedTechnology;
233
234#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
236pub struct ProjectAnalysis {
237 pub project_root: PathBuf,
238 pub languages: Vec<DetectedLanguage>,
239 pub technologies: Vec<DetectedTechnology>,
241 #[deprecated(note = "Use technologies field instead")]
243 pub frameworks: Vec<DetectedFramework>,
244 pub dependencies: DependencyMap,
245 pub entry_points: Vec<EntryPoint>,
246 pub ports: Vec<Port>,
247 pub environment_variables: Vec<EnvVar>,
248 pub project_type: ProjectType,
249 pub build_scripts: Vec<BuildScript>,
250 pub services: Vec<ServiceAnalysis>,
252 pub architecture_type: ArchitectureType,
254 pub docker_analysis: Option<DockerAnalysis>,
256 pub analysis_metadata: AnalysisMetadata,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
261pub struct AnalysisMetadata {
262 pub timestamp: String,
263 pub analyzer_version: String,
264 pub analysis_duration_ms: u64,
265 pub files_analyzed: usize,
266 pub confidence_score: f32,
267}
268
269#[derive(Debug, Clone)]
271pub struct AnalysisConfig {
272 pub include_dev_dependencies: bool,
273 pub deep_analysis: bool,
274 pub ignore_patterns: Vec<String>,
275 pub max_file_size: usize,
276}
277
278impl Default for AnalysisConfig {
279 fn default() -> Self {
280 Self {
281 include_dev_dependencies: false,
282 deep_analysis: true,
283 ignore_patterns: vec![
284 "node_modules".to_string(),
285 ".git".to_string(),
286 "target".to_string(),
287 "build".to_string(),
288 ".next".to_string(),
289 "dist".to_string(),
290 ],
291 max_file_size: 1024 * 1024, }
293 }
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
298pub struct ProjectInfo {
299 pub path: PathBuf,
301 pub name: String,
303 pub project_category: ProjectCategory,
305 pub analysis: ProjectAnalysis,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
311pub enum ProjectCategory {
312 Frontend,
313 Backend,
314 Api,
315 Service,
316 Library,
317 Tool,
318 Documentation,
319 Infrastructure,
320 Unknown,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
325pub struct MonorepoAnalysis {
326 pub root_path: PathBuf,
328 pub is_monorepo: bool,
330 pub projects: Vec<ProjectInfo>,
332 pub metadata: AnalysisMetadata,
334 pub technology_summary: TechnologySummary,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
340pub struct TechnologySummary {
341 pub languages: Vec<String>,
342 pub frameworks: Vec<String>,
343 pub databases: Vec<String>,
344 pub total_projects: usize,
345 pub architecture_pattern: ArchitecturePattern,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
350pub enum ArchitecturePattern {
351 Monolithic,
353 Fullstack,
355 Microservices,
357 ApiFirst,
359 EventDriven,
361 Mixed,
363}
364
365pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
385 analyze_project_with_config(path, &AnalysisConfig::default())
386}
387
388pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
390 let start_time = std::time::Instant::now();
391
392 let project_root = crate::common::file_utils::validate_project_path(path)?;
394
395 log::info!("Starting analysis of project: {}", project_root.display());
396
397 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
399 log::debug!("Found {} files to analyze", files.len());
400
401 let languages = language_detector::detect_languages(&files, config)?;
403 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
404 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
405 let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
406
407 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
409
410 let duration = start_time.elapsed();
411 let confidence = calculate_confidence_score(&languages, &frameworks);
412
413 #[allow(deprecated)]
414 let analysis = ProjectAnalysis {
415 project_root,
416 languages,
417 technologies: frameworks.clone(), frameworks, dependencies,
420 entry_points: context.entry_points,
421 ports: context.ports,
422 environment_variables: context.environment_variables,
423 project_type: context.project_type,
424 build_scripts: context.build_scripts,
425 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
428 analysis_metadata: AnalysisMetadata {
429 timestamp: Utc::now().to_rfc3339(),
430 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
431 analysis_duration_ms: duration.as_millis() as u64,
432 files_analyzed: files.len(),
433 confidence_score: confidence,
434 },
435 };
436
437 log::info!("Analysis completed in {}ms", duration.as_millis());
438 Ok(analysis)
439}
440
441fn calculate_confidence_score(
443 languages: &[DetectedLanguage],
444 frameworks: &[DetectedFramework],
445) -> f32 {
446 if languages.is_empty() {
447 return 0.0;
448 }
449
450 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
451 let framework_confidence: f32 = if frameworks.is_empty() {
452 0.5 } else {
454 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
455 };
456
457 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
465 fn test_confidence_calculation() {
466 let languages = vec![
467 DetectedLanguage {
468 name: "Rust".to_string(),
469 version: Some("1.70.0".to_string()),
470 confidence: 0.9,
471 files: vec![],
472 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
473 dev_dependencies: vec!["assert_cmd".to_string()],
474 package_manager: Some("cargo".to_string()),
475 }
476 ];
477
478 let technologies = vec![
479 DetectedTechnology {
480 name: "Actix Web".to_string(),
481 version: Some("4.0".to_string()),
482 category: TechnologyCategory::BackendFramework,
483 confidence: 0.8,
484 requires: vec!["serde".to_string(), "tokio".to_string()],
485 conflicts_with: vec![],
486 is_primary: true,
487 file_indicators: vec![],
488 }
489 ];
490
491 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
494 assert!(score > 0.8);
495 assert!(score <= 1.0);
496 }
497
498 #[test]
499 fn test_empty_analysis() {
500 let languages = vec![];
501 let frameworks = vec![];
502 let score = calculate_confidence_score(&languages, &frameworks);
503 assert_eq!(score, 0.0);
504 }
505}