forge_lint/sol/med/
tx_origin.rs1use super::TxOrigin;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::{Expr, ExprKind, Stmt, StmtKind, visit::Visit},
8 interface::{kw, sym},
9};
10use std::ops::ControlFlow;
11
12declare_forge_lint!(TX_ORIGIN, Severity::Med, "tx-origin", "`tx.origin` is used for authorization");
13
14impl<'ast> EarlyLintPass<'ast> for TxOrigin {
15 fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
16 if let StmtKind::If(cond, ..)
17 | StmtKind::DoWhile(_, cond)
18 | StmtKind::While(cond, _)
19 | StmtKind::For { cond: Some(cond), .. } = &stmt.kind
20 {
21 emit_if_contains_tx_origin(ctx, cond);
22 }
23 }
24
25 fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
26 if let ExprKind::Call(callee, args) = &expr.kind
27 && matches!(&callee.kind, ExprKind::Ident(id) if matches!(id.name, sym::require | sym::assert))
28 && let Some(cond) = args.exprs().next()
29 {
30 emit_if_contains_tx_origin(ctx, cond);
31 }
32 }
33}
34
35fn emit_if_contains_tx_origin<'ast>(ctx: &LintContext, expr: &'ast Expr<'ast>) {
36 if TxOriginFinder.visit_expr(expr).is_break() {
37 ctx.emit(&TX_ORIGIN, expr.span);
38 }
39}
40
41struct TxOriginFinder;
42
43impl<'ast> Visit<'ast> for TxOriginFinder {
44 type BreakValue = ();
45
46 fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow<()> {
47 if let ExprKind::Member(base, member) = &expr.kind
48 && member.name == kw::Origin
49 && matches!(&base.kind, ExprKind::Ident(id) if id.name == sym::tx)
50 {
51 return ControlFlow::Break(());
52 }
53 self.walk_expr(expr)
54 }
55}