#![allow(unused_imports)]
mod base;
mod engine;
mod circular_dependency;
mod god_class;
mod long_parameter;
mod data_clumps;
mod dead_code;
mod feature_envy;
mod inappropriate_intimacy;
mod lazy_class;
mod message_chain;
mod middle_man;
mod refused_bequest;
mod ai_boilerplate;
mod ai_churn;
mod ai_complexity_spike;
mod ai_duplicate_block;
mod ai_missing_tests;
mod ai_naming_pattern;
mod architectural_bottleneck;
mod core_utility;
mod degree_centrality;
mod influential_code;
mod module_cohesion;
mod shotgun_surgery;
mod eval_detector;
mod pickle_detector;
mod sql_injection;
mod taint_detector;
mod unsafe_template;
mod generator_misuse;
mod infinite_loop;
mod unused_imports;
mod health_delta;
mod incremental_cache;
mod query_cache;
mod risk_analyzer;
mod root_cause_analyzer;
mod voting_engine;
pub mod external_tool;
mod bandit;
mod eslint;
mod gh_actions;
mod mypy;
mod npm_audit;
mod radon;
mod ruff;
mod semgrep;
mod tsc;
mod vulture;
pub use base::{
DetectionSummary,
Detector,
DetectorConfig,
DetectorResult,
ProgressCallback,
};
pub use engine::{
DetectorEngine,
DetectorEngineBuilder,
};
pub use circular_dependency::CircularDependencyDetector;
pub use god_class::{GodClassDetector, GodClassThresholds};
pub use long_parameter::{LongParameterListDetector, LongParameterThresholds};
pub use data_clumps::DataClumpsDetector;
pub use dead_code::DeadCodeDetector;
pub use feature_envy::FeatureEnvyDetector;
pub use inappropriate_intimacy::InappropriateIntimacyDetector;
pub use lazy_class::LazyClassDetector;
pub use message_chain::MessageChainDetector;
pub use middle_man::MiddleManDetector;
pub use refused_bequest::RefusedBequestDetector;
pub use ai_boilerplate::AIBoilerplateDetector;
pub use ai_churn::AIChurnDetector;
pub use ai_complexity_spike::AIComplexitySpikeDetector;
pub use ai_duplicate_block::AIDuplicateBlockDetector;
pub use ai_missing_tests::AIMissingTestsDetector;
pub use ai_naming_pattern::AINamingPatternDetector;
pub use architectural_bottleneck::ArchitecturalBottleneckDetector;
pub use core_utility::CoreUtilityDetector;
pub use degree_centrality::DegreeCentralityDetector;
pub use influential_code::InfluentialCodeDetector;
pub use module_cohesion::ModuleCohesionDetector;
pub use shotgun_surgery::ShotgunSurgeryDetector;
pub use eval_detector::EvalDetector;
pub use pickle_detector::PickleDeserializationDetector;
pub use sql_injection::SQLInjectionDetector;
pub use taint_detector::TaintDetector;
pub use unsafe_template::UnsafeTemplateDetector;
pub use generator_misuse::GeneratorMisuseDetector;
pub use infinite_loop::InfiniteLoopDetector;
pub use unused_imports::UnusedImportsDetector;
pub use health_delta::{
estimate_batch_fix_impact, estimate_fix_impact, BatchHealthScoreDelta, HealthScoreDelta,
HealthScoreDeltaCalculator, ImpactLevel, MetricsBreakdown,
};
pub use incremental_cache::{CacheStats, IncrementalCache};
pub use query_cache::{ClassData, FileData, FunctionData, QueryCache};
pub use risk_analyzer::{
analyze_compound_risks, RiskAnalyzer, RiskAssessment, RiskFactor,
};
pub use root_cause_analyzer::{RootCauseAnalysis, RootCauseAnalyzer, RootCauseSummary};
pub use voting_engine::{
ConfidenceMethod, ConsensusResult, DetectorWeight, SeverityResolution, VotingEngine,
VotingStats, VotingStrategy,
};
pub use bandit::BanditDetector;
pub use eslint::ESLintDetector;
pub use gh_actions::GHActionsInjectionDetector;
pub use mypy::MypyDetector;
pub use npm_audit::NpmAuditDetector;
pub use radon::RadonDetector;
pub use ruff::{RuffImportDetector, RuffLintDetector};
pub use semgrep::SemgrepDetector;
pub use tsc::TscDetector;
pub use vulture::VultureDetector;
pub use external_tool::{
ExternalToolResult,
GraphContext,
JsRuntime,
batch_get_graph_context,
get_graph_context,
get_js_exec_command,
get_js_runtime,
is_python_tool_installed,
is_tool_installed,
run_external_tool,
run_js_tool,
};
use std::path::Path;
use std::sync::Arc;
pub fn default_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
vec![
Arc::new(CircularDependencyDetector::new()),
Arc::new(GodClassDetector::new()),
Arc::new(LongParameterListDetector::new()),
Arc::new(DataClumpsDetector::new()),
Arc::new(DeadCodeDetector::new()),
Arc::new(FeatureEnvyDetector::new()),
Arc::new(InappropriateIntimacyDetector::new()),
Arc::new(LazyClassDetector::new()),
Arc::new(MessageChainDetector::new()),
Arc::new(MiddleManDetector::new()),
Arc::new(RefusedBequestDetector::new()),
Arc::new(AIBoilerplateDetector::new()),
Arc::new(AIChurnDetector::new()),
Arc::new(AIComplexitySpikeDetector::new()),
Arc::new(AIDuplicateBlockDetector::new()),
Arc::new(AIMissingTestsDetector::new()),
Arc::new(AINamingPatternDetector::new()),
Arc::new(ArchitecturalBottleneckDetector::new()),
Arc::new(CoreUtilityDetector::new()),
Arc::new(DegreeCentralityDetector::new()),
Arc::new(InfluentialCodeDetector::new()),
Arc::new(ModuleCohesionDetector::new()),
Arc::new(ShotgunSurgeryDetector::new()),
Arc::new(EvalDetector::with_repository_path(repository_path.to_path_buf())),
Arc::new(PickleDeserializationDetector::with_repository_path(repository_path.to_path_buf())),
Arc::new(SQLInjectionDetector::with_repository_path(repository_path.to_path_buf())),
Arc::new(TaintDetector::with_repository_path(repository_path.to_path_buf())),
Arc::new(UnsafeTemplateDetector::new()),
Arc::new(GeneratorMisuseDetector::new()),
Arc::new(InfiniteLoopDetector::new()),
Arc::new(UnusedImportsDetector::new()),
]
}
pub fn python_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
vec![
Arc::new(BanditDetector::new(repository_path)),
Arc::new(RuffLintDetector::new(repository_path)),
Arc::new(RuffImportDetector::new(repository_path)),
Arc::new(MypyDetector::new(repository_path)),
Arc::new(RadonDetector::new(repository_path)),
Arc::new(VultureDetector::new(repository_path)),
]
}
pub fn javascript_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
vec![
Arc::new(ESLintDetector::new(repository_path)),
Arc::new(TscDetector::new(repository_path)),
Arc::new(NpmAuditDetector::new(repository_path)),
]
}
pub fn security_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
vec![
Arc::new(BanditDetector::new(repository_path)),
Arc::new(SemgrepDetector::new(repository_path)),
Arc::new(NpmAuditDetector::new(repository_path)),
Arc::new(GHActionsInjectionDetector::new(repository_path)),
]
}
pub fn all_external_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
vec![
Arc::new(BanditDetector::new(repository_path)),
Arc::new(RuffLintDetector::new(repository_path)),
Arc::new(RuffImportDetector::new(repository_path)),
Arc::new(MypyDetector::new(repository_path)),
Arc::new(RadonDetector::new(repository_path)),
Arc::new(VultureDetector::new(repository_path)),
Arc::new(ESLintDetector::new(repository_path)),
Arc::new(TscDetector::new(repository_path)),
Arc::new(NpmAuditDetector::new(repository_path)),
Arc::new(SemgrepDetector::new(repository_path)),
Arc::new(GHActionsInjectionDetector::new(repository_path)),
]
}
pub fn create_default_engine(workers: usize, repository_path: &Path) -> DetectorEngine {
DetectorEngineBuilder::new()
.workers(workers)
.detectors(default_detectors(repository_path))
.build()
}
pub fn create_full_engine(workers: usize, repository_path: &Path) -> DetectorEngine {
let mut detectors = default_detectors(repository_path);
detectors.extend(all_external_detectors(repository_path));
DetectorEngineBuilder::new()
.workers(workers)
.detectors(detectors)
.build()
}
pub fn walk_source_files<'a>(
repository_path: &'a Path,
extensions: Option<&'a [&'a str]>,
) -> impl Iterator<Item = std::path::PathBuf> + 'a {
use ignore::WalkBuilder;
let mut builder = WalkBuilder::new(repository_path);
builder
.hidden(true) .git_ignore(true) .git_global(true) .git_exclude(true) .require_git(false) .add_custom_ignore_filename(".repotoireignore");
builder.build().filter_map(move |entry| {
let entry = entry.ok()?;
let path = entry.path();
if !path.is_file() {
return None;
}
if let Some(exts) = extensions {
let ext = path.extension()?.to_str()?;
if !exts.contains(&ext) {
return None;
}
}
Some(path.to_path_buf())
})
}
pub fn is_line_suppressed(line: &str, prev_line: Option<&str>) -> bool {
let suppression_pattern = "repotoire: ignore";
let suppression_pattern_alt = "repotoire:ignore";
let line_lower = line.to_lowercase();
if line_lower.contains(suppression_pattern) || line_lower.contains(suppression_pattern_alt) {
return true;
}
if let Some(prev) = prev_line {
let prev_lower = prev.trim().to_lowercase();
if (prev_lower.starts_with('#') || prev_lower.starts_with("//") ||
prev_lower.starts_with("--") || prev_lower.starts_with("/*")) &&
(prev_lower.contains(suppression_pattern) || prev_lower.contains(suppression_pattern_alt)) {
return true;
}
}
false
}