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 findings.push(Finding {
35 smell_name: "unsafe_api".into(),
36 category: SmellCategory::Security,
37 severity: Severity::Warning,
38 location: Location {
39 path: ctx.file.path.clone(),
40 start_line: i + 1,
41 end_line: i + 1,
42 name: None,
43 },
44 message: format!("Potentially dangerous: `{pat}` — {msg}"),
45 suggested_refactorings: vec!["Use a safe alternative".into()],
46 ..Default::default()
47 });
48 break; }
50 }
51 }
52 findings
53 }
54}
55
56fn is_in_string(line: &str, pat: &str) -> bool {
58 if let Some(pos) = line.find(pat) {
59 let before = &line[..pos];
60 let quotes = before.matches('"').count();
61 quotes % 2 == 1 } else {
63 false
64 }
65}
66
67fn patterns_for(lang: &str) -> Vec<(&'static str, &'static str)> {
69 match lang {
70 "rust" => vec![("unsafe ", "unsafe block/fn — review for memory safety")],
71 "python" => vec![
72 ("eval(", "eval() executes arbitrary code"),
73 ("exec(", "exec() executes arbitrary code"),
74 ("os.system(", "os.system() is vulnerable to shell injection"),
75 ("subprocess.call(", "prefer subprocess.run with shell=False"),
76 (
77 "pickle.load",
78 "pickle deserialization can execute arbitrary code",
79 ),
80 ],
81 "typescript" | "javascript" => vec![
82 ("eval(", "eval() executes arbitrary code"),
83 ("innerHTML", "innerHTML can lead to XSS"),
84 (
85 "dangerouslySetInnerHTML",
86 "React escape hatch — review for XSS",
87 ),
88 ("document.write(", "document.write can lead to XSS"),
89 ],
90 "c" | "cpp" => vec![
91 ("gets(", "gets() has no bounds checking — use fgets()"),
92 (
93 "sprintf(",
94 "sprintf() has no bounds checking — use snprintf()",
95 ),
96 ("strcpy(", "strcpy() has no bounds checking — use strncpy()"),
97 ("strcat(", "strcat() has no bounds checking — use strncat()"),
98 ("system(", "system() is vulnerable to shell injection"),
99 ],
100 "go" => vec![
101 ("exec.Command(", "review for command injection"),
102 ("template.HTML(", "bypasses HTML escaping — review for XSS"),
103 ],
104 _ => vec![],
105 }
106}