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 kubelint;
25pub mod language_detector;
26pub mod monorepo;
27pub mod runtime;
28pub mod security;
29pub mod security_analyzer;
30pub mod tool_management;
31pub mod vulnerability;
32
33pub use dependency_parser::{DependencyAnalysis, DependencyInfo, DetailedDependencyMap};
35
36pub use security_analyzer::{
38 ComplianceStatus, SecurityAnalysisConfig, SecurityAnalyzer, SecurityCategory, SecurityFinding,
39 SecurityReport, SecuritySeverity,
40};
41
42pub use security::SecretPatternManager;
44pub use security::config::SecurityConfigPreset;
45
46pub use tool_management::{InstallationSource, ToolDetector, ToolInstaller, ToolStatus};
48
49pub use runtime::{
51 DetectionConfidence, JavaScriptRuntime, PackageManager, RuntimeDetectionResult, RuntimeDetector,
52};
53
54pub use vulnerability::types::VulnerabilitySeverity as VulnSeverity;
56pub use vulnerability::{
57 VulnerabilityChecker, VulnerabilityInfo, VulnerabilityReport, VulnerableDependency,
58};
59
60pub use monorepo::{MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config};
62
63pub use docker_analyzer::{
65 ComposeFileInfo, DockerAnalysis, DockerEnvironment, DockerService, DockerfileInfo,
66 NetworkingConfig, OrchestrationPattern, 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(
390 path: &Path,
391 config: &AnalysisConfig,
392) -> Result<ProjectAnalysis> {
393 let start_time = std::time::Instant::now();
394
395 let project_root = crate::common::file_utils::validate_project_path(path)?;
397
398 log::info!("Starting analysis of project: {}", project_root.display());
399
400 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
402 log::debug!("Found {} files to analyze", files.len());
403
404 let languages = language_detector::detect_languages(&files, config)?;
406 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
407 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
408 let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
409
410 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
412
413 let duration = start_time.elapsed();
414 let confidence = calculate_confidence_score(&languages, &frameworks);
415
416 #[allow(deprecated)]
417 let analysis = ProjectAnalysis {
418 project_root,
419 languages,
420 technologies: frameworks.clone(), frameworks, dependencies,
423 entry_points: context.entry_points,
424 ports: context.ports,
425 environment_variables: context.environment_variables,
426 project_type: context.project_type,
427 build_scripts: context.build_scripts,
428 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
431 analysis_metadata: AnalysisMetadata {
432 timestamp: Utc::now().to_rfc3339(),
433 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
434 analysis_duration_ms: duration.as_millis() as u64,
435 files_analyzed: files.len(),
436 confidence_score: confidence,
437 },
438 };
439
440 log::info!("Analysis completed in {}ms", duration.as_millis());
441 Ok(analysis)
442}
443
444fn calculate_confidence_score(
446 languages: &[DetectedLanguage],
447 frameworks: &[DetectedFramework],
448) -> f32 {
449 if languages.is_empty() {
450 return 0.0;
451 }
452
453 let lang_confidence: f32 =
454 languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
455 let framework_confidence: f32 = if frameworks.is_empty() {
456 0.5 } else {
458 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
459 };
460
461 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467
468 #[test]
469 fn test_confidence_calculation() {
470 let languages = vec![DetectedLanguage {
471 name: "Rust".to_string(),
472 version: Some("1.70.0".to_string()),
473 confidence: 0.9,
474 files: vec![],
475 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
476 dev_dependencies: vec!["assert_cmd".to_string()],
477 package_manager: Some("cargo".to_string()),
478 }];
479
480 let technologies = vec![DetectedTechnology {
481 name: "Actix Web".to_string(),
482 version: Some("4.0".to_string()),
483 category: TechnologyCategory::BackendFramework,
484 confidence: 0.8,
485 requires: vec!["serde".to_string(), "tokio".to_string()],
486 conflicts_with: vec![],
487 is_primary: true,
488 file_indicators: vec![],
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}