Skip to main content

cha_core/plugins/
unsafe_api.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect usage of potentially dangerous functions and constructs.
4///
5/// ## References
6///
7/// [1] CWE-676: Use of Potentially Dangerous Function.
8///     https://cwe.mitre.org/data/definitions/676.html
9pub 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; // one finding per line
49                }
50            }
51        }
52        findings
53    }
54}
55
56/// Heuristic: pattern is likely inside a string literal if preceded by a quote.
57fn 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 // odd number of quotes = inside string
62    } else {
63        false
64    }
65}
66
67// cha:ignore unsafe_api
68fn 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}