use super::parser::Contract;
pub struct SecurityConverter;
impl SecurityConverter {
pub fn new() -> Self {
Self
}
pub fn detect_patterns(&self, contract: &Contract) -> Vec<String> {
let mut patterns = Vec::new();
if self.has_reentrancy_risk(contract) {
patterns.push("@secure".to_string());
patterns.push("@reentrancy_guard".to_string());
}
if self.uses_arithmetic(contract) {
patterns.push("@safe_math".to_string());
}
if self.has_access_control(contract) {
patterns.push("@admin".to_string());
}
patterns
}
pub fn has_reentrancy_risk(&self, contract: &Contract) -> bool {
use super::parser::{Visibility, Mutability};
contract.functions.iter().any(|f| {
let visible = matches!(f.visibility, Visibility::Public | Visibility::External);
let state_changing = matches!(f.mutability, Mutability::Payable | Mutability::NonPayable);
let has_external_call = Self::body_has_external_call(f.body.as_deref().unwrap_or(""));
visible && state_changing && has_external_call
})
}
fn body_has_external_call(body: &str) -> bool {
body.contains(".call(") || body.contains(".call{") || body.contains(".transfer(")
}
pub fn uses_arithmetic(&self, contract: &Contract) -> bool {
if contract.functions.iter().any(|f| {
f.name.contains("add") || f.name.contains("sub")
|| f.name.contains("mul") || f.name.contains("div")
}) {
return true;
}
for f in &contract.functions {
if let Some(ref body) = f.body {
if Self::body_has_arithmetic(body) {
return true;
}
}
}
for v in &contract.state_variables {
if let Some(ref init) = v.initial_value {
if Self::body_has_arithmetic(init) {
return true;
}
}
}
false
}
fn body_has_arithmetic(s: &str) -> bool {
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
if i + 1 < bytes.len() {
match (bytes[i], bytes[i + 1]) {
(b'+', b'+') | (b'-', b'-') | (b'-', b'>') => { i += 2; continue; }
(b'*', b'*') => return true,
_ => {}
}
}
match bytes[i] {
b'+' | b'-' | b'*' | b'/' => return true,
_ => {}
}
i += 1;
}
false
}
fn has_access_control(&self, contract: &Contract) -> bool {
contract.functions.iter().any(|f| {
f.modifiers.iter().any(|m|
m.contains("onlyOwner") ||
m.contains("onlyAdmin") ||
m.contains("onlyRole")
)
})
}
}
impl Default for SecurityConverter {
fn default() -> Self {
Self::new()
}
}