pub fn is_user_input_variable(var_name: &str, preceding_text: &str) -> bool {
let input_patterns = [
format!("scanf(\"%d\", &{})", var_name),
format!("scanf(\"%d\",&{})", var_name),
format!("scanf ( \"%d\" , &{} )", var_name),
format!("fscanf(stdin, \"%d\", &{})", var_name),
format!("scanf(\"%u\", &{})", var_name),
];
input_patterns
.iter()
.any(|pattern| preceding_text.contains(pattern))
|| preceding_text.contains("scanf") && preceding_text.contains(&format!("&{}", var_name))
}
pub fn has_validation_before_loop(
var_name: &str,
preceding_text: &str,
_loop_pos: usize,
_source: &str,
) -> bool {
if let Some(scanf_pos) = preceding_text.rfind("scanf") {
let between_scanf_and_loop = &preceding_text[scanf_pos..];
let validation_patterns = [
format!("if ({} >", var_name),
format!("if ({} <", var_name),
format!("if ({} >=", var_name),
format!("if ({} <=", var_name),
format!("if (0 >{}", var_name),
format!("if (0 <{}", var_name),
];
return validation_patterns
.iter()
.any(|p| between_scanf_and_loop.contains(p));
}
false
}
pub fn is_uninitialized_variable(var_name: &str, preceding_text: &str) -> bool {
if var_name
.chars()
.all(|c| c.is_uppercase() || c == '_' || c.is_numeric())
{
return false; }
let declaration_patterns = [
format!("int {};", var_name),
format!("int {} ;", var_name),
format!("size_t {};", var_name),
format!("unsigned {}", var_name),
format!("long {}", var_name),
];
let is_declared = declaration_patterns
.iter()
.any(|p| preceding_text.contains(p))
|| (preceding_text.contains("int")
&& preceding_text.contains(var_name)
&& !preceding_text.contains(&format!("{}[", var_name)));
if !is_declared {
return false;
}
let assignment_patterns = [
format!("{} =", var_name),
format!("{}=", var_name),
format!("&{}", var_name), ];
let is_initialized = assignment_patterns
.iter()
.any(|p| preceding_text.contains(p));
!is_initialized
}
pub fn has_bounds_validation(var_name: &str, preceding_text: &str) -> bool {
let patterns = [
format!("{} <", var_name),
format!("{} <=", var_name),
format!("{} >", var_name),
format!("{} >=", var_name),
format!("< {}", var_name),
format!("<= {}", var_name),
format!("> {}", var_name),
format!(">= {}", var_name),
];
for pattern in &patterns {
if preceding_text.contains("if") && preceding_text.contains(pattern) {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_user_input_variable() {
let code = r#"int count; scanf("%d", &count);"#;
assert!(is_user_input_variable("count", code));
let code2 = "int count = 10;";
assert!(!is_user_input_variable("count", code2));
}
#[test]
fn test_has_validation_before_loop() {
let code = r#"scanf("%d", &count); if (count > 100) { exit(1); } for"#;
assert!(has_validation_before_loop("count", code, 50, code));
let code2 = r#"scanf("%d", &count); for"#;
assert!(!has_validation_before_loop("count", code2, 30, code2));
}
#[test]
fn test_is_uninitialized_variable() {
let code = "int size; for (int i = 0; i < size; i++)";
assert!(is_uninitialized_variable("size", code));
let code2 = "int size = 10; for (int i = 0; i < size; i++)";
assert!(!is_uninitialized_variable("size", code2));
let code3 = "int MAX_SIZE; // constant";
assert!(!is_uninitialized_variable("MAX_SIZE", code3));
}
#[test]
fn test_has_bounds_validation() {
let code = "if (index < size) { arr[index] = val; }";
assert!(has_bounds_validation("index", code));
let code2 = "arr[index] = val;";
assert!(!has_bounds_validation("index", code2));
}
}