use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils;
use lang_parsing_substrate::query;
use tree_sitter::Node;
const OBSOLESCENT_FUNCS: &[(&str, &str)] = &[
("gets", "gets_s() (or fgets())"),
("strcpy", "strcpy_s()"),
("strcat", "strcat_s()"),
("sprintf", "sprintf_s() (or snprintf())"),
("vsprintf", "vsprintf_s() (or vsnprintf())"),
("scanf", "scanf_s()"),
("fscanf", "fscanf_s()"),
("strtok", "strtok_s()"),
("asctime", "asctime_s()"),
("ctime", "ctime_s()"),
("rewind", "fseek()"),
("setbuf", "setvbuf()"),
];
#[derive(Debug)]
pub struct Msc24C;
impl Msc24C {
#[allow(dead_code)]
pub fn new() -> Self {
Msc24C
}
fn traverse(&self, root: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
for call in query::find_descendants_of_kind(*root, "call_expression") {
let Some(func) = call.child_by_field_name("function") else {
continue;
};
if func.kind() != "identifier" {
continue;
}
let func_name = ast_utils::get_node_text(&func, source);
let Some((_, replacement)) = OBSOLESCENT_FUNCS
.iter()
.find(|(name, _)| *name == func_name)
else {
continue;
};
let pos = call.start_position();
violations.push(RuleViolation {
rule_id: "MSC24-C".to_string(),
severity: Severity::Medium,
line: pos.row + 1,
column: pos.column + 1,
message: format!(
"'{}' is deprecated/obsolescent -- prefer {}",
func_name, replacement
),
file_path: String::new(),
suggestion: Some(format!("Replace '{}' with {}", func_name, replacement)),
requires_manual_review: Some(false),
});
}
}
}
impl CertRule for Msc24C {
fn rule_id(&self) -> &'static str {
"MSC24-C"
}
fn description(&self) -> &'static str {
"Do not use deprecated or obsolescent functions"
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn cert_id(&self) -> &'static str {
"MSC24-C"
}
fn scan(&self, root: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.traverse(root, source, violations);
}
}