cha_core/plugins/
unsafe_api.rs1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3pub struct UnsafeApiAnalyzer;
10
11impl Plugin for UnsafeApiAnalyzer {
12 fn name(&self) -> &str {
13 "unsafe_api"
14 }
15
16 fn description(&self) -> &str {
17 "Dangerous function calls (eval/exec/system)"
18 }
19
20 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
21 let lang = &ctx.model.language;
22 let patterns = patterns_for(lang);
23 if patterns.is_empty() {
24 return vec![];
25 }
26 let mut findings = Vec::new();
27 for (i, line) in ctx.file.content.lines().enumerate() {
28 let trimmed = line.trim();
29 if trimmed.starts_with("//") || trimmed.starts_with('#') || trimmed.starts_with("/*") {
30 continue;
31 }
32 for &(pat, msg) in &patterns {
33 if line.contains(pat) && !is_in_string(line, pat) {
34 let col = line.find(pat).unwrap_or(0);
35 findings.push(Finding {
36 smell_name: "unsafe_api".into(),
37 category: SmellCategory::Security,
38 severity: Severity::Warning,
39 location: Location {
40 path: ctx.file.path.clone(),
41 start_line: i + 1,
42 start_col: col,
43 end_line: i + 1,
44 end_col: col + pat.len(),
45 name: None,
46 },
47 message: format!("Potentially dangerous: `{pat}` — {msg}"),
48 suggested_refactorings: vec!["Use a safe alternative".into()],
49 ..Default::default()
50 });
51 break; }
53 }
54 }
55 findings
56 }
57}
58
59fn is_in_string(line: &str, pat: &str) -> bool {
61 if let Some(pos) = line.find(pat) {
62 let before = &line[..pos];
63 let quotes = before.matches('"').count();
64 quotes % 2 == 1 } else {
66 false
67 }
68}
69
70fn patterns_for(lang: &str) -> Vec<(&'static str, &'static str)> {
72 match lang {
73 "rust" => vec![("unsafe ", "unsafe block/fn — review for memory safety")],
74 "python" => vec![
75 ("eval(", "eval() executes arbitrary code"),
76 ("exec(", "exec() executes arbitrary code"),
77 ("os.system(", "os.system() is vulnerable to shell injection"),
78 ("subprocess.call(", "prefer subprocess.run with shell=False"),
79 (
80 "pickle.load",
81 "pickle deserialization can execute arbitrary code",
82 ),
83 ],
84 "typescript" | "javascript" => vec![
85 ("eval(", "eval() executes arbitrary code"),
86 ("innerHTML", "innerHTML can lead to XSS"),
87 (
88 "dangerouslySetInnerHTML",
89 "React escape hatch — review for XSS",
90 ),
91 ("document.write(", "document.write can lead to XSS"),
92 ],
93 "c" | "cpp" => vec![
94 ("gets(", "gets() has no bounds checking — use fgets()"),
95 (
96 "sprintf(",
97 "sprintf() has no bounds checking — use snprintf()",
98 ),
99 ("strcpy(", "strcpy() has no bounds checking — use strncpy()"),
100 ("strcat(", "strcat() has no bounds checking — use strncat()"),
101 ("system(", "system() is vulnerable to shell injection"),
102 ],
103 "go" => vec![
104 ("exec.Command(", "review for command injection"),
105 ("template.HTML(", "bypasses HTML escaping — review for XSS"),
106 ],
107 _ => vec![],
108 }
109}