Skip to main content

fhevm_forge/linter/rules/
missing_allow_addr.rs

1// FHEVM-002: euint* handle passed to external contract without TFHE.allow().
2//
3// Pattern: Detect when an external contract call is made with a euint argument
4// but TFHE.allow(handle, address) is not called in the preceding lines.
5
6use regex::Regex;
7use std::{path::Path, sync::OnceLock};
8use super::{LintError, LintRule, Severity, make_error};
9
10static EXTERNAL_CALL_RE: OnceLock<Regex> = OnceLock::new();
11static ALLOW_RE:         OnceLock<Regex> = OnceLock::new();
12
13pub struct MissingAllowAddr;
14
15impl LintRule for MissingAllowAddr {
16    fn id(&self) -> &'static str { "FHEVM-002" }
17
18    fn check(&self, file_path: &Path, source: &str) -> Vec<LintError> {
19        let ext = file_path.extension().and_then(|e| e.to_str()).unwrap_or("");
20        if ext != "sol" { return vec![]; }
21
22        // Matches patterns like: someContract.someMethod(euintVar, ...)
23        // where the argument is a euint variable being passed externally
24        let external_re = EXTERNAL_CALL_RE.get_or_init(|| {
25            Regex::new(r"\b(\w+)\.(mint|transfer|deposit|withdraw|set|update|add|burn)\s*\([^)]*\b(euint|ebool)\b").unwrap()
26        });
27        let allow_re = ALLOW_RE.get_or_init(|| {
28            Regex::new(r"TFHE\.allow\(").unwrap()
29        });
30
31        let lines: Vec<&str> = source.lines().collect();
32        let mut errors = Vec::new();
33
34        for (i, line) in lines.iter().enumerate() {
35            if external_re.is_match(line) {
36                // Look back 5 lines for TFHE.allow()
37                let window_start = i.saturating_sub(5);
38                let window = &lines[window_start..=i];
39                let has_allow = window.iter().any(|l| allow_re.is_match(l));
40
41                if !has_allow {
42                    errors.push(make_error(
43                        "FHEVM-002",
44                        Severity::Error,
45                        file_path,
46                        i + 1,
47                        "euint handle passed to external contract without TFHE.allow(). \
48                         Call TFHE.allow(handle, address(externalContract)) before the external call.",
49                        Some(line),
50                    ));
51                }
52            }
53        }
54
55        errors
56    }
57}