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;
#[derive(Debug)]
pub struct Msc14C;
impl Msc14C {
#[allow(dead_code)]
pub fn new() -> Self {
Msc14C
}
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"
|| ast_utils::get_node_text(&func, source) != "strerror_r"
{
continue;
}
let Some(parent) = call.parent() else {
continue;
};
let directly_assigned = match parent.kind() {
"init_declarator" => parent.child_by_field_name("value") == Some(call),
"assignment_expression" => parent.child_by_field_name("right") == Some(call),
_ => false,
};
if directly_assigned {
continue;
}
let pos = call.start_position();
violations.push(RuleViolation {
rule_id: "MSC14-C".to_string(),
severity: Severity::Low,
line: pos.row + 1,
column: pos.column + 1,
message: "strerror_r() return value used directly -- its return type (int vs. char*) differs between POSIX/XSI and GNU implementations".to_string(),
file_path: String::new(),
suggestion: Some(
"Capture the return value in an int, check it for an error, and read the message from the supplied buffer rather than using the return value directly"
.to_string(),
),
requires_manual_review: Some(false),
});
}
}
}
impl CertRule for Msc14C {
fn rule_id(&self) -> &'static str {
"MSC14-C"
}
fn description(&self) -> &'static str {
"Do not introduce unnecessary platform dependencies"
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn severity(&self) -> Severity {
Severity::Low
}
fn cert_id(&self) -> &'static str {
"MSC14-C"
}
fn scan(&self, root: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.traverse(root, source, violations);
}
}