Skip to content

Commit 9b120bd

Browse files
Alex HolmbergAlex Holmberg
authored andcommitted
feat: improved analyzer from false positives of voltagen and expo issues
1 parent 5601ee1 commit 9b120bd

12 files changed

Lines changed: 569 additions & 320 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,4 @@ path = "examples/check_vulnerabilities.rs"
8686

8787
[[example]]
8888
name = "security_analysis"
89-
path = "examples/security_analysis.rs"
89+
path = "examples/security_analysis.rs"

debug_expo_test.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use syncable_cli::analyzer::{
2+
framework_detector::detect_frameworks,
3+
AnalysisConfig, DetectedLanguage, TechnologyCategory
4+
};
5+
use std::path::Path;
6+
7+
fn main() {
8+
// Test Expo React Native detection that should NOT detect Next.js
9+
let language = DetectedLanguage {
10+
name: "TypeScript".to_string(),
11+
version: Some("4.0.0".to_string()),
12+
confidence: 0.95,
13+
files: vec![
14+
std::path::PathBuf::from("app.json"),
15+
std::path::PathBuf::from("App.tsx"),
16+
std::path::PathBuf::from("android/build.gradle"),
17+
std::path::PathBuf::from("ios/Podfile"),
18+
],
19+
main_dependencies: vec![
20+
"expo".to_string(),
21+
"react-native".to_string(),
22+
"react".to_string(),
23+
"next".to_string(), // This dependency should not cause Next.js to be detected
24+
],
25+
dev_dependencies: vec![],
26+
package_manager: Some("npm".to_string()),
27+
};
28+
29+
let config = AnalysisConfig::default();
30+
let project_root = Path::new(".");
31+
32+
let technologies = detect_frameworks(project_root, &[language], &config).unwrap();
33+
34+
println!("Detected technologies:");
35+
for tech in &technologies {
36+
println!(" - {} (confidence: {}, primary: {})", tech.name, tech.confidence, tech.is_primary);
37+
}
38+
39+
// Should detect Expo as primary, not Next.js
40+
let expo = technologies.iter().find(|t| t.name == "Expo");
41+
let nextjs = technologies.iter().find(|t| t.name == "Next.js");
42+
43+
println!("Expo detected: {:?}", expo.is_some());
44+
println!("Next.js detected: {:?}", nextjs.is_some());
45+
46+
if let Some(expo_tech) = expo {
47+
println!("Expo is primary: {}", expo_tech.is_primary);
48+
}
49+
}

debug_false_positive

1.48 MB
Binary file not shown.

debug_test

1.49 MB
Binary file not shown.

debug_test.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use syncable_cli::analyzer::{
2+
framework_detector::detect_frameworks,
3+
AnalysisConfig, DetectedLanguage
4+
};
5+
use std::path::Path;
6+
7+
fn main() {
8+
// Test Expo React Native detection that should NOT detect Next.js
9+
let language = DetectedLanguage {
10+
name: "TypeScript".to_string(),
11+
version: Some("4.0.0".to_string()),
12+
confidence: 0.95,
13+
files: vec![
14+
std::path::PathBuf::from("app.json"),
15+
std::path::PathBuf::from("App.tsx"),
16+
std::path::PathBuf::from("android/build.gradle"),
17+
std::path::PathBuf::from("ios/Podfile"),
18+
],
19+
main_dependencies: vec![
20+
"expo".to_string(),
21+
"react-native".to_string(),
22+
"react".to_string(),
23+
"next".to_string(), // This dependency should not cause Next.js to be detected
24+
],
25+
dev_dependencies: vec![],
26+
package_manager: Some("npm".to_string()),
27+
};
28+
29+
let config = AnalysisConfig::default();
30+
let project_root = Path::new(".");
31+
32+
match detect_frameworks(project_root, &[language], &config) {
33+
Ok(technologies) => {
34+
println!("Detected technologies:");
35+
for tech in &technologies {
36+
println!(" - {} (confidence: {:.2}, primary: {})", tech.name, tech.confidence, tech.is_primary);
37+
}
38+
39+
// Should detect Expo as primary, not Next.js
40+
let expo = technologies.iter().find(|t| t.name == "Expo");
41+
let nextjs = technologies.iter().find(|t| t.name == "Next.js");
42+
43+
println!("Expo detected: {:?}", expo.is_some());
44+
println!("Next.js detected: {:?}", nextjs.is_some());
45+
46+
if let Some(expo_tech) = expo {
47+
println!("Expo is primary: {}", expo_tech.is_primary);
48+
}
49+
}
50+
Err(e) => {
51+
println!("Error: {}", e);
52+
}
53+
}
54+
}

src/analyzer/frameworks/javascript.rs

Lines changed: 125 additions & 68 deletions
Large diffs are not rendered by default.

src/analyzer/frameworks/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ impl FrameworkDetectionUtils {
8484
let pattern_confidence = matches as f32 / total_patterns as f32;
8585
// Use additive approach instead of multiplicative to avoid extremely low scores
8686
// Base confidence provides a floor, pattern confidence provides the scaling
87-
let final_confidence = (rule.confidence * pattern_confidence + base_confidence * 0.1).min(1.0);
87+
// Cap dependency-based confidence at 0.95 to ensure file-based detection (1.0) takes precedence
88+
let final_confidence = (rule.confidence * pattern_confidence + base_confidence * 0.1).min(0.95);
8889

8990
// Debug logging for Tanstack Start detection
9091
if rule.name.contains("Tanstack") {
@@ -123,7 +124,9 @@ impl FrameworkDetectionUtils {
123124
dependency.contains(&pattern.replace('*', ""))
124125
}
125126
} else {
126-
dependency == pattern || dependency.contains(pattern)
127+
// For dependency detection, use exact matching to avoid false positives
128+
// Only match if the dependency is exactly the pattern or starts with the pattern followed by a version specifier
129+
dependency == pattern || dependency.starts_with(&(pattern.to_string() + "@")) || dependency.starts_with(&(pattern.to_string() + "/"))
127130
}
128131
}
129132

src/analyzer/tool_management/detector.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use log::{debug, info};
99
pub struct ToolStatus {
1010
pub available: bool,
1111
pub path: Option<PathBuf>,
12+
pub execution_path: Option<PathBuf>, // Path to use for execution
1213
pub version: Option<String>,
1314
pub installation_source: InstallationSource,
1415
pub last_checked: SystemTime,
@@ -162,6 +163,7 @@ impl ToolDetector {
162163
let not_found = ToolStatus {
163164
available: false,
164165
path: None,
166+
execution_path: None,
165167
version: None,
166168
installation_source: InstallationSource::NotFound,
167169
last_checked: SystemTime::now(),
@@ -183,6 +185,7 @@ impl ToolDetector {
183185
return ToolStatus {
184186
available: true,
185187
path: Some(path),
188+
execution_path: None, // Execute by name when in PATH
186189
version,
187190
installation_source: InstallationSource::SystemPath,
188191
last_checked: SystemTime::now(),
@@ -204,7 +207,8 @@ impl ToolDetector {
204207
tool_name, tool_path, version, source);
205208
return ToolStatus {
206209
available: true,
207-
path: Some(tool_path),
210+
path: Some(tool_path.clone()),
211+
execution_path: Some(tool_path), // Use full path for execution
208212
version: Some(version),
209213
installation_source: source,
210214
last_checked: SystemTime::now(),
@@ -222,6 +226,7 @@ impl ToolDetector {
222226
return ToolStatus {
223227
available: true,
224228
path: Some(tool_path_exe),
229+
execution_path: Some(tool_path_exe), // Use full path for execution
225230
version,
226231
installation_source: source,
227232
last_checked: SystemTime::now(),
@@ -236,6 +241,7 @@ impl ToolDetector {
236241
ToolStatus {
237242
available: false,
238243
path: None,
244+
execution_path: None,
239245
version: None,
240246
installation_source: InstallationSource::NotFound,
241247
last_checked: SystemTime::now(),

src/analyzer/tool_management/installers/go.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn install_govulncheck(
2323
if success {
2424
info!("✅ govulncheck installed successfully");
2525
installed_tools.insert("govulncheck".to_string(), true);
26-
tool_detector.clear_cache();
26+
tool_detector.clear_cache(); // Clear cache to force fresh detection
2727
info!("💡 Note: Make sure ~/go/bin is in your PATH to use govulncheck");
2828
} else {
2929
warn!("❌ Failed to install govulncheck");

0 commit comments

Comments
 (0)