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