diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..2cc52c5c8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,12 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# fmt: all (#3398) +748ae7fc6da5bd63f1955cb1a7b5eb6b36e0ad61 diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 276e68eb4..eccc4ada5 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -64,11 +64,6 @@ jobs: run: | git submodule update --init --recursive - - uses: Swatinem/rust-cache@v2 - - name: Run cargo test - run: | - cargo test _by_loading_contract_directly - - uses: Swatinem/rust-cache@v2 - name: Run cargo test run: | @@ -272,23 +267,26 @@ jobs: - name: Checkout sources uses: actions/checkout@v2 - - name: Install stable toolchain + - name: Install nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable - components: rustfmt, clippy + toolchain: nightly + components: rustfmt override: true - - name: Install git submodules + - name: Run cargo fmt run: | - git submodule update --init --recursive + cargo +nightly fmt --all --check - - name: Run cargo fmt - uses: actions-rs/cargo@v1 + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 with: - command: fmt - args: --all -- --check + profile: minimal + toolchain: stable + components: clippy + override: true + - name: Run cargo clippy uses: actions-rs/cargo@v1 diff --git a/aderyn/src/lib.rs b/aderyn/src/lib.rs index 31752b5bb..3abe1cbc4 100644 --- a/aderyn/src/lib.rs +++ b/aderyn/src/lib.rs @@ -88,21 +88,21 @@ fn right_pad(s: &str, by: usize) -> String { pub static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); -pub fn aderyn_is_currently_running_newest_version() -> Result { +pub fn aderyn_is_currently_running_newest_version() -> Option { let client = reqwest::blocking::Client::builder() .user_agent(APP_USER_AGENT) - .build()?; + .build() + .expect("client is unable to initialize"); - let latest_version_checker = client - .get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest") - .send()?; + let latest_version_checker = + client.get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest").send().ok()?; - let data = latest_version_checker.json::()?; - let newest = - Version::parse(data["tag_name"].as_str().unwrap().replace('v', "").as_str()).unwrap(); - let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); + let data = latest_version_checker.json::().ok()?; + let version_string = data["tag_name"].as_str()?; + let newest = Version::parse(version_string.replace('v', "").as_str()).ok()?; + let current = Version::parse(env!("CARGO_PKG_VERSION")).expect("Pkg version not available"); - Ok(current >= newest) + Some(current >= newest) } #[cfg(test)] @@ -111,6 +111,6 @@ mod latest_version_checker_tests { #[test] fn can_get_latest_version_from_crate_registry() { - assert!(aderyn_is_currently_running_newest_version().is_ok()) + assert!(aderyn_is_currently_running_newest_version().is_some()) } } diff --git a/aderyn/src/lsp.rs b/aderyn/src/lsp.rs index 69d95eb43..d9dbc94db 100644 --- a/aderyn/src/lsp.rs +++ b/aderyn/src/lsp.rs @@ -1,15 +1,13 @@ use log::{info, warn}; use notify_debouncer_full::notify::{Event, RecommendedWatcher, Result as NotifyResult}; -use std::collections::HashSet; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::Duration; -use tokio::runtime::Builder; -use tokio::sync::mpsc::Receiver; -use tokio::sync::Mutex; -use tower_lsp::jsonrpc::Result; -use tower_lsp::{lsp_types::*, ClientSocket}; -use tower_lsp::{Client, LanguageServer, LspService, Server}; +use std::{collections::HashSet, path::PathBuf, sync::Arc, time::Duration}; +use tokio::{ + runtime::Builder, + sync::{mpsc::Receiver, Mutex}, +}; +use tower_lsp::{ + jsonrpc::Result, lsp_types::*, Client, ClientSocket, LanguageServer, LspService, Server, +}; use aderyn_driver::driver::{self, Args}; @@ -27,10 +25,7 @@ impl LanguageServer for LanguageServerBackend { let code_editor = self.client.lock().await; code_editor - .log_message( - MessageType::INFO, - "Aderyn LSP received an initialization request!", - ) + .log_message(MessageType::INFO, "Aderyn LSP received an initialization request!") .await; Ok(InitializeResult { @@ -66,9 +61,7 @@ impl LanguageServer for LanguageServerBackend { info!("TLSP shutdown"); let code_editor = self.client.lock().await; - code_editor - .log_message(MessageType::INFO, "Aderyn LSP has been shutdown") - .await; + code_editor.log_message(MessageType::INFO, "Aderyn LSP has been shutdown").await; Ok(()) } } @@ -92,7 +85,8 @@ pub fn spin_up_language_server(args: Args) { // Block on this function async_runtime.block_on(async { - // Channel to communicate file system changes (triggered when files are added, removed, or changed) + // Channel to communicate file system changes (triggered when files are added, removed, or + // changed) let (tx_file_change_event, rx_file_change_event) = tokio::sync::mpsc::channel(10); // Create the async watcher @@ -110,10 +104,7 @@ pub fn spin_up_language_server(args: Args) { // Watch for file changes file_system_watcher - .watch( - PathBuf::from(args.root.clone()).as_path(), - RecursiveMode::Recursive, - ) + .watch(PathBuf::from(args.root.clone()).as_path(), RecursiveMode::Recursive) .expect("unable to watch for file changes"); // Most editor's LSP clients communicate through stdout/stdin channels. Theefore use @@ -169,10 +160,7 @@ fn create_lsp_service_and_react_to_file_event( return; }; - info!( - "sending diagnostics to client {:?}", - &diagnostics_report.diagnostics - ); + info!("sending diagnostics to client {:?}", &diagnostics_report.diagnostics); let client_mutex = guarded_client.lock().await; for (file_uri, file_diagnostics) in &diagnostics_report.diagnostics { @@ -182,11 +170,8 @@ fn create_lsp_service_and_react_to_file_event( } // Clear out the diagnostics for file which had reported errors before - let current_run_file_uris = diagnostics_report - .diagnostics - .keys() - .cloned() - .collect::>(); + let current_run_file_uris = + diagnostics_report.diagnostics.keys().cloned().collect::>(); let mut seen_file_uris_mutex = seen_file_uris.lock().await; let seen_file_uris = &mut *seen_file_uris_mutex; @@ -195,9 +180,7 @@ fn create_lsp_service_and_react_to_file_event( if !¤t_run_file_uris.contains(seen_file_uri) { // Clear the diagnostics for this seen file uri // It had errors in the past, but not any more - client_mutex - .publish_diagnostics(seen_file_uri.clone(), vec![], None) - .await; + client_mutex.publish_diagnostics(seen_file_uri.clone(), vec![], None).await; } } @@ -234,9 +217,7 @@ fn create_lsp_service_and_react_to_file_event( } }); - LanguageServerBackend { - client: guarded_client_clone, - } + LanguageServerBackend { client: guarded_client_clone } }); (service, socket) } diff --git a/aderyn/src/main.rs b/aderyn/src/main.rs index eda36a235..8c1a55df3 100644 --- a/aderyn/src/main.rs +++ b/aderyn/src/main.rs @@ -141,7 +141,7 @@ fn main() { // Check for updates if !cmd_args.skip_update_check { - if let Ok(yes) = aderyn_is_currently_running_newest_version() { + if let Some(yes) = aderyn_is_currently_running_newest_version() { if !yes { println!(); println!("NEW VERSION OF ADERYN AVAILABLE! Please run `cyfrinup` to upgrade."); diff --git a/aderyn/src/panic.rs b/aderyn/src/panic.rs index 147f5c223..ae9792f7a 100644 --- a/aderyn/src/panic.rs +++ b/aderyn/src/panic.rs @@ -24,20 +24,16 @@ pub fn stderr_buffer_writer() -> BufferWriter { } pub fn add_handler() { - std::panic::set_hook(Box::new(move |info: &PanicInfo<'_>| { - print_compiler_bug_message(info) - })); + std::panic::set_hook(Box::new(move |info: &PanicInfo<'_>| print_compiler_bug_message(info))); } fn print_compiler_bug_message(info: &PanicInfo<'_>) { - let message = match ( - info.payload().downcast_ref::<&str>(), - info.payload().downcast_ref::(), - ) { - (Some(s), _) => (*s).to_string(), - (_, Some(s)) => s.to_string(), - (None, None) => "unknown error".into(), - }; + let message = + match (info.payload().downcast_ref::<&str>(), info.payload().downcast_ref::()) { + (Some(s), _) => (*s).to_string(), + (_, Some(s)) => s.to_string(), + (None, None) => "unknown error".into(), + }; let location = match info.location() { None => "".into(), @@ -46,16 +42,14 @@ fn print_compiler_bug_message(info: &PanicInfo<'_>) { let buffer_writer = stderr_buffer_writer(); let mut buffer = buffer_writer.buffer(); - buffer - .set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Red))) - .unwrap(); + buffer.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Color::Red))).unwrap(); write!(buffer, "error").unwrap(); buffer.set_color(ColorSpec::new().set_bold(true)).unwrap(); write!(buffer, ": Fatal compiler bug!\n\n").unwrap(); buffer.set_color(&ColorSpec::new()).unwrap(); writeln!( buffer, - "This is a fatal bug in the Aderyn, sorry! + "This is a fatal bug in Aderyn, sorry! Please report this crash to https://github.com/cyfrin/aderyn/issues/new and include this error message with your report. diff --git a/aderyn_core/Cargo.toml b/aderyn_core/Cargo.toml index 5efac1653..30b4d8d11 100644 --- a/aderyn_core/Cargo.toml +++ b/aderyn_core/Cargo.toml @@ -34,3 +34,6 @@ derive_more = "0.99.18" petgraph = "0" serial_test = "3.0.0" once_cell = "1.19.0" + +[lib] +doctest = false diff --git a/aderyn_core/src/ast/ast.rs b/aderyn_core/src/ast/ast.rs index 9cf34f0b6..1b92b375b 100644 --- a/aderyn_core/src/ast/ast.rs +++ b/aderyn_core/src/ast/ast.rs @@ -1,6 +1,5 @@ use crate::{ - ast::macros::*, - ast::*, + ast::{macros::*, *}, visitor::ast_visitor::{ASTConstVisitor, Node}, }; use eyre::Result; diff --git a/aderyn_core/src/ast/ast_nodes.rs b/aderyn_core/src/ast/ast_nodes.rs index 5caa77ecf..e6ad2ea5a 100644 --- a/aderyn_core/src/ast/ast_nodes.rs +++ b/aderyn_core/src/ast/ast_nodes.rs @@ -1,5 +1,7 @@ -use super::macros::{ast_node, ast_node_no_partial_eq, expr_node, node_group, stmt_node}; -use super::*; +use super::{ + macros::{ast_node, ast_node_no_partial_eq, expr_node, node_group, stmt_node}, + *, +}; use std::collections::{BTreeMap, HashMap}; use serde::{Deserialize, Serialize}; @@ -99,19 +101,17 @@ impl<'de> Deserialize<'de> for TypeName { let type_name = node_type.unwrap().as_str().unwrap(); match type_name { - "FunctionTypeName" => Ok(TypeName::FunctionTypeName( - serde_json::from_value(json).unwrap(), - )), - "ArrayTypeName" => Ok(TypeName::ArrayTypeName( - serde_json::from_value(json).unwrap(), - )), + "FunctionTypeName" => { + Ok(TypeName::FunctionTypeName(serde_json::from_value(json).unwrap())) + } + "ArrayTypeName" => Ok(TypeName::ArrayTypeName(serde_json::from_value(json).unwrap())), "Mapping" => Ok(TypeName::Mapping(serde_json::from_value(json).unwrap())), - "UserDefinedTypeName" => Ok(TypeName::UserDefinedTypeName( - serde_json::from_value(json).unwrap(), - )), - "ElementaryTypeName" => Ok(TypeName::ElementaryTypeName( - serde_json::from_value(json).unwrap(), - )), + "UserDefinedTypeName" => { + Ok(TypeName::UserDefinedTypeName(serde_json::from_value(json).unwrap())) + } + "ElementaryTypeName" => { + Ok(TypeName::ElementaryTypeName(serde_json::from_value(json).unwrap())) + } _ => panic!("Unrecognized type name {type_name}"), } } @@ -645,7 +645,8 @@ ast_node!( stmt_node!( #[derive(Hash)] struct Return { - function_return_parameters: Option, // When returning in a modifier, this can be none + function_return_parameters: Option, /* When returning in a modifier, this can be + * none */ expression: Option, } ); diff --git a/aderyn_core/src/ast/impls/disp/expressions.rs b/aderyn_core/src/ast/impls/disp/expressions.rs index 13b9d2abd..6584dde5a 100644 --- a/aderyn_core/src/ast/impls/disp/expressions.rs +++ b/aderyn_core/src/ast/impls/disp/expressions.rs @@ -26,11 +26,7 @@ impl Display for Expression { impl Display for UnaryOperation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "{}{}", - self.sub_expression, - self.operator.as_str() - )) + f.write_fmt(format_args!("{}{}", self.sub_expression, self.operator.as_str())) } } @@ -84,10 +80,7 @@ impl Display for FunctionCallOptions { let option_count = self.options.len(); if self.names.len() != option_count { - eprintln!( - "ERROR: invalid FunctionCallOptions: {:?}, {:?}", - self.names, self.options - ); + eprintln!("ERROR: invalid FunctionCallOptions: {:?}, {:?}", self.names, self.options); return Err(std::fmt::Error); } @@ -127,10 +120,7 @@ impl Display for FunctionCallOptions { impl Display for IndexAccess { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(index_expression) = &self.index_expression { - f.write_fmt(format_args!( - "{}[{}]", - self.base_expression, index_expression - )) + f.write_fmt(format_args!("{}[{}]", self.base_expression, index_expression)) } else { f.write_fmt(format_args!("{}[]", self.base_expression)) } diff --git a/aderyn_core/src/ast/impls/disp/types.rs b/aderyn_core/src/ast/impls/disp/types.rs index 9d3004178..dcbe5c758 100644 --- a/aderyn_core/src/ast/impls/disp/types.rs +++ b/aderyn_core/src/ast/impls/disp/types.rs @@ -54,9 +54,6 @@ impl Display for ArrayTypeName { impl Display for Mapping { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "mapping({} => {})", - self.key_type, self.value_type - )) + f.write_fmt(format_args!("mapping({} => {})", self.key_type, self.value_type)) } } diff --git a/aderyn_core/src/ast/impls/disp/user_defined_value_types.rs b/aderyn_core/src/ast/impls/disp/user_defined_value_types.rs index 8e64a23e1..e7476817b 100644 --- a/aderyn_core/src/ast/impls/disp/user_defined_value_types.rs +++ b/aderyn_core/src/ast/impls/disp/user_defined_value_types.rs @@ -3,9 +3,6 @@ use std::fmt::Display; impl Display for UserDefinedValueTypeDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "type {} is {}", - self.name, self.underlying_type, - )) + f.write_fmt(format_args!("type {} is {}", self.name, self.underlying_type,)) } } diff --git a/aderyn_core/src/ast/impls/node/blocks.rs b/aderyn_core/src/ast/impls/node/blocks.rs index 8bc7c0cb5..db0d0c5ad 100644 --- a/aderyn_core/src/ast/impls/node/blocks.rs +++ b/aderyn_core/src/ast/impls/node/blocks.rs @@ -14,11 +14,7 @@ impl Node for Block { } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let children_ids = self - .statements - .iter() - .flat_map(|x| x.get_node_id()) - .collect::>(); + let children_ids = self.statements.iter().flat_map(|x| x.get_node_id()).collect::>(); visitor.visit_immediate_children(self.id, children_ids)?; Ok(()) } @@ -36,11 +32,7 @@ impl Node for UncheckedBlock { } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let children_ids = self - .statements - .iter() - .flat_map(|x| x.get_node_id()) - .collect::>(); + let children_ids = self.statements.iter().flat_map(|x| x.get_node_id()).collect::>(); visitor.visit_immediate_children(self.id, children_ids)?; Ok(()) } diff --git a/aderyn_core/src/ast/impls/node/documentation.rs b/aderyn_core/src/ast/impls/node/documentation.rs index dc9d1c8c3..77cda3c54 100644 --- a/aderyn_core/src/ast/impls/node/documentation.rs +++ b/aderyn_core/src/ast/impls/node/documentation.rs @@ -10,10 +10,7 @@ impl Node for Documentation { } Documentation::Structured(opt_structured_documentation) => { if opt_structured_documentation.is_some() { - opt_structured_documentation - .as_ref() - .unwrap() - .accept(visitor)?; + opt_structured_documentation.as_ref().unwrap().accept(visitor)?; } Ok(()) } diff --git a/aderyn_core/src/ast/impls/node/enumerations.rs b/aderyn_core/src/ast/impls/node/enumerations.rs index e4eb8903a..2cce81363 100644 --- a/aderyn_core/src/ast/impls/node/enumerations.rs +++ b/aderyn_core/src/ast/impls/node/enumerations.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; use macros::accept_id; @@ -20,12 +19,7 @@ impl Node for EnumDefinition { visitor.end_visit_enum_definition(self) } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let member_ids = &self - .members - .iter() - .map(|x| x.id) - .collect::>() - .clone(); + let member_ids = &self.members.iter().map(|x| x.id).collect::>().clone(); visitor.visit_immediate_children(self.id, member_ids.clone())?; Ok(()) } diff --git a/aderyn_core/src/ast/impls/node/errors.rs b/aderyn_core/src/ast/impls/node/errors.rs index 312482334..288a266f0 100644 --- a/aderyn_core/src/ast/impls/node/errors.rs +++ b/aderyn_core/src/ast/impls/node/errors.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for ErrorDefinition { diff --git a/aderyn_core/src/ast/impls/node/events.rs b/aderyn_core/src/ast/impls/node/events.rs index da138d795..45fd76865 100644 --- a/aderyn_core/src/ast/impls/node/events.rs +++ b/aderyn_core/src/ast/impls/node/events.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for EventDefinition { diff --git a/aderyn_core/src/ast/impls/node/expressions.rs b/aderyn_core/src/ast/impls/node/expressions.rs index 7ee55bfb4..6d136fb27 100644 --- a/aderyn_core/src/ast/impls/node/expressions.rs +++ b/aderyn_core/src/ast/impls/node/expressions.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for Expression { diff --git a/aderyn_core/src/ast/impls/node/functions.rs b/aderyn_core/src/ast/impls/node/functions.rs index 639b6a4da..4e25e44fa 100644 --- a/aderyn_core/src/ast/impls/node/functions.rs +++ b/aderyn_core/src/ast/impls/node/functions.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for ParameterList { @@ -36,11 +35,8 @@ impl Node for OverrideSpecifier { visitor.end_visit_override_specifier(self) } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let overrides_ids = &self - .overrides - .iter() - .filter_map(|x| x.get_node_id()) - .collect::>(); + let overrides_ids = + &self.overrides.iter().filter_map(|x| x.get_node_id()).collect::>(); visitor.visit_immediate_children(self.id, overrides_ids.clone())?; Ok(()) } diff --git a/aderyn_core/src/ast/impls/node/identifiers.rs b/aderyn_core/src/ast/impls/node/identifiers.rs index e4d58f1a7..82e4bf339 100644 --- a/aderyn_core/src/ast/impls/node/identifiers.rs +++ b/aderyn_core/src/ast/impls/node/identifiers.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for Identifier { diff --git a/aderyn_core/src/ast/impls/node/import_directives.rs b/aderyn_core/src/ast/impls/node/import_directives.rs index 219d0b20f..413bb2672 100644 --- a/aderyn_core/src/ast/impls/node/import_directives.rs +++ b/aderyn_core/src/ast/impls/node/import_directives.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for ImportDirective { diff --git a/aderyn_core/src/ast/impls/node/modifiers.rs b/aderyn_core/src/ast/impls/node/modifiers.rs index 9a8e3830a..54c3a87fb 100644 --- a/aderyn_core/src/ast/impls/node/modifiers.rs +++ b/aderyn_core/src/ast/impls/node/modifiers.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for ModifierDefinition { diff --git a/aderyn_core/src/ast/impls/node/pragma_directives.rs b/aderyn_core/src/ast/impls/node/pragma_directives.rs index 46282bdea..63930b04e 100644 --- a/aderyn_core/src/ast/impls/node/pragma_directives.rs +++ b/aderyn_core/src/ast/impls/node/pragma_directives.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for PragmaDirective { diff --git a/aderyn_core/src/ast/impls/node/source_units.rs b/aderyn_core/src/ast/impls/node/source_units.rs index 8005db3c0..eab2df0d3 100644 --- a/aderyn_core/src/ast/impls/node/source_units.rs +++ b/aderyn_core/src/ast/impls/node/source_units.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for SourceUnitNode { @@ -43,11 +42,7 @@ impl Node for SourceUnit { visitor.end_visit_source_unit(self) } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let node_ids = &self - .nodes - .iter() - .flat_map(|x| x.get_node_id()) - .collect::>(); + let node_ids = &self.nodes.iter().flat_map(|x| x.get_node_id()).collect::>(); visitor.visit_immediate_children(self.id, node_ids.clone())?; Ok(()) } diff --git a/aderyn_core/src/ast/impls/node/statements.rs b/aderyn_core/src/ast/impls/node/statements.rs index 9999f3d7e..92c3ac95b 100644 --- a/aderyn_core/src/ast/impls/node/statements.rs +++ b/aderyn_core/src/ast/impls/node/statements.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; use macros::accept_id; @@ -69,12 +68,7 @@ impl Node for VariableDeclarationStatement { visitor.end_visit_variable_declaration_statement(self) } fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> { - let declaration_ids = self - .declarations - .iter() - .flatten() - .map(|x| x.id) - .collect::>(); + let declaration_ids = self.declarations.iter().flatten().map(|x| x.id).collect::>(); visitor.visit_immediate_children(self.id, declaration_ids)?; if let Some(initial_value) = &self.initial_value { if let Some(id) = initial_value.get_node_id() { diff --git a/aderyn_core/src/ast/impls/node/structures.rs b/aderyn_core/src/ast/impls/node/structures.rs index 75e7ba1da..7e3786bcb 100644 --- a/aderyn_core/src/ast/impls/node/structures.rs +++ b/aderyn_core/src/ast/impls/node/structures.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for StructDefinition { diff --git a/aderyn_core/src/ast/impls/node/types.rs b/aderyn_core/src/ast/impls/node/types.rs index 5fd7ca43c..8960987bb 100644 --- a/aderyn_core/src/ast/impls/node/types.rs +++ b/aderyn_core/src/ast/impls/node/types.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; use macros::accept_id; diff --git a/aderyn_core/src/ast/impls/node/user_defined_value_types.rs b/aderyn_core/src/ast/impls/node/user_defined_value_types.rs index 0a984caf6..a954d74f2 100644 --- a/aderyn_core/src/ast/impls/node/user_defined_value_types.rs +++ b/aderyn_core/src/ast/impls/node/user_defined_value_types.rs @@ -1,6 +1,7 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::ASTConstVisitor; -use crate::visitor::ast_visitor::Node; +use crate::{ + ast::*, + visitor::ast_visitor::{ASTConstVisitor, Node}, +}; use eyre::Result; impl Node for UserDefinedValueTypeDefinition { diff --git a/aderyn_core/src/ast/impls/node/using_for_directives.rs b/aderyn_core/src/ast/impls/node/using_for_directives.rs index 0456370d2..65d10ddea 100644 --- a/aderyn_core/src/ast/impls/node/using_for_directives.rs +++ b/aderyn_core/src/ast/impls/node/using_for_directives.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for UsingForDirective { diff --git a/aderyn_core/src/ast/impls/node/variables.rs b/aderyn_core/src/ast/impls/node/variables.rs index a5a3ee78a..bead337d2 100644 --- a/aderyn_core/src/ast/impls/node/variables.rs +++ b/aderyn_core/src/ast/impls/node/variables.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::visitor::ast_visitor::*; +use crate::{ast::*, visitor::ast_visitor::*}; use eyre::Result; impl Node for VariableDeclaration { diff --git a/aderyn_core/src/ast/impls/own/contracts.rs b/aderyn_core/src/ast/impls/own/contracts.rs index 104a59aba..7ba770f45 100644 --- a/aderyn_core/src/ast/impls/own/contracts.rs +++ b/aderyn_core/src/ast/impls/own/contracts.rs @@ -195,17 +195,16 @@ impl ContractDefinition { for &contract_id in contract_ids.iter() { // Loop through all of the schema source_units in the project for source_unit in source_units.iter() { - // Attempt to retrieve the current contract in the inheritance hierarchy from the current schema source_unit + // Attempt to retrieve the current contract in the inheritance hierarchy from + // the current schema source_unit let contract_definition = match source_unit.contract_definition(contract_id) { Some(contract_definition) => contract_definition, None => continue, }; - // Attempt to retrieve the requested state variable from the current contract in the inheritance hierarchy - if contract_definition - .variable_declaration(state_variable_id) - .is_some() - { + // Attempt to retrieve the requested state variable from the current contract in + // the inheritance hierarchy + if contract_definition.variable_declaration(state_variable_id).is_some() { return true; } } @@ -297,11 +296,7 @@ impl ContractDefinition { if let FunctionKind::Constructor = function_definition.kind() { "constructor".to_string() } else { - format!( - "`{}` {}", - function_definition.name, - function_definition.kind() - ) + format!("`{}` {}", function_definition.name, function_definition.kind()) }, self.name, self.kind, diff --git a/aderyn_core/src/ast/impls/own/expressions.rs b/aderyn_core/src/ast/impls/own/expressions.rs index 40f1a03fe..58d857697 100644 --- a/aderyn_core/src/ast/impls/own/expressions.rs +++ b/aderyn_core/src/ast/impls/own/expressions.rs @@ -119,49 +119,43 @@ impl Expression { pub fn type_descriptions(&self) -> Option<&TypeDescriptions> { match self { - Expression::Literal(Literal { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::Identifier(Identifier { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::UnaryOperation(UnaryOperation { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::BinaryOperation(BinaryOperation { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::Conditional(Conditional { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::Assignment(Assignment { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::FunctionCall(FunctionCall { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::FunctionCallOptions(FunctionCallOptions { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::IndexAccess(IndexAccess { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::IndexRangeAccess(IndexRangeAccess { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::MemberAccess(MemberAccess { - type_descriptions, .. - }) => Some(type_descriptions), + Expression::Literal(Literal { type_descriptions, .. }) => Some(type_descriptions), + Expression::Identifier(Identifier { type_descriptions, .. }) => Some(type_descriptions), + Expression::UnaryOperation(UnaryOperation { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::BinaryOperation(BinaryOperation { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::Conditional(Conditional { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::Assignment(Assignment { type_descriptions, .. }) => Some(type_descriptions), + Expression::FunctionCall(FunctionCall { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::FunctionCallOptions(FunctionCallOptions { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::IndexAccess(IndexAccess { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::IndexRangeAccess(IndexRangeAccess { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::MemberAccess(MemberAccess { type_descriptions, .. }) => { + Some(type_descriptions) + } Expression::ElementaryTypeNameExpression(ElementaryTypeNameExpression { type_descriptions, .. }) => Some(type_descriptions), - Expression::TupleExpression(TupleExpression { - type_descriptions, .. - }) => Some(type_descriptions), - Expression::NewExpression(NewExpression { - type_descriptions, .. - }) => Some(type_descriptions), + Expression::TupleExpression(TupleExpression { type_descriptions, .. }) => { + Some(type_descriptions) + } + Expression::NewExpression(NewExpression { type_descriptions, .. }) => { + Some(type_descriptions) + } } } @@ -271,11 +265,7 @@ impl MemberAccess { impl TupleExpression { pub fn contains_operation(&self, operator: &str) -> bool { for component in self.components.iter() { - if component - .as_ref() - .map(|expr| expr.contains_operation(operator)) - .unwrap_or(false) - { + if component.as_ref().map(|expr| expr.contains_operation(operator)).unwrap_or(false) { return true; } } diff --git a/aderyn_core/src/ast/impls/own/functions.rs b/aderyn_core/src/ast/impls/own/functions.rs index 2039386b6..2d6c46453 100644 --- a/aderyn_core/src/ast/impls/own/functions.rs +++ b/aderyn_core/src/ast/impls/own/functions.rs @@ -34,12 +34,7 @@ impl FunctionDefinition { match expression { Expression::Identifier(identifier) => { if let Some(reference_id) = identifier.referenced_declaration { - if self - .return_parameters - .parameters - .iter() - .any(|p| p.id == reference_id) - { + if self.return_parameters.parameters.iter().any(|p| p.id == reference_id) { ids.push(reference_id); } } diff --git a/aderyn_core/src/ast/impls/own/identifiers.rs b/aderyn_core/src/ast/impls/own/identifiers.rs index 74d66a794..a74256db0 100644 --- a/aderyn_core/src/ast/impls/own/identifiers.rs +++ b/aderyn_core/src/ast/impls/own/identifiers.rs @@ -5,12 +5,8 @@ impl PartialEq for Identifier { fn eq(&self, other: &Self) -> bool { self.argument_types.eq(&other.argument_types) && self.name.eq(&other.name) - && self - .overloaded_declarations - .eq(&other.overloaded_declarations) - && self - .referenced_declaration - .eq(&other.referenced_declaration) + && self.overloaded_declarations.eq(&other.overloaded_declarations) + && self.referenced_declaration.eq(&other.referenced_declaration) && self.type_descriptions.eq(&other.type_descriptions) } } @@ -29,10 +25,7 @@ impl Hash for Identifier { impl PartialEq for IdentifierPath { fn eq(&self, other: &Self) -> bool { - self.name.eq(&other.name) - && self - .referenced_declaration - .eq(&other.referenced_declaration) + self.name.eq(&other.name) && self.referenced_declaration.eq(&other.referenced_declaration) } } diff --git a/aderyn_core/src/ast/impls/own/source_units.rs b/aderyn_core/src/ast/impls/own/source_units.rs index 047cb86d7..7ffe1512b 100644 --- a/aderyn_core/src/ast/impls/own/source_units.rs +++ b/aderyn_core/src/ast/impls/own/source_units.rs @@ -1,6 +1,5 @@ use crate::ast::*; -use eyre::eyre; -use eyre::Result; +use eyre::{eyre, Result}; use std::io; impl SourceUnitNode { @@ -46,10 +45,7 @@ impl SourceUnit { }) .collect(); - let index = values - .first() - .and_then(|&value| value) - .ok_or_else(|| eyre!("not found"))?; + let index = values.first().and_then(|&value| value).ok_or_else(|| eyre!("not found"))?; if index > source.len() { return Err(eyre!("index out of bounds")); diff --git a/aderyn_core/src/ast/impls/own/statements.rs b/aderyn_core/src/ast/impls/own/statements.rs index d102b6415..17615dd07 100644 --- a/aderyn_core/src/ast/impls/own/statements.rs +++ b/aderyn_core/src/ast/impls/own/statements.rs @@ -52,11 +52,7 @@ impl BlockOrStatement { BlockOrStatement::Statement(statement) => match statement.as_ref() { Statement::Return(Return { .. }) => true, - Statement::IfStatement(IfStatement { - true_body, - false_body, - .. - }) => { + Statement::IfStatement(IfStatement { true_body, false_body, .. }) => { if !true_body.contains_returns() { return false; } diff --git a/aderyn_core/src/ast/impls/own/types.rs b/aderyn_core/src/ast/impls/own/types.rs index dc1bb5d77..4a98d89e1 100644 --- a/aderyn_core/src/ast/impls/own/types.rs +++ b/aderyn_core/src/ast/impls/own/types.rs @@ -18,8 +18,7 @@ impl Hash for ElementaryTypeName { impl PartialEq for UserDefinedTypeName { fn eq(&self, other: &Self) -> bool { - self.referenced_declaration - .eq(&other.referenced_declaration) + self.referenced_declaration.eq(&other.referenced_declaration) } } diff --git a/aderyn_core/src/ast/mod.rs b/aderyn_core/src/ast/mod.rs index 0eb5c89e6..743d1d8aa 100644 --- a/aderyn_core/src/ast/mod.rs +++ b/aderyn_core/src/ast/mod.rs @@ -20,23 +20,21 @@ mod tests { #[test] fn can_parse_ast() { - fs::read_dir(PathBuf::from("../tests/ast")) - .unwrap() - .for_each(|path| { - let path = path.unwrap().path(); - let path_str = path.to_string_lossy(); + fs::read_dir(PathBuf::from("../tests/ast")).unwrap().for_each(|path| { + let path = path.unwrap().path(); + let path_str = path.to_string_lossy(); - let input = fs::read_to_string(&path).unwrap(); - let result: Result = serde_json::from_str(&input); - match result { - Err(e) => { - println!("... {path_str} fail: {e}"); - panic!(); - } - Ok(_) => { - println!("... {path_str} ok"); - } + let input = fs::read_to_string(&path).unwrap(); + let result: Result = serde_json::from_str(&input); + match result { + Err(e) => { + println!("... {path_str} fail: {e}"); + panic!(); } - }) + Ok(_) => { + println!("... {path_str} ok"); + } + } + }) } } diff --git a/aderyn_core/src/ast/yul.rs b/aderyn_core/src/ast/yul.rs index 3d0c0af0d..d96530076 100644 --- a/aderyn_core/src/ast/yul.rs +++ b/aderyn_core/src/ast/yul.rs @@ -2,7 +2,10 @@ use crate::visitor::ast_visitor::{list_accept, ASTConstVisitor, Node}; use eyre::Result; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, hash::Hash, hash::Hasher}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, +}; use super::*; diff --git a/aderyn_core/src/audit/public_functions_no_sender.rs b/aderyn_core/src/audit/public_functions_no_sender.rs index 245e6f7f0..0a0efc968 100644 --- a/aderyn_core/src/audit/public_functions_no_sender.rs +++ b/aderyn_core/src/audit/public_functions_no_sender.rs @@ -49,15 +49,14 @@ impl AuditorDetector for PublicFunctionsNoSenderChecksDetector { get_implemented_external_and_public_functions(context).filter(|function_definition| { // Check if the function has an owner-related modifier let does_not_have_an_owner_modifier = - !ExtractModifierInvocations::from(*function_definition) - .extracted - .iter() - .any(|modifier| { + !ExtractModifierInvocations::from(*function_definition).extracted.iter().any( + |modifier| { modifier.modifier_name.name() == "onlyOwner" || modifier.modifier_name.name() == "onlyAdmin" || modifier.modifier_name.name() == "onlyRole" || modifier.modifier_name.name() == "requiresAuth" - }); + }, + ); // Check if the function has a `msg.sender` BinaryOperation check let has_msg_sender_binary_operation = has_msg_sender_binary_operation(&((*function_definition).into())); @@ -94,11 +93,7 @@ impl AuditorDetector for PublicFunctionsNoSenderChecksDetector { self.found_instances .iter() .map(|instance| { - row![ - instance.contract_name, - instance.function_kind, - instance.function_name, - ] + row![instance.contract_name, instance.function_kind, instance.function_name,] }) .collect() } diff --git a/aderyn_core/src/context/browser/extractor.rs b/aderyn_core/src/context/browser/extractor.rs index a9b4cf102..2d30d0e49 100644 --- a/aderyn_core/src/context/browser/extractor.rs +++ b/aderyn_core/src/context/browser/extractor.rs @@ -141,11 +141,7 @@ impl<'a> ExtractReferencedDeclarationsConditionally<'a> { condition: Box bool>, ) -> Self { let mut extractor: ExtractReferencedDeclarationsConditionally = - ExtractReferencedDeclarationsConditionally { - extracted: vec![], - condition, - context, - }; + ExtractReferencedDeclarationsConditionally { extracted: vec![], condition, context }; node.accept(&mut extractor).unwrap_or_default(); extractor } diff --git a/aderyn_core/src/context/browser/siblings.rs b/aderyn_core/src/context/browser/siblings.rs index 9c74b6dda..18e165b7d 100644 --- a/aderyn_core/src/context/browser/siblings.rs +++ b/aderyn_core/src/context/browser/siblings.rs @@ -4,8 +4,7 @@ use crate::{ visitor::ast_visitor::{ASTConstVisitor, Node}, }; -use super::GetImmediateChildren; -use super::SortNodeReferencesToSequence; +use super::{GetImmediateChildren, SortNodeReferencesToSequence}; pub trait GetNextSibling { /// Get the next sibling an ASTNode diff --git a/aderyn_core/src/context/browser/sort_nodes.rs b/aderyn_core/src/context/browser/sort_nodes.rs index 5bcb0c5cf..e0bad45a4 100644 --- a/aderyn_core/src/context/browser/sort_nodes.rs +++ b/aderyn_core/src/context/browser/sort_nodes.rs @@ -52,9 +52,7 @@ fn sort_by_src_position<'a>( // Now sort them let mut nodes = nodes.to_vec(); nodes.sort_by(|a, b| { - context - .get_relative_location_of_nodes(a.id().unwrap(), b.id().unwrap()) - .unwrap() + context.get_relative_location_of_nodes(a.id().unwrap(), b.id().unwrap()).unwrap() }); Some(nodes) } diff --git a/aderyn_core/src/context/browser/storage_vars.rs b/aderyn_core/src/context/browser/storage_vars.rs index 566d34aa0..e49b1dde5 100644 --- a/aderyn_core/src/context/browser/storage_vars.rs +++ b/aderyn_core/src/context/browser/storage_vars.rs @@ -11,44 +11,44 @@ use std::{ ops::Add, }; -/// Given an AST Block, it tries to detect any state variable inside it that may have been manipulated. -/// Now it's important to know that manipulations can occur either directly by assigning to a state variable -/// or, it may occur by assigning to a storage pointer that points to some state variable. -/// This light weight state variable manipulation finder captures both of the above kinds of assignments. -/// However, it's not smart enough to use a data dependency graph to determine the exact state variables -/// these storage pointers would be pointing to, in the context of the block-flow. +/// Given an AST Block, it tries to detect any state variable inside it that may have been +/// manipulated. Now it's important to know that manipulations can occur either directly by +/// assigning to a state variable or, it may occur by assigning to a storage pointer that points to +/// some state variable. This light weight state variable manipulation finder captures both of the +/// above kinds of assignments. However, it's not smart enough to use a data dependency graph to +/// determine the exact state variables these storage pointers would be pointing to, in the context +/// of the block-flow. /// -/// NOTE - Asignment is not the only avenue for manipulating state variables, but also operations like -/// `push()` and `pop()` on arrays, `M[i] = x` on mappings, `delete X` imply the same. +/// NOTE - Asignment is not the only avenue for manipulating state variables, but also operations +/// like `push()` and `pop()` on arrays, `M[i] = x` on mappings, `delete X` imply the same. /// /// Here, the term manipulation covers all kinds of changes discussed above. /// -/// IMPORTANT: DO NOT MAKE THESE MEMBERS PUBLIC. Use the public methods implemented on this structure only. +/// IMPORTANT: DO NOT MAKE THESE MEMBERS PUBLIC. Use the public methods implemented on this +/// structure only. pub struct ApproximateStorageChangeFinder<'a> { directly_manipulated_state_variables: BTreeSet, manipulated_storage_pointers: BTreeSet, - /// Key => State Variable ID, Value => Storage Pointer ID (Heuristics based, this map is NOT exhaustive) - /// It leaves out a lot of links especially in cases where storage pointers are passed to and fro internal functions. - /// But on average, for most cases the way code is generally written, this should contain a decent chunk of links to lookup. + /// Key => State Variable ID, Value => Storage Pointer ID (Heuristics based, this map is NOT + /// exhaustive) It leaves out a lot of links especially in cases where storage pointers are + /// passed to and fro internal functions. But on average, for most cases the way code is + /// generally written, this should contain a decent chunk of links to lookup. state_variables_to_storage_pointers: BTreeMap>, context: &'a WorkspaceContext, } -/// This trait implementation will be useful when we run it through our callgraph and try to aggregate state variable changes. +/// This trait implementation will be useful when we run it through our callgraph and try to +/// aggregate state variable changes. impl<'a> Add> for ApproximateStorageChangeFinder<'a> { type Output = ApproximateStorageChangeFinder<'a>; fn add(mut self, rhs: ApproximateStorageChangeFinder) -> Self::Output { self.directly_manipulated_state_variables .extend(rhs.directly_manipulated_state_variables.iter()); - self.manipulated_storage_pointers - .extend(rhs.manipulated_storage_pointers.iter()); + self.manipulated_storage_pointers.extend(rhs.manipulated_storage_pointers.iter()); // For state_variables_to_storage_pointers, we have to "add" the storage point entry vectors for (state_var_id, storage_pointer_ids) in &rhs.state_variables_to_storage_pointers { - match self - .state_variables_to_storage_pointers - .entry(*state_var_id) - { + match self.state_variables_to_storage_pointers.entry(*state_var_id) { std::collections::btree_map::Entry::Vacant(v) => { v.insert(BTreeSet::from_iter(storage_pointer_ids.iter().copied())); } @@ -64,11 +64,7 @@ impl<'a> Add> for ApproximateStorageChangeFin impl<'a> Debug for ApproximateStorageChangeFinder<'a> { // Do not print context. Hence, debug is custom derived for this struct fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - "Manipulated directly: {:?}", - self.directly_manipulated_state_variables - )?; + writeln!(f, "Manipulated directly: {:?}", self.directly_manipulated_state_variables)?; if !self.directly_manipulated_state_variables.is_empty() { writeln!(f, "↓↓")?; @@ -102,11 +98,7 @@ impl<'a> Debug for ApproximateStorageChangeFinder<'a> { writeln!(f)?; } - writeln!( - f, - "Links heuristics: {:?}", - self.state_variables_to_storage_pointers - )?; + writeln!(f, "Links heuristics: {:?}", self.state_variables_to_storage_pointers)?; if !self.state_variables_to_storage_pointers.is_empty() { writeln!(f, "↓↓")?; @@ -162,10 +154,7 @@ impl<'a> ApproximateStorageChangeFinder<'a> { manipulated_state_vars.extend(self.directly_manipulated_state_variables.iter()); for (state_variable_id, storage_pointers) in self.state_variables_to_storage_pointers.iter() { - if storage_pointers - .iter() - .any(|ptr| self.manipulated_storage_pointers.contains(ptr)) - { + if storage_pointers.iter().any(|ptr| self.manipulated_storage_pointers.contains(ptr)) { manipulated_state_vars.insert(*state_variable_id); } } @@ -190,20 +179,14 @@ impl<'a> ApproximateStorageChangeFinder<'a> { return Some(false); } // Now use our heuristics - if self - .state_variables_to_storage_pointers - .get(&var.id) - .is_some_and(|entry| { - entry - .iter() - .any(|e| self.manipulated_storage_pointers.contains(e)) - }) - { + if self.state_variables_to_storage_pointers.get(&var.id).is_some_and(|entry| { + entry.iter().any(|e| self.manipulated_storage_pointers.contains(e)) + }) { return Some(true); } - // At this point, we don't know if any of the storage pointers refer to [`var`], so we cannot say for - // sure, if it has been manipulated or not. + // At this point, we don't know if any of the storage pointers refer to [`var`], so we + // cannot say for sure, if it has been manipulated or not. None } @@ -218,20 +201,14 @@ impl<'a> ApproximateStorageChangeFinder<'a> { return Some(true); } // Now use our heuristics - if self - .state_variables_to_storage_pointers - .get(&var.id) - .is_some_and(|entry| { - entry - .iter() - .any(|e| self.manipulated_storage_pointers.contains(e)) - }) - { + if self.state_variables_to_storage_pointers.get(&var.id).is_some_and(|entry| { + entry.iter().any(|e| self.manipulated_storage_pointers.contains(e)) + }) { return Some(false); } - // At this point, we don't know if any of the storage pointers refer to [`var`], so we cannot say for - // sure, if it has been manipulated or not. + // At this point, we don't know if any of the storage pointers refer to [`var`], so we + // cannot say for sure, if it has been manipulated or not. None } } @@ -258,16 +235,12 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { } fn visit_member_access(&mut self, member: &MemberAccess) -> Result { - if !member - .expression - .type_descriptions() - .is_some_and(|type_desc| { - type_desc.type_string.as_ref().is_some_and(|type_string| { - type_string.ends_with("[] storage ref") - || type_string.ends_with("[] storage pointer") - }) + if !member.expression.type_descriptions().is_some_and(|type_desc| { + type_desc.type_string.as_ref().is_some_and(|type_string| { + type_string.ends_with("[] storage ref") + || type_string.ends_with("[] storage pointer") }) - { + }) { return Ok(true); } @@ -297,9 +270,10 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { let (base_variable_rhs_ids, _) = find_base(assignment.right_hand_side.as_ref()); for (id, type_string) in zip(base_variable_lhs_ids.iter(), type_strings.iter()) { - // When something is assigned to an expression of type "storage pointer", no state variable's value changes. - // The only value changed is the thing which the storage pointer points to. - // The value of a storage variable changes if the expression's type string contains "storage ref" in case of structs, arrays, etc + // When something is assigned to an expression of type "storage pointer", no state + // variable's value changes. The only value changed is the thing which the + // storage pointer points to. The value of a storage variable changes if the + // expression's type string contains "storage ref" in case of structs, arrays, etc if type_string.ends_with("storage pointer") { continue; @@ -316,9 +290,9 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { }; } - // Now, on a separate note, let's look for a heuristic to link up state variables with storage pointers. - // But here, we only handle the cases when there are equal number of elements on either side of `=` . - // This allows us to assume 1:1 relationship. + // Now, on a separate note, let's look for a heuristic to link up state variables with + // storage pointers. But here, we only handle the cases when there are equal number + // of elements on either side of `=` . This allows us to assume 1:1 relationship. if base_variable_lhs_ids.len() == base_variable_rhs_ids.len() { for (lhs_id, rhs_id) in zip(base_variable_lhs_ids, base_variable_rhs_ids) { if let ( @@ -347,16 +321,18 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { // let (left_node_ids, _) = find_base(assignment.left_hand_side.as_ref()); // let right_node_ids = flatten_expression(assignment.right_hand_side.as_ref()); - // See if it's a 1:1 relationship. Only then, proceed. Later, we can think of heuristics to handle x:y - // (but likely we will have to rely on a dependency graph for that. Case: `(x, y) = func()` is out of scope for this). + // See if it's a 1:1 relationship. Only then, proceed. Later, we can think of heuristics to + // handle x:y (but likely we will have to rely on a dependency graph for that. Case: + // `(x, y) = func()` is out of scope for this). // // When it comes to tracking WRITEs, it doesn't matter what's on RHS of `=` - // When it comes to tracking READs, it does! If the LHS type is storage then you are simply carrying a reference - // at compile time, you are not actually reading. Whereas, if the LHS is memory, you are performing "sload"! - // But again, this logic changes based on type of value in RHS. If it's a function call you should look at - // return values and nature of the corresponding variable where that value will be stored. Likewise, differernt - // for different nodes albeit identifier one is probably the most straightforward - // if left_node_ids.len() == right_node_ids.len() {} + // When it comes to tracking READs, it does! If the LHS type is storage then you are simply + // carrying a reference at compile time, you are not actually reading. Whereas, if + // the LHS is memory, you are performing "sload"! But again, this logic changes + // based on type of value in RHS. If it's a function call you should look at + // return values and nature of the corresponding variable where that value will be stored. + // Likewise, differernt for different nodes albeit identifier one is probably the + // most straightforward if left_node_ids.len() == right_node_ids.len() {} Ok(true) } @@ -385,18 +361,19 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { if corresponding_variable_declaration_ids.len() == initial_value_node_ids.len() { let common_len = corresponding_variable_declaration_ids.len(); - // This for loop takes care of recording instances in VDS (Var Decl Stmnt) where there is a read - // from the storage. + // This for loop takes care of recording instances in VDS (Var Decl Stmnt) where + // there is a read from the storage. // // READ HEURISTICS // // TODO: Write tests for these - // Then creates `passes_read_check()` before matching the var id on extracting refernce declarations - // Then replicate the logic for assignments + // Then creates `passes_read_check()` before matching the var id on extracting + // refernce declarations Then replicate the logic for assignments // use lvaluerequested = false and islvalue = true to check if it's being read // in case of visiting indexaccess, memberaccess, etc (expr_node! variants) - // So that it can detect stuff outside of just assignments. Example - functionCall(a.b) where a is state var - // (Technically you are reading a.b's value to make that function call) + // So that it can detect stuff outside of just assignments. Example - + // functionCall(a.b) where a is state var (Technically you are + // reading a.b's value to make that function call) // // for i in 0..common_len { // let variable_declaration_id = corresponding_variable_declaration_ids[i]; @@ -408,10 +385,11 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { // ) // .is_some() // { - // // If we are not assigning something to a storage pointer or a storage reference, that means - // // we're storing it in memory. Therefore, we can consider that the corresponding initialValue - // // is being "read". Otherwise we are just creating pointers, not "sload"ing. - // continue; + // // If we are not assigning something to a storage pointer or a storage + // reference, that means // we're storing it in memory. + // Therefore, we can consider that the corresponding initialValue + // // is being "read". Otherwise we are just creating pointers, not + // "sload"ing. continue; // } // if let Some(node) = self.context.nodes.get(&corresponding_initial_value_id) { @@ -425,8 +403,9 @@ impl<'a> ASTConstVisitor for ApproximateStorageChangeFinder<'a> { // self.context, // variable_id, // ) { - // // Assumption: At this point in code we know that it could be a storage pointer/variable that represents - // // uint, bool, array element, etc. so it's technically being read. + // // Assumption: At this point in code we know that it could be + // a storage pointer/variable that represents // + // uint, bool, array element, etc. so it's technically being read. // Some(AssigneeType::StateVariable) => { // self.directly_read_state_variables.insert(variable_id); // } @@ -502,11 +481,7 @@ fn find_base(expr: &Expression) -> (Vec, Vec) { match expr { Expression::Identifier(Identifier { referenced_declaration: Some(id), - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) => { node_ids.push(*id); @@ -515,11 +490,7 @@ fn find_base(expr: &Expression) -> (Vec, Vec) { // Handle mappings assignment Expression::IndexAccess(IndexAccess { base_expression, - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) => { node_ids.extend(find_base(base_expression.as_ref()).0); @@ -528,11 +499,7 @@ fn find_base(expr: &Expression) -> (Vec, Vec) { // Handle struct member assignment Expression::MemberAccess(MemberAccess { expression, - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) => { node_ids.extend(find_base(expression.as_ref()).0); @@ -554,11 +521,7 @@ fn find_base(expr: &Expression) -> (Vec, Vec) { // Handle assignment with values like ++i, --j, i++, etc Expression::UnaryOperation(UnaryOperation { sub_expression, - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) => { node_ids.extend(find_base(sub_expression.as_ref()).0); @@ -587,7 +550,8 @@ fn is_storage_variable_or_storage_pointer( if let ASTNode::VariableDeclaration(variable) = node { // Assumption // variable.state_variable is true when it's an actual state variable - // variable.storage_location is StorageLocation::Storage when it's a storage reference pointer + // variable.storage_location is StorageLocation::Storage when it's a storage reference + // pointer if variable.state_variable { return Some(AssigneeType::StateVariable); } else if variable.storage_location == StorageLocation::Storage { @@ -626,10 +590,7 @@ mod approximate_storage_change_finder_tests { let finder = ApproximateStorageChangeFinder::from(&context, func); let no_changes_found = !finder.state_variables_have_been_manipulated(); - println!( - "NoStateVarManipulationExample::dontManipulateStateVar()\n{:?}", - finder - ); + println!("NoStateVarManipulationExample::dontManipulateStateVar()\n{:?}", finder); println!("{:?}", finder); assert!(no_changes_found); } @@ -647,20 +608,14 @@ mod approximate_storage_change_finder_tests { let finder = ApproximateStorageChangeFinder::from(&context, func); let changes_found = finder.state_variables_have_been_manipulated(); - println!( - "SimpleStateVarManipulationExample::manipulateStateVarDirectly()\n{:?}", - finder - ); + println!("SimpleStateVarManipulationExample::manipulateStateVarDirectly()\n{:?}", finder); assert!(changes_found); assert_eq!(finder.directly_manipulated_state_variables.len(), 5); assert!(finder.manipulated_storage_pointers.is_empty()); let finder = ApproximateStorageChangeFinder::from(&context, func2); let changes_found = finder.state_variables_have_been_manipulated(); - println!( - "SimpleStateVarManipulationExample::readSimpleStateVars()\n{:?}", - finder - ); + println!("SimpleStateVarManipulationExample::readSimpleStateVars()\n{:?}", finder); assert!(!changes_found); assert!(finder.directly_manipulated_state_variables.is_empty()); assert!(finder.manipulated_storage_pointers.is_empty()); @@ -681,10 +636,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateDirectly() function let finder = ApproximateStorageChangeFinder::from(&context, func1); - println!( - "FixedSizeArraysAssignmentExample::manipulateDirectly()\n{:?}", - finder - ); + println!("FixedSizeArraysAssignmentExample::manipulateDirectly()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); @@ -694,10 +646,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateViaIndexAccess() function let finder = ApproximateStorageChangeFinder::from(&context, func2); - println!( - "FixedSizeArraysAssignmentExample::manipulateViaIndexAccess()\n{:?}", - finder - ); + println!("FixedSizeArraysAssignmentExample::manipulateViaIndexAccess()\n{:?}", finder); let changes_found2 = finder.state_variables_have_been_manipulated(); assert!(changes_found2); @@ -726,10 +675,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateStateVariables let finder = ApproximateStorageChangeFinder::from(&context, func); - println!( - "StructPlusFixedArrayAssignmentExample::manipulateStateVariables()\n{:?}", - finder - ); + println!("StructPlusFixedArrayAssignmentExample::manipulateStateVariables()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert_eq!(finder.directly_manipulated_state_variables.len(), 3); @@ -815,10 +761,7 @@ mod approximate_storage_change_finder_tests { // Test funcHelper let finder = ApproximateStorageChangeFinder::from(&context, func_helper); - println!( - "StructPlusFixedArrayAssignmentExample::manipulateHelper()\n{:?}", - finder - ); + println!("StructPlusFixedArrayAssignmentExample::manipulateHelper()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert_eq!(finder.manipulated_storage_pointers.len(), 2); @@ -999,10 +942,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateDirectly() let finder = ApproximateStorageChangeFinder::from(&context, func); - println!( - "DynamicArraysPushExample::manipulateDirectly()\n{:?}", - finder - ); + println!("DynamicArraysPushExample::manipulateDirectly()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert!(finder.manipulated_storage_pointers.is_empty()); @@ -1010,10 +950,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateViaIndexAccess() let finder = ApproximateStorageChangeFinder::from(&context, func2); - println!( - "DynamicArraysPushExample::manipulateViaIndexAccess()\n{:?}", - finder - ); + println!("DynamicArraysPushExample::manipulateViaIndexAccess()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert!(finder.manipulated_storage_pointers.is_empty()); @@ -1021,10 +958,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateViaMemberAccess() let finder = ApproximateStorageChangeFinder::from(&context, func3); - println!( - "DynamicArraysPushExample::manipulateViaMemberAccess()\n{:?}", - finder - ); + println!("DynamicArraysPushExample::manipulateViaMemberAccess()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert!(finder.manipulated_storage_pointers.is_empty()); @@ -1032,10 +966,7 @@ mod approximate_storage_change_finder_tests { // Test manipulateViaMemberAccess2() let finder = ApproximateStorageChangeFinder::from(&context, func4); - println!( - "DynamicArraysPushExample::manipulateViaMemberAccess2()\n{:?}", - finder - ); + println!("DynamicArraysPushExample::manipulateViaMemberAccess2()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert_eq!(finder.manipulated_storage_pointers.len(), 1); // we only want to capture p1 @@ -1074,10 +1005,7 @@ mod approximate_storage_change_finder_tests { // Test func() let finder = ApproximateStorageChangeFinder::from(&context, func); - println!( - "FixedSizeArraysDeletionExample::manipulateDirectly()\n{:?}", - finder - ); + println!("FixedSizeArraysDeletionExample::manipulateDirectly()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert_eq!(finder.directly_manipulated_state_variables.len(), 1); @@ -1085,10 +1013,7 @@ mod approximate_storage_change_finder_tests { // Test func2() let finder = ApproximateStorageChangeFinder::from(&context, func2); - println!( - "FixedSizeArraysDeletionExample::manipulateViaIndexAccess()\n{:?}", - finder - ); + println!("FixedSizeArraysDeletionExample::manipulateViaIndexAccess()\n{:?}", finder); let changes_found = finder.state_variables_have_been_manipulated(); assert!(changes_found); assert_eq!(finder.directly_manipulated_state_variables.len(), 2); @@ -1109,19 +1034,13 @@ mod storage_vars_tests_helper { impl WorkspaceContext { pub fn find_contract_by_name(&self, name: &str) -> &ContractDefinition { - self.contract_definitions() - .into_iter() - .find(|c| c.name.as_str() == name) - .unwrap() + self.contract_definitions().into_iter().find(|c| c.name.as_str() == name).unwrap() } } impl ContractDefinition { pub fn find_function_by_name(&self, name: &str) -> &FunctionDefinition { - self.function_definitions() - .iter() - .find(|func| func.name == name) - .unwrap() + self.function_definitions().iter().find(|func| func.name == name).unwrap() } pub fn find_state_variable_node_id_by_name(&self, name: &str) -> NodeID { diff --git a/aderyn_core/src/context/graph/callgraph.rs b/aderyn_core/src/context/graph/callgraph.rs index ccbaeedaa..ad89c06fd 100644 --- a/aderyn_core/src/context/graph/callgraph.rs +++ b/aderyn_core/src/context/graph/callgraph.rs @@ -2,8 +2,6 @@ //! //! Our first kind of callgraph is [`CallGraph`] it comes bundled with actions to help //! application modules "hook in" and consume the graphs. -//! -//! use std::collections::HashSet; @@ -35,8 +33,9 @@ pub struct CallGraph { pub entry_points: Vec, /// Surface points are calculated based on the entry points (input) - /// and only consists of [`crate::ast::FunctionDefinition`] and [`crate::ast::ModifierDefinition`] - /// These are nodes that are the *actual* starting points for traversal in the graph + /// and only consists of [`crate::ast::FunctionDefinition`] and + /// [`crate::ast::ModifierDefinition`] These are nodes that are the *actual* starting + /// points for traversal in the graph pub inward_surface_points: Vec, /// Same as the inward one, but acts on reverse graph. @@ -66,9 +65,8 @@ impl CallGraph { // Construct entry points for &node in nodes { - let node_id = node - .id() - .ok_or_else(|| super::Error::UnidentifiedEntryPointNode(node.clone()))?; + let node_id = + node.id().ok_or_else(|| super::Error::UnidentifiedEntryPointNode(node.clone()))?; entry_points.push(node_id); } @@ -98,11 +96,10 @@ impl CallGraph { outward_surface_points.push(id); } } else { - let parent_surface_point = node - .closest_ancestor_of_type(context, NodeType::FunctionDefinition) - .or_else(|| { - node.closest_ancestor_of_type(context, NodeType::ModifierDefinition) - }); + let parent_surface_point = + node.closest_ancestor_of_type(context, NodeType::FunctionDefinition).or_else( + || node.closest_ancestor_of_type(context, NodeType::ModifierDefinition), + ); if let Some(parent_surface_point) = parent_surface_point { if let Some(parent_surface_point_id) = parent_surface_point.id() { outward_surface_points.push(parent_surface_point_id); @@ -111,12 +108,7 @@ impl CallGraph { } } - Ok(CallGraph { - entry_points, - inward_surface_points, - outward_surface_points, - direction, - }) + Ok(CallGraph { entry_points, inward_surface_points, outward_surface_points, direction }) } pub fn new( @@ -127,30 +119,24 @@ impl CallGraph { Self::from_nodes(context, nodes, direction) } - /// Visit the entry points and all the plausible function definitions and modifier definitions that - /// EVM may encounter during execution. + /// Visit the entry points and all the plausible function definitions and modifier definitions + /// that EVM may encounter during execution. pub fn accept(&self, context: &WorkspaceContext, visitor: &mut T) -> super::Result<()> where T: CallGraphVisitor, { self._accept( context, - context - .inward_callgraph - .as_ref() - .ok_or(super::Error::InwardCallgraphNotAvailable)?, - context - .outward_callgraph - .as_ref() - .ok_or(super::Error::OutwardCallgraphNotAvailable)?, + context.inward_callgraph.as_ref().ok_or(super::Error::InwardCallgraphNotAvailable)?, + context.outward_callgraph.as_ref().ok_or(super::Error::OutwardCallgraphNotAvailable)?, visitor, ) } /// Responsible for informing the trackers. - /// First, we visit the entry points. Then, we derive the subgraph from the [`WorkspaceCallGraph`] - /// which consists of all the nodes that can be reached by traversing the edges starting - /// from the surface points. + /// First, we visit the entry points. Then, we derive the subgraph from the + /// [`WorkspaceCallGraph`] which consists of all the nodes that can be reached by traversing + /// the edges starting from the surface points. fn _accept( &self, context: &WorkspaceContext, @@ -161,7 +147,8 @@ impl CallGraph { where T: CallGraphVisitor, { - // Visit entry point nodes (so that trackers can track the state across all code regions in 1 place) + // Visit entry point nodes (so that trackers can track the state across all code regions in + // 1 place) for entry_point_id in &self.entry_points { self.make_entry_point_visit_call(context, *entry_point_id, visitor)?; } @@ -331,18 +318,14 @@ impl CallGraph { } CurrentDFSVector::OutwardSideEffect => { if let ASTNode::FunctionDefinition(function) = node { - visitor - .visit_outward_side_effect_function_definition(function) - .map_err(|_| { - super::Error::OutwardSideEffectFunctionDefinitionVisitError - })?; + visitor.visit_outward_side_effect_function_definition(function).map_err( + |_| super::Error::OutwardSideEffectFunctionDefinitionVisitError, + )?; } if let ASTNode::ModifierDefinition(modifier) = node { - visitor - .visit_outward_side_effect_modifier_definition(modifier) - .map_err(|_| { - super::Error::OutwardSideEffectModifierDefinitionVisitError - })?; + visitor.visit_outward_side_effect_modifier_definition(modifier).map_err( + |_| super::Error::OutwardSideEffectModifierDefinitionVisitError, + )?; } } } @@ -360,13 +343,8 @@ impl CallGraph { where T: CallGraphVisitor, { - let node = context - .nodes - .get(&node_id) - .ok_or(super::Error::InvalidEntryPointId(node_id))?; - visitor - .visit_entry_point(node) - .map_err(|_| super::Error::EntryPointVisitError)?; + let node = context.nodes.get(&node_id).ok_or(super::Error::InvalidEntryPointId(node_id))?; + visitor.visit_entry_point(node).map_err(|_| super::Error::EntryPointVisitError)?; Ok(()) } } diff --git a/aderyn_core/src/context/graph/callgraph_tests.rs b/aderyn_core/src/context/graph/callgraph_tests.rs index 0e7b68796..2855e79ef 100644 --- a/aderyn_core/src/context/graph/callgraph_tests.rs +++ b/aderyn_core/src/context/graph/callgraph_tests.rs @@ -15,21 +15,13 @@ mod callgraph_test_functions { fn get_function_by_name(context: &WorkspaceContext, name: &str) -> ASTNode { ASTNode::from( - context - .function_definitions() - .into_iter() - .find(|&x| x.name == *name) - .unwrap(), + context.function_definitions().into_iter().find(|&x| x.name == *name).unwrap(), ) } fn get_modifier_definition_by_name(context: &WorkspaceContext, name: &str) -> ASTNode { ASTNode::from( - context - .modifier_definitions() - .into_iter() - .find(|&x| x.name == *name) - .unwrap(), + context.modifier_definitions().into_iter().find(|&x| x.name == *name).unwrap(), ) } @@ -181,91 +173,75 @@ mod callgraph_test_functions { // inward functions fn has_found_inward_functions_with_names(&self, name: &[&str]) -> bool { - name.iter() - .all(|&n| self.inward_func_definitions_names.contains(&n.to_string())) + name.iter().all(|&n| self.inward_func_definitions_names.contains(&n.to_string())) } // outward functions fn has_found_outward_functions_with_names(&self, name: &[&str]) -> bool { - name.iter() - .all(|&n| self.outward_func_definitions_names.contains(&n.to_string())) + name.iter().all(|&n| self.outward_func_definitions_names.contains(&n.to_string())) } fn has_not_found_any_outward_functions_with_name(&self, name: &str) -> bool { - !self - .outward_func_definitions_names - .contains(&name.to_string()) + !self.outward_func_definitions_names.contains(&name.to_string()) } // outward modifiers fn has_found_outward_modifiers_with_names(&self, name: &[&str]) -> bool { - name.iter().all(|&n| { - self.outward_modifier_definitions_names - .contains(&n.to_string()) - }) + name.iter().all(|&n| self.outward_modifier_definitions_names.contains(&n.to_string())) } // outward side effects fn has_found_outward_side_effect_functions_with_name(&self, name: &[&str]) -> bool { - name.iter().all(|&n| { - self.outward_side_effects_func_definitions_names - .contains(&n.to_string()) - }) + name.iter() + .all(|&n| self.outward_side_effects_func_definitions_names.contains(&n.to_string())) } } impl CallGraphVisitor for Tracker<'_> { fn visit_entry_point(&mut self, node: &ASTNode) -> eyre::Result<()> { - self.entry_points - .push(self.context.get_node_sort_key_pure(node)); + self.entry_points.push(self.context.get_node_sort_key_pure(node)); Ok(()) } fn visit_inward_function_definition( &mut self, node: &crate::ast::FunctionDefinition, ) -> eyre::Result<()> { - self.inward_func_definitions_names - .push(node.name.to_string()); + self.inward_func_definitions_names.push(node.name.to_string()); Ok(()) } fn visit_inward_modifier_definition( &mut self, node: &crate::ast::ModifierDefinition, ) -> eyre::Result<()> { - self.inward_modifier_definitions_names - .push(node.name.to_string()); + self.inward_modifier_definitions_names.push(node.name.to_string()); Ok(()) } fn visit_outward_function_definition( &mut self, node: &crate::ast::FunctionDefinition, ) -> eyre::Result<()> { - self.outward_func_definitions_names - .push(node.name.to_string()); + self.outward_func_definitions_names.push(node.name.to_string()); Ok(()) } fn visit_outward_modifier_definition( &mut self, node: &crate::ast::ModifierDefinition, ) -> eyre::Result<()> { - self.outward_modifier_definitions_names - .push(node.name.to_string()); + self.outward_modifier_definitions_names.push(node.name.to_string()); Ok(()) } fn visit_outward_side_effect_function_definition( &mut self, node: &FunctionDefinition, ) -> eyre::Result<()> { - self.outward_side_effects_func_definitions_names - .push(node.name.to_string()); + self.outward_side_effects_func_definitions_names.push(node.name.to_string()); Ok(()) } fn visit_outward_side_effect_modifier_definition( &mut self, node: &ModifierDefinition, ) -> eyre::Result<()> { - self.outward_side_effects_modifier_definitions_names - .push(node.name.to_string()); + self.outward_side_effects_modifier_definitions_names.push(node.name.to_string()); Ok(()) } } diff --git a/aderyn_core/src/context/graph/traits.rs b/aderyn_core/src/context/graph/traits.rs index f6476f49b..5bc5bdea9 100644 --- a/aderyn_core/src/context/graph/traits.rs +++ b/aderyn_core/src/context/graph/traits.rs @@ -16,22 +16,26 @@ pub trait CallGraphVisitor { self.visit_any(node) } - /// Meant to be invoked while traversing [`crate::context::workspace_context::WorkspaceContext::inward_callgraph`] + /// Meant to be invoked while traversing + /// [`crate::context::workspace_context::WorkspaceContext::inward_callgraph`] fn visit_inward_function_definition(&mut self, node: &FunctionDefinition) -> eyre::Result<()> { self.visit_any(&(node.into())) } - /// Meant to be invoked while traversing [`crate::context::workspace_context::WorkspaceContext::outward_callgraph`] + /// Meant to be invoked while traversing + /// [`crate::context::workspace_context::WorkspaceContext::outward_callgraph`] fn visit_outward_function_definition(&mut self, node: &FunctionDefinition) -> eyre::Result<()> { self.visit_any(&(node.into())) } - /// Meant to be invoked while traversing [`crate::context::workspace_context::WorkspaceContext::inward_callgraph`] + /// Meant to be invoked while traversing + /// [`crate::context::workspace_context::WorkspaceContext::inward_callgraph`] fn visit_inward_modifier_definition(&mut self, node: &ModifierDefinition) -> eyre::Result<()> { self.visit_any(&(node.into())) } - /// Meant to be invoked while traversing [`crate::context::workspace_context::WorkspaceContext::outward_callgraph`] + /// Meant to be invoked while traversing + /// [`crate::context::workspace_context::WorkspaceContext::outward_callgraph`] fn visit_outward_modifier_definition(&mut self, node: &ModifierDefinition) -> eyre::Result<()> { self.visit_any(&(node.into())) } diff --git a/aderyn_core/src/context/graph/workspace_callgraph.rs b/aderyn_core/src/context/graph/workspace_callgraph.rs index 95f12fe51..6c222a548 100644 --- a/aderyn_core/src/context/graph/workspace_callgraph.rs +++ b/aderyn_core/src/context/graph/workspace_callgraph.rs @@ -16,8 +16,9 @@ pub struct WorkspaceCallGraph { } /** -* Every NodeID in RawCallGraph should corresponds to [`crate::ast::FunctionDefinition`] or [`crate::ast::ModifierDefinition`] -*/ + * Every NodeID in RawCallGraph should corresponds to [`crate::ast::FunctionDefinition`] or + * [`crate::ast::ModifierDefinition`] + */ pub type RawCallGraph = HashMap>; impl WorkspaceCallGraph { @@ -48,8 +49,8 @@ impl WorkspaceCallGraph { } } -/// Make connections from each of the nodes of [`crate::ast::FunctionDefinition`] and [`crate::ast::ModifierDefinition`] -/// with their connected counterparts. +/// Make connections from each of the nodes of [`crate::ast::FunctionDefinition`] and +/// [`crate::ast::ModifierDefinition`] with their connected counterparts. fn dfs_to_create_graph( id: NodeID, raw_callgraph: &mut RawCallGraph, @@ -128,7 +129,8 @@ fn create_connection_if_not_exsits( ) { match raw_callgraph.entry(from_id) { hash_map::Entry::Occupied(mut o) => { - // Performance Tip: Maybe later use binary search (it requires keeping ascending order while inserting tho) + // Performance Tip: Maybe later use binary search (it requires keeping ascending order + // while inserting tho) if !o.get().contains(&to_id) { o.get_mut().push(to_id); } diff --git a/aderyn_core/src/context/meta_workspace.rs b/aderyn_core/src/context/meta_workspace.rs index 49561b7cb..bec1f9908 100644 --- a/aderyn_core/src/context/meta_workspace.rs +++ b/aderyn_core/src/context/meta_workspace.rs @@ -1,5 +1,4 @@ -use crate::ast::*; -use crate::WorkspaceContext; +use crate::{ast::*, WorkspaceContext}; use super::macros::generate_get_source_unit; @@ -26,9 +25,7 @@ impl WorkspaceContext { self.elementary_type_names_context.keys().collect() } pub fn elementary_type_name_expressions(&self) -> Vec<&ElementaryTypeNameExpression> { - self.elementary_type_name_expressions_context - .keys() - .collect() + self.elementary_type_name_expressions_context.keys().collect() } pub fn emit_statements(&self) -> Vec<&EmitStatement> { self.emit_statements_context.keys().collect() @@ -150,9 +147,7 @@ impl WorkspaceContext { self.user_defined_type_names_context.keys().collect() } pub fn user_defined_value_type_definitions(&self) -> Vec<&UserDefinedValueTypeDefinition> { - self.user_defined_value_type_definitions_context - .keys() - .collect() + self.user_defined_value_type_definitions_context.keys().collect() } pub fn using_for_directives(&self) -> Vec<&UsingForDirective> { self.using_for_directives_context.keys().collect() @@ -161,9 +156,7 @@ impl WorkspaceContext { self.variable_declarations_context.keys().collect() } pub fn variable_declaration_statements(&self) -> Vec<&VariableDeclarationStatement> { - self.variable_declaration_statements_context - .keys() - .collect() + self.variable_declaration_statements_context.keys().collect() } pub fn while_statements(&self) -> Vec<&WhileStatement> { self.while_statements_context.keys().collect() diff --git a/aderyn_core/src/context/workspace_context.rs b/aderyn_core/src/context/workspace_context.rs index 8baaa76ec..62e74f954 100644 --- a/aderyn_core/src/context/workspace_context.rs +++ b/aderyn_core/src/context/workspace_context.rs @@ -1,11 +1,7 @@ -use crate::ast::*; -use crate::fscloc::cloc::IgnoreLine; -use std::cmp::Ordering; -use std::collections::HashMap; +use crate::{ast::*, fscloc::cloc::IgnoreLine}; +use std::{cmp::Ordering, collections::HashMap}; -use super::browser::GetImmediateParent; -use super::capturable::Capturable; -use super::graph::WorkspaceCallGraph; +use super::{browser::GetImmediateParent, capturable::Capturable, graph::WorkspaceCallGraph}; pub use crate::ast::ASTNode; #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] @@ -237,10 +233,8 @@ impl WorkspaceContext { pub fn get_node_sort_key(&self, node: &ASTNode) -> (String, usize, String) { let source_unit = self.get_source_unit_from_child_node(node).unwrap(); let absolute_path = source_unit.absolute_path.as_ref().unwrap().clone(); - let source_line = node - .src() - .map(|src| source_unit.source_line(src).unwrap_or(0)) - .unwrap_or(0); + let source_line = + node.src().map(|src| source_unit.source_line(src).unwrap_or(0)).unwrap_or(0); let src_location = match node { ASTNode::ContractDefinition(contract_node) => contract_node diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 33a403ef2..9002caecf 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -26,7 +26,7 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), - Box::::default(), + Box::::default(), Box::::default(), Box::::default(), Box::::default(), @@ -101,6 +101,7 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), ] } @@ -113,6 +114,7 @@ pub fn get_all_detectors_names() -> Vec { #[derive(Debug, PartialEq, EnumString, Display)] #[strum(serialize_all = "kebab-case")] pub(crate) enum IssueDetectorNamePool { + StateVariableCouldBeDeclaredImmutable, MultiplePlaceholders, StateVariableChangesWithoutEvents, MissingInheritance, @@ -122,8 +124,8 @@ pub(crate) enum IssueDetectorNamePool { FunctionPointerInConstructor, DeadCode, FunctionSelectorCollision, - CacheArrayLength, - AssertStateChange, + ArrayLengthNotCached, + StateChangeInAssert, CostlyOperationsInsideLoops, ConstantFunctionChangingState, BuiltinSymbolShadow, @@ -132,18 +134,18 @@ pub(crate) enum IssueDetectorNamePool { DelegateCallInLoop, CentralizationRisk, SolmateSafeTransferLib, - AvoidAbiEncodePacked, - Ecrecover, + HashCollisionDueToAbiEncodePacked, + SignatureMalleabilityDueToRawEcrecover, DeprecatedOzFunctions, UnsafeERC20Functions, UnspecificSolidityPragma, - ZeroAddressCheck, + NoZeroAddressCheck, UselessPublicFunction, - ConstantsInsteadOfLiterals, UnindexedEvents, - RequireWithString, - NonReentrantBeforeOthers, - BlockTimestampDeadline, + RequireWithoutString, + NonReentrantIsNotBeforeOthers, + BlockTimestampIsWeakDeadline, + LiteralInsteadOfConstant, UnsafeOzERC721Mint, PushZeroOpcode, ArbitraryTransferFrom, @@ -173,8 +175,8 @@ pub(crate) enum IssueDetectorNamePool { StateVariableShadowing, UncheckedSend, MisusedBoolean, - SendEtherNoChecks, - DelegateCallUncheckedAddress, + SendsEtherAwayWithoutCheckingAddress, + DelegateCallOnUncheckedAddress, TautologicalCompare, #[allow(clippy::upper_case_acronyms)] RTLO, @@ -190,7 +192,7 @@ pub(crate) enum IssueDetectorNamePool { DeleteNestedMapping, UnusedStateVariable, ConstantFunctionsAssembly, - BooleanEquality, + RedundantBooleanEquality, TxOriginUsedForAuth, MsgValueInLoop, ContractLocksEther, @@ -209,6 +211,9 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { + Some(Box::::default()) + } IssueDetectorNamePool::MultiplePlaceholders => { Some(Box::::default()) } @@ -226,6 +231,9 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } + IssueDetectorNamePool::LiteralInsteadOfConstant => { + Some(Box::::default()) + } IssueDetectorNamePool::FunctionPointerInConstructor => { Some(Box::::default()) } @@ -233,8 +241,10 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::CacheArrayLength => Some(Box::::default()), - IssueDetectorNamePool::AssertStateChange => { + IssueDetectorNamePool::ArrayLengthNotCached => { + Some(Box::::default()) + } + IssueDetectorNamePool::StateChangeInAssert => { Some(Box::::default()) } IssueDetectorNamePool::CostlyOperationsInsideLoops => { @@ -274,10 +284,12 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::AvoidAbiEncodePacked => { + IssueDetectorNamePool::HashCollisionDueToAbiEncodePacked => { Some(Box::::default()) } - IssueDetectorNamePool::Ecrecover => Some(Box::::default()), + IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover => { + Some(Box::::default()) + } IssueDetectorNamePool::DeprecatedOzFunctions => { Some(Box::::default()) } @@ -287,21 +299,20 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::ZeroAddressCheck => Some(Box::::default()), + IssueDetectorNamePool::NoZeroAddressCheck => { + Some(Box::::default()) + } IssueDetectorNamePool::UselessPublicFunction => { Some(Box::::default()) } - IssueDetectorNamePool::ConstantsInsteadOfLiterals => { - Some(Box::::default()) - } IssueDetectorNamePool::UnindexedEvents => Some(Box::::default()), - IssueDetectorNamePool::RequireWithString => { + IssueDetectorNamePool::RequireWithoutString => { Some(Box::::default()) } - IssueDetectorNamePool::NonReentrantBeforeOthers => { + IssueDetectorNamePool::NonReentrantIsNotBeforeOthers => { Some(Box::::default()) } - IssueDetectorNamePool::BlockTimestampDeadline => { + IssueDetectorNamePool::BlockTimestampIsWeakDeadline => { Some(Box::::default()) } IssueDetectorNamePool::UnsafeOzERC721Mint => { @@ -378,10 +389,10 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option Some(Box::::default()), IssueDetectorNamePool::MisusedBoolean => Some(Box::::default()), - IssueDetectorNamePool::SendEtherNoChecks => { + IssueDetectorNamePool::SendsEtherAwayWithoutCheckingAddress => { Some(Box::::default()) } - IssueDetectorNamePool::DelegateCallUncheckedAddress => { + IssueDetectorNamePool::DelegateCallOnUncheckedAddress => { Some(Box::::default()) } IssueDetectorNamePool::TautologicalCompare => { @@ -417,7 +428,9 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::BooleanEquality => Some(Box::::default()), + IssueDetectorNamePool::RedundantBooleanEquality => { + Some(Box::::default()) + } IssueDetectorNamePool::TxOriginUsedForAuth => { Some(Box::::default()) } diff --git a/aderyn_core/src/detect/experimental/ancestral_line.rs b/aderyn_core/src/detect/experimental/ancestral_line.rs index 537934cb1..1dcd7fa9d 100644 --- a/aderyn_core/src/detect/experimental/ancestral_line.rs +++ b/aderyn_core/src/detect/experimental/ancestral_line.rs @@ -108,10 +108,7 @@ mod ancestral_line_demo_tests { assert!(found); println!("{:?}", detector.instances()); - println!( - "Total number of instances: {:?}", - detector.instances().len() - ); + println!("Total number of instances: {:?}", detector.instances().len()); assert!(detector.instances().len() == 4); } } diff --git a/aderyn_core/src/detect/experimental/closest_ancestor.rs b/aderyn_core/src/detect/experimental/closest_ancestor.rs index d529620a6..d476cc1f6 100644 --- a/aderyn_core/src/detect/experimental/closest_ancestor.rs +++ b/aderyn_core/src/detect/experimental/closest_ancestor.rs @@ -92,10 +92,7 @@ mod closest_ancestor_demo_tests { assert!(found); println!("{:?}", detector.instances()); - println!( - "Total number of instances: {:?}", - detector.instances().len() - ); + println!("Total number of instances: {:?}", detector.instances().len()); assert!(detector.instances().len() == 4); } } diff --git a/aderyn_core/src/detect/experimental/immediate_children.rs b/aderyn_core/src/detect/experimental/immediate_children.rs index 2cd7e172e..d08e090ec 100644 --- a/aderyn_core/src/detect/experimental/immediate_children.rs +++ b/aderyn_core/src/detect/experimental/immediate_children.rs @@ -92,10 +92,7 @@ mod child_chain_demo_tests { assert!(found); println!("{:?}", detector.instances()); - println!( - "Total number of instances: {:?}", - detector.instances().len() - ); + println!("Total number of instances: {:?}", detector.instances().len()); assert!(detector.instances().len() == 1); } } diff --git a/aderyn_core/src/detect/experimental/immediate_parent.rs b/aderyn_core/src/detect/experimental/immediate_parent.rs index a0f4d2c1c..dc5aeb030 100644 --- a/aderyn_core/src/detect/experimental/immediate_parent.rs +++ b/aderyn_core/src/detect/experimental/immediate_parent.rs @@ -109,10 +109,7 @@ mod parent_chain_demo_tests { assert!(found); println!("{:?}", detector.instances()); - println!( - "Total number of instances: {:?}", - detector.instances().len() - ); + println!("Total number of instances: {:?}", detector.instances().len()); assert!(detector.instances().len() == 3); } } diff --git a/aderyn_core/src/detect/helpers.rs b/aderyn_core/src/detect/helpers.rs index 8daa10b56..17a41ac75 100644 --- a/aderyn_core/src/detect/helpers.rs +++ b/aderyn_core/src/detect/helpers.rs @@ -44,14 +44,10 @@ pub fn get_calls_and_delegate_calls(context: &WorkspaceContext) -> Vec<&MemberAc pub fn get_implemented_external_and_public_functions( context: &WorkspaceContext, ) -> impl Iterator { - context - .function_definitions() - .into_iter() - .filter(|function| { - (function.visibility == Visibility::Public - || function.visibility == Visibility::External) - && function.implemented - }) + context.function_definitions().into_iter().filter(|function| { + (function.visibility == Visibility::Public || function.visibility == Visibility::External) + && function.implemented + }) } pub fn pragma_directive_to_semver(pragma_directive: &PragmaDirective) -> Result { @@ -81,24 +77,16 @@ pub fn pragma_directive_to_semver(pragma_directive: &PragmaDirective) -> Result< // ``` pub fn has_msg_sender_binary_operation(ast_node: &ASTNode) -> bool { // Directly return the evaluation of the condition - ExtractBinaryOperations::from(ast_node) - .extracted - .iter() - .any(|binary_operation| { - ExtractMemberAccesses::from(binary_operation) - .extracted - .iter() - .any(|member_access| { - member_access.member_name == "sender" - && if let Expression::Identifier(identifier) = - member_access.expression.as_ref() - { - identifier.name == "msg" - } else { - false - } - }) + ExtractBinaryOperations::from(ast_node).extracted.iter().any(|binary_operation| { + ExtractMemberAccesses::from(binary_operation).extracted.iter().any(|member_access| { + member_access.member_name == "sender" + && if let Expression::Identifier(identifier) = member_access.expression.as_ref() { + identifier.name == "msg" + } else { + false + } }) + }) } // Check if an ast_node sends native eth @@ -129,12 +117,16 @@ pub fn has_calls_that_sends_native_eth(ast_node: &ASTNode) -> bool { // payable(address(..)).transfer(100) // payable(address(..)).send(100) + // address.sendValue(..) (from openzeppelin) let function_calls = ExtractFunctionCalls::from(ast_node).extracted; for function_call in function_calls { if let Expression::MemberAccess(member_access) = function_call.expression.as_ref() { - if member_access.member_name == "transfer" || member_access.member_name == "send" { + if member_access.member_name == "transfer" + || member_access.member_name == "send" + || member_access.member_name == "sendValue" + { if let Some(type_description) = member_access.expression.type_descriptions() { if type_description .type_string @@ -272,19 +264,12 @@ Expression::UnaryOperation with ! operator followed by a sub expression that cou pub fn is_constant_boolean(context: &WorkspaceContext, ast_node: &Expression) -> bool { if let Expression::Literal(literal) = ast_node { if literal.kind == LiteralKind::Bool - && literal - .value - .as_ref() - .is_some_and(|value| value == "false" || value == "true") + && literal.value.as_ref().is_some_and(|value| value == "false" || value == "true") { return true; } } - if let Expression::Identifier(Identifier { - referenced_declaration: Some(id), - .. - }) = ast_node - { + if let Expression::Identifier(Identifier { referenced_declaration: Some(id), .. }) = ast_node { if let Some(ASTNode::VariableDeclaration(variable_declaration)) = context.nodes.get(id) { if variable_declaration .type_descriptions diff --git a/aderyn_core/src/detect/high/arbitrary_transfer_from.rs b/aderyn_core/src/detect/high/arbitrary_transfer_from.rs index 2c6ffc12d..2588e59c5 100644 --- a/aderyn_core/src/detect/high/arbitrary_transfer_from.rs +++ b/aderyn_core/src/detect/high/arbitrary_transfer_from.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, FunctionCall, NodeID, TypeName}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -46,22 +44,20 @@ fn check_argument_validity(function_call: &FunctionCall) -> bool { impl IssueDetector for ArbitraryTransferFromDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { let transfer_from_function_calls = - context - .function_calls() - .into_iter() - .filter(|&function_call| { - // For each function call, check if the function call is a member access - // and if the member name is "transferFrom" or "safeTransferFrom", then check if the first argument is valid - // If the first argument is valid, add the function call to found_instances - if let Expression::MemberAccess(member_access) = &*function_call.expression { - if member_access.member_name == "transferFrom" - || member_access.member_name == "safeTransferFrom" - { - return check_argument_validity(function_call); - } + context.function_calls().into_iter().filter(|&function_call| { + // For each function call, check if the function call is a member access + // and if the member name is "transferFrom" or "safeTransferFrom", then check if the + // first argument is valid If the first argument is valid, add the + // function call to found_instances + if let Expression::MemberAccess(member_access) = &*function_call.expression { + if member_access.member_name == "transferFrom" + || member_access.member_name == "safeTransferFrom" + { + return check_argument_validity(function_call); } - false - }); + } + false + }); for item in transfer_from_function_calls { capture!(self, context, item); @@ -113,10 +109,7 @@ mod arbitrary_transfer_from_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs b/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs index b7cb5ae31..fc1b18a3b 100644 --- a/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs +++ b/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs @@ -18,24 +18,18 @@ pub struct AvoidAbiEncodePackedDetector { impl IssueDetector for AvoidAbiEncodePackedDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for member_access in context.member_accesses() { - // If the member_access's member_name = "encodePacked", loop through the argument_types and count how many of them contain any of the following in type_strings: + // If the member_access's member_name = "encodePacked", loop through the argument_types + // and count how many of them contain any of the following in type_strings: // ["bytes ", "[]", "string"] - // If the count is greater than 1, add the member_access to the found_abi_encode_packed vector + // If the count is greater than 1, add the member_access to the found_abi_encode_packed + // vector if member_access.member_name == "encodePacked" { let mut count = 0; let argument_types = member_access.argument_types.as_ref().unwrap(); for argument_type in argument_types { - if argument_type - .type_string - .as_ref() - .unwrap() - .contains("bytes ") + if argument_type.type_string.as_ref().unwrap().contains("bytes ") || argument_type.type_string.as_ref().unwrap().contains("[]") - || argument_type - .type_string - .as_ref() - .unwrap() - .contains("string") + || argument_type.type_string.as_ref().unwrap().contains("string") { count += 1; } @@ -67,7 +61,7 @@ impl IssueDetector for AvoidAbiEncodePackedDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::AvoidAbiEncodePacked) + format!("{}", IssueDetectorNamePool::HashCollisionDueToAbiEncodePacked) } } @@ -94,10 +88,7 @@ mod avoid_abi_encode_packed_tests { // failure0, failure1 and failure3 assert_eq!(detector.instances().len(), 3); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/block_timestamp_deadline.rs b/aderyn_core/src/detect/high/block_timestamp_deadline.rs index 55e8f4310..f824ae660 100644 --- a/aderyn_core/src/detect/high/block_timestamp_deadline.rs +++ b/aderyn_core/src/detect/high/block_timestamp_deadline.rs @@ -19,14 +19,16 @@ impl IssueDetector for BlockTimestampDeadlineDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for call in context.function_calls() { // Uniswap V2 - Function Calls - // For each FunctionCall, if the Expression is a MemberAccess that is named any of the following: - // [ - // swapExactTokensForTokens, swapTokensForExactTokens, swapExactETHForTokens, swapTokensForExactETH, - // swapExactTokensForETH, swapETHForExactTokens, swapExactTokensForTokensSupportingFeeOnTransferTokens, - // swapExactETHForTokensSupportingFeeOnTransferTokens, swapExactTokensForETHSupportingFeeOnTransferTokens - // ] - // If the last FunctionCall argument is a MemberAccess identifier with member_name "timestamp", - // and the MemberAccess expression.name is "block", add the node to the found_block_timestamp_deadlines vector. + // For each FunctionCall, if the Expression is a MemberAccess that is named any of the + // following: [ + // swapExactTokensForTokens, swapTokensForExactTokens, swapExactETHForTokens, + // swapTokensForExactETH, swapExactTokensForETH, swapETHForExactTokens, + // swapExactTokensForTokensSupportingFeeOnTransferTokens, + // swapExactETHForTokensSupportingFeeOnTransferTokens, + // swapExactTokensForETHSupportingFeeOnTransferTokens ] + // If the last FunctionCall argument is a MemberAccess identifier with member_name + // "timestamp", and the MemberAccess expression.name is "block", add the + // node to the found_block_timestamp_deadlines vector. if let Expression::MemberAccess(ref member_access) = *call.expression { if member_access.member_name == "swapExactTokensForTokens" || member_access.member_name == "swapTokensForExactTokens" @@ -57,12 +59,13 @@ impl IssueDetector for BlockTimestampDeadlineDetector { } } // Uniswap V3 - Function Calls - // For each FunctionCall, if it is of kind StructConstructorCall, where the call's Expression has a name of any of the following: - // [ + // For each FunctionCall, if it is of kind StructConstructorCall, where the call's + // Expression has a name of any of the following: [ // ExactInputSingleParams, ExactInputParams, ExactOutputSingleParams, ExactOutputParams // ] - // If any of the call's arguments is a MemberAccess identifier with member_name "timestamp", - // and the MemberAccess expression.name is "block", add the node to the found_block_timestamp_deadlines vector. + // If any of the call's arguments is a MemberAccess identifier with member_name + // "timestamp", and the MemberAccess expression.name is "block", add the + // node to the found_block_timestamp_deadlines vector. if call.kind == FunctionCallKind::StructConstructorCall { if let Expression::Identifier(ref identifier) = *call.expression { if identifier.name == "ExactInputSingleParams" @@ -110,7 +113,7 @@ impl IssueDetector for BlockTimestampDeadlineDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::BlockTimestampDeadline) + format!("{}", IssueDetectorNamePool::BlockTimestampIsWeakDeadline) } } @@ -134,10 +137,7 @@ mod block_timestamp_deadline_detector_tests { // assert that the number of instances found is correct assert_eq!(detector.instances().len(), 9); // assert that the severity is High - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert that the title is correct assert_eq!( detector.title(), @@ -167,10 +167,7 @@ mod block_timestamp_deadline_detector_tests { // assert that the number of instances found is correct assert_eq!(detector.instances().len(), 8); // assert that the severity is High - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/const_func_change_state.rs b/aderyn_core/src/detect/high/const_func_change_state.rs index db0e07258..007277d40 100644 --- a/aderyn_core/src/detect/high/const_func_change_state.rs +++ b/aderyn_core/src/detect/high/const_func_change_state.rs @@ -1,16 +1,18 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, StateMutability}; -use crate::capture; -use crate::context::browser::ApproximateStorageChangeFinder; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ApproximateStorageChangeFinder, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -33,10 +35,7 @@ impl IssueDetector for ConstantFunctionChangingStateDetector { continue; } // Now, investigate the function to see if there is scope for any state variable changes - let mut tracker = StateVariableChangeTracker { - state_var_has_changed: false, - context, - }; + let mut tracker = StateVariableChangeTracker { state_var_has_changed: false, context }; let callgraph = CallGraph::new(context, &[&(func.into())], CallGraphDirection::Inward)?; callgraph.accept(context, &mut tracker)?; @@ -159,9 +158,6 @@ mod constant_func_changing_state { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/contract_locks_ether.rs b/aderyn_core/src/detect/high/contract_locks_ether.rs index dbf2eef0c..d9a04465e 100644 --- a/aderyn_core/src/detect/high/contract_locks_ether.rs +++ b/aderyn_core/src/detect/high/contract_locks_ether.rs @@ -1,15 +1,13 @@ use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{convert::identity, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -26,9 +24,7 @@ impl IssueDetector for ContractLocksEtherDetector { for contract in context.contract_definitions() { // If a contract can accept eth, but doesn't allow for withdrawal capture it! if contract.can_accept_eth(context).is_some_and(identity) - && !contract - .allows_withdrawal_of_eth(context) - .is_some_and(identity) + && !contract.allows_withdrawal_of_eth(context).is_some_and(identity) { capture!(self, context, contract); } @@ -126,9 +122,10 @@ mod contract_eth_helper { } } } - // At this point we have successfully gone through all the contracts in the inheritance heirarchy - // but tracker has determined that none of them have have calls that sends native eth Even if they are by - // some chance, they are not reachable from external & public functions + // At this point we have successfully gone through all the contracts in the inheritance + // heirarchy but tracker has determined that none of them have have calls + // that sends native eth Even if they are by some chance, they are not + // reachable from external & public functions Some(false) } } @@ -175,9 +172,6 @@ mod contract_locks_ether_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs b/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs index aa13aaab7..30579c598 100644 --- a/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs +++ b/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -36,19 +34,18 @@ impl IssueDetector for DangerousStrictEqualityOnBalanceDetector { ] { if let Expression::MemberAccess(member_access) = expr { if member_access.member_name == "balance" - && member_access - .expression - .as_ref() - .type_descriptions() - .is_some_and(|type_desc| { + && member_access.expression.as_ref().type_descriptions().is_some_and( + |type_desc| { type_desc.type_string.as_ref().is_some_and(|type_string| { - // For older solc versions when you say this.balance, "this" is of type contract XXX + // For older solc versions when you say this.balance, "this" is + // of type contract XXX type_string.starts_with("contract ") // In newers solidity versions, you say adddress(this).balance or payable(address(this)).balance || type_string == "address" || type_string == "address payable" }) - }) + }, + ) { capture!(self, context, binary_operation); } @@ -103,10 +100,7 @@ mod dangerous_strict_equality_balance_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } #[test] @@ -122,9 +116,6 @@ mod dangerous_strict_equality_balance_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/dangerous_unary_operator.rs b/aderyn_core/src/detect/high/dangerous_unary_operator.rs index 3d079bc97..683fa77a4 100644 --- a/aderyn_core/src/detect/high/dangerous_unary_operator.rs +++ b/aderyn_core/src/detect/high/dangerous_unary_operator.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::context::browser::Peek; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::Peek, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -78,15 +75,9 @@ mod dangerous_unary_expression_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Dangerous unary operator found in assignment.") - ); + assert_eq!(detector.title(), String::from("Dangerous unary operator found in assignment.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/delegate_call_in_loop.rs b/aderyn_core/src/detect/high/delegate_call_in_loop.rs index d265b683d..808a85edc 100644 --- a/aderyn_core/src/detect/high/delegate_call_in_loop.rs +++ b/aderyn_core/src/detect/high/delegate_call_in_loop.rs @@ -1,5 +1,4 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::{ ast::{ASTNode, NodeID}, @@ -116,15 +115,9 @@ mod delegate_call_in_loop_detector_tests { // assert that the detector found the correct number of instances (1) assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Using `delegatecall` in loop") - ); + assert_eq!(detector.title(), String::from("Using `delegatecall` in loop")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/delegate_call_no_address_check.rs b/aderyn_core/src/detect/high/delegate_call_no_address_check.rs index 77d9ef8f7..358066770 100644 --- a/aderyn_core/src/detect/high/delegate_call_no_address_check.rs +++ b/aderyn_core/src/detect/high/delegate_call_no_address_check.rs @@ -1,16 +1,18 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; use crate::capture; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + context::{ + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -47,7 +49,7 @@ impl IssueDetector for DelegateCallOnUncheckedAddressDetector { } fn title(&self) -> String { - String::from("Delegatecall made by the function without checks on any adress.") + String::from("Delegatecall made by the function without checks on any address.") } fn description(&self) -> String { @@ -59,7 +61,7 @@ impl IssueDetector for DelegateCallOnUncheckedAddressDetector { } fn name(&self) -> String { - IssueDetectorNamePool::DelegateCallUncheckedAddress.to_string() + IssueDetectorNamePool::DelegateCallOnUncheckedAddress.to_string() } } @@ -111,20 +113,14 @@ mod delegate_call_no_address_check_tests { assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), - String::from("Delegatecall made by the function without checks on any adress.") + String::from("Delegatecall made by the function without checks on any address.") ); // assert the description is correct - assert_eq!( - detector.description(), - String::from("Introduce checks on the address") - ); + assert_eq!(detector.description(), String::from("Introduce checks on the address")); } } diff --git a/aderyn_core/src/detect/high/deletion_nested_mapping.rs b/aderyn_core/src/detect/high/deletion_nested_mapping.rs index 8676956e2..ad29f02ce 100644 --- a/aderyn_core/src/detect/high/deletion_nested_mapping.rs +++ b/aderyn_core/src/detect/high/deletion_nested_mapping.rs @@ -1,16 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ ASTNode, Expression, Identifier, IndexAccess, Mapping, NodeID, TypeName, UserDefinedTypeName, VariableDeclaration, }; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -23,14 +21,11 @@ pub struct DeletionNestedMappingDetector { impl IssueDetector for DeletionNestedMappingDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - for delete_operation in context - .unary_operations() - .into_iter() - .filter(|op| op.operator == "delete") + for delete_operation in + context.unary_operations().into_iter().filter(|op| op.operator == "delete") { - if let Expression::IndexAccess(IndexAccess { - base_expression, .. - }) = delete_operation.sub_expression.as_ref() + if let Expression::IndexAccess(IndexAccess { base_expression, .. }) = + delete_operation.sub_expression.as_ref() { if let Expression::Identifier(Identifier { referenced_declaration: Some(referenced_id), @@ -44,7 +39,8 @@ impl IssueDetector for DeletionNestedMappingDetector { .as_ref() .is_some_and(|type_string| type_string.starts_with("mapping")) { - // Check if the value in the mapping is of type struct that has a member which is also a mapping + // Check if the value in the mapping is of type struct that has a member + // which is also a mapping if let Some(ASTNode::VariableDeclaration(VariableDeclaration { type_name: Some(TypeName::Mapping(Mapping { value_type, .. })), .. @@ -120,15 +116,9 @@ mod deletion_nested_mapping_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Deletion from a nested mappping.") - ); + assert_eq!(detector.title(), String::from("Deletion from a nested mappping.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/dynamic_array_length_assignment.rs b/aderyn_core/src/detect/high/dynamic_array_length_assignment.rs index f1abb1ea0..7aa6c8d38 100644 --- a/aderyn_core/src/detect/high/dynamic_array_length_assignment.rs +++ b/aderyn_core/src/detect/high/dynamic_array_length_assignment.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -95,15 +93,9 @@ mod dynamic_array_length_assignment_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 5); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Array length value has a direct assignment.") - ); + assert_eq!(detector.title(), String::from("Array length value has a direct assignment.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/enumerable_loop_removal.rs b/aderyn_core/src/detect/high/enumerable_loop_removal.rs index 3e0ddeb5a..c8d28c45a 100644 --- a/aderyn_core/src/detect/high/enumerable_loop_removal.rs +++ b/aderyn_core/src/detect/high/enumerable_loop_removal.rs @@ -1,14 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, NodeType}; -use crate::capture; -use crate::context::browser::{ExtractMemberAccesses, GetClosestAncestorOfTypeX}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractMemberAccesses, GetClosestAncestorOfTypeX}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -21,8 +21,8 @@ pub struct EnumerableLoopRemovalDetector { impl IssueDetector for EnumerableLoopRemovalDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - // Find MemberAccesses with name `remove` and typeDescriptions.typeString.contains(EnumerableSet) - // for each one + // Find MemberAccesses with name `remove` and + // typeDescriptions.typeString.contains(EnumerableSet) for each one // Find the closest ancestor of a loop // if it exists, extract all `at` member accesses on the enumerableset // If an `at` memberaccess also exists in the loop, add the remove to found_instances @@ -45,14 +45,13 @@ impl IssueDetector for EnumerableLoopRemovalDetector { member_access.closest_ancestor_of_type(context, NodeType::DoWhileStatement), ]; for parent_loop in parent_loops.into_iter().flatten() { - ExtractMemberAccesses::from(parent_loop) - .extracted - .into_iter() - .for_each(|at_member_access| { + ExtractMemberAccesses::from(parent_loop).extracted.into_iter().for_each( + |at_member_access| { if at_member_access.member_name == "at" { capture!(self, context, member_access); } - }); + }, + ); } }); @@ -101,9 +100,6 @@ mod enuemrable_loop_removal_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 5); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/experimental_encoder.rs b/aderyn_core/src/detect/high/experimental_encoder.rs index 7752b1e5c..a59478958 100644 --- a/aderyn_core/src/detect/high/experimental_encoder.rs +++ b/aderyn_core/src/detect/high/experimental_encoder.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -74,10 +72,7 @@ mod storage_array_encode_compiler_bug_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!(detector.title(), String::from("Experimental ABI Encoder")); // assert the description is correct diff --git a/aderyn_core/src/detect/high/function_selector_collision.rs b/aderyn_core/src/detect/high/function_selector_collision.rs index bfd8b9b46..1c0045c64 100644 --- a/aderyn_core/src/detect/high/function_selector_collision.rs +++ b/aderyn_core/src/detect/high/function_selector_collision.rs @@ -1,13 +1,14 @@ -use std::collections::{BTreeMap, HashMap}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashMap}, + error::Error, +}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -25,7 +26,8 @@ impl IssueDetector for FunctionSelectorCollisionDetector { let mut selectors: HashMap>> = HashMap::new(); // PLAN - // If we have > 1 function_name entries for any function_selector, then capture all the corresponding NodeIDs + // If we have > 1 function_name entries for any function_selector, then capture all the + // corresponding NodeIDs for function in context.function_definitions() { if let Some(selector) = function.function_selector.as_ref() { @@ -52,7 +54,8 @@ impl IssueDetector for FunctionSelectorCollisionDetector { for function_entries in selectors.values() { if function_entries.len() >= 2 { - // Now we know that there is a collision + at least 2 different function names found for that selector. + // Now we know that there is a collision + at least 2 different function names found + // for that selector. // Capture the relevant functions for (function_name, function_ids) in function_entries { @@ -129,9 +132,6 @@ mod function_signature_collision { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/incorrect_caret_operator.rs b/aderyn_core/src/detect/high/incorrect_caret_operator.rs index 51a3381fc..7ea4be052 100644 --- a/aderyn_core/src/detect/high/incorrect_caret_operator.rs +++ b/aderyn_core/src/detect/high/incorrect_caret_operator.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, LiteralKind, Mutability, NodeID}; -use crate::capture; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::workspace_context::{ASTNode, WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -22,13 +19,12 @@ pub struct IncorrectUseOfCaretOperatorDetector { impl IssueDetector for IncorrectUseOfCaretOperatorDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // Copied Heuristic from Slither: - // look for binary expressions with ^ operator where at least one of the operands is a constant, and - // # the constant is not in hex, because hex typically is used with bitwise xor and not exponentiation + // look for binary expressions with ^ operator where at least one of the operands is a + // constant, and # the constant is not in hex, because hex typically is used with + // bitwise xor and not exponentiation - for binary_operation in context - .binary_operations() - .into_iter() - .filter(|op| op.operator == "^") + for binary_operation in + context.binary_operations().into_iter().filter(|op| op.operator == "^") { for expr in [ binary_operation.left_expression.as_ref(), @@ -110,10 +106,7 @@ mod incorrect_use_of_caret_operator_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 5); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/incorrect_erc20_interface.rs b/aderyn_core/src/detect/high/incorrect_erc20_interface.rs index abdad7151..c466aeb78 100644 --- a/aderyn_core/src/detect/high/incorrect_erc20_interface.rs +++ b/aderyn_core/src/detect/high/incorrect_erc20_interface.rs @@ -1,15 +1,11 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{ASTNode, NodeID, Visibility}; -use crate::capture; -use crate::context::browser::ExtractFunctionDefinitions; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractFunctionDefinitions, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -150,10 +146,8 @@ mod erc_matching_function_signature_helper { // ERC20 function signature matching impl FunctionDefinition { pub fn represents_erc20_transfer(&self) -> Option { - let satisifer = SignatureMatcher { - name: "transfer", - paramter_types: vec!["address", "uint256"], - }; + let satisifer = + SignatureMatcher { name: "transfer", paramter_types: vec!["address", "uint256"] }; satisifer.satisfies(self) } @@ -166,34 +160,24 @@ mod erc_matching_function_signature_helper { } pub fn represents_erc20_approve(&self) -> Option { - let satisifer = SignatureMatcher { - name: "approve", - paramter_types: vec!["address", "uint256"], - }; + let satisifer = + SignatureMatcher { name: "approve", paramter_types: vec!["address", "uint256"] }; satisifer.satisfies(self) } pub fn represents_erc20_allowance(&self) -> Option { - let satisifer = SignatureMatcher { - name: "allowance", - paramter_types: vec!["address", "address"], - }; + let satisifer = + SignatureMatcher { name: "allowance", paramter_types: vec!["address", "address"] }; satisifer.satisfies(self) } pub fn represents_erc20_balance_of(&self) -> Option { - let satisifer = SignatureMatcher { - name: "balanceOf", - paramter_types: vec!["address"], - }; + let satisifer = SignatureMatcher { name: "balanceOf", paramter_types: vec!["address"] }; satisifer.satisfies(self) } pub fn represents_erc20_total_supply(&self) -> Option { - let satisifer = SignatureMatcher { - name: "totalSupply", - paramter_types: vec![], - }; + let satisifer = SignatureMatcher { name: "totalSupply", paramter_types: vec![] }; satisifer.satisfies(self) } } @@ -221,9 +205,6 @@ mod incorrect_erc20_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 5); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/incorrect_erc721_interface.rs b/aderyn_core/src/detect/high/incorrect_erc721_interface.rs index 39d20da7d..f15dc1f40 100644 --- a/aderyn_core/src/detect/high/incorrect_erc721_interface.rs +++ b/aderyn_core/src/detect/high/incorrect_erc721_interface.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, NodeID, Visibility}; -use crate::capture; -use crate::context::browser::ExtractFunctionDefinitions; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractFunctionDefinitions, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -163,10 +160,8 @@ mod erc721_matching_function_signature_helper { // ERC721 function signature matching impl FunctionDefinition { pub fn represents_erc721_get_approved(&self) -> bool { - let satisifer = SignatureMatcher { - name: "getApproved", - paramter_types: vec!["uint256"], - }; + let satisifer = + SignatureMatcher { name: "getApproved", paramter_types: vec!["uint256"] }; satisifer.satisfies(self) } @@ -179,10 +174,8 @@ mod erc721_matching_function_signature_helper { } pub fn represents_erc721_approve(&self) -> bool { - let satisifer = SignatureMatcher { - name: "approve", - paramter_types: vec!["address", "uint256"], - }; + let satisifer = + SignatureMatcher { name: "approve", paramter_types: vec!["address", "uint256"] }; satisifer.satisfies(self) } @@ -195,18 +188,12 @@ mod erc721_matching_function_signature_helper { } pub fn represents_erc721_balance_of(&self) -> bool { - let satisifer = SignatureMatcher { - name: "balanceOf", - paramter_types: vec!["address"], - }; + let satisifer = SignatureMatcher { name: "balanceOf", paramter_types: vec!["address"] }; satisifer.satisfies(self) } pub fn represents_erc721_owner_of(&self) -> bool { - let satisifer = SignatureMatcher { - name: "ownerOf", - paramter_types: vec!["uint256"], - }; + let satisifer = SignatureMatcher { name: "ownerOf", paramter_types: vec!["uint256"] }; satisifer.satisfies(self) } @@ -262,13 +249,9 @@ mod erc721_matching_function_signature_helper { pub fn returns_address(&self) -> bool { let params = &self.return_parameters.parameters; params.len() == 1 - && params[0] - .type_descriptions - .type_string - .as_ref() - .is_some_and(|type_string| { - type_string == "address" || type_string == "address payable" - }) + && params[0].type_descriptions.type_string.as_ref().is_some_and(|type_string| { + type_string == "address" || type_string == "address payable" + }) } } } @@ -291,7 +274,8 @@ mod incorrect_erc721_tests { let mut detector = IncorrectERC721InterfaceDetector::default(); let found = detector.detect(&context).unwrap(); - // We capture every faulty method in the IncorrectERC721 contract that has the wrong return type + // We capture every faulty method in the IncorrectERC721 contract that has the wrong return + // type println!("{:#?}", detector.instances()); // assert that the detector found an issue @@ -301,9 +285,6 @@ mod incorrect_erc721_tests { assert_eq!(detector.instances().len(), 8); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/incorrect_shift_order.rs b/aderyn_core/src/detect/high/incorrect_shift_order.rs index c96ced6c0..3ab680595 100644 --- a/aderyn_core/src/detect/high/incorrect_shift_order.rs +++ b/aderyn_core/src/detect/high/incorrect_shift_order.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, YulExpression}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -77,15 +75,9 @@ mod incorrect_shift_order_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Incorrect Assembly Shift Parameter Order") - ); + assert_eq!(detector.title(), String::from("Incorrect Assembly Shift Parameter Order")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/misused_boolean.rs b/aderyn_core/src/detect/high/misused_boolean.rs index a726674f0..a22beaab6 100644 --- a/aderyn_core/src/detect/high/misused_boolean.rs +++ b/aderyn_core/src/detect/high/misused_boolean.rs @@ -1,5 +1,4 @@ -use crate::detect::helpers::is_constant_boolean; -use crate::issue_detector; +use crate::{detect::helpers::is_constant_boolean, issue_detector}; use eyre::Result; issue_detector! { diff --git a/aderyn_core/src/detect/high/msg_value_in_loops.rs b/aderyn_core/src/detect/high/msg_value_in_loops.rs index d8644961a..f52216e8a 100644 --- a/aderyn_core/src/detect/high/msg_value_in_loops.rs +++ b/aderyn_core/src/detect/high/msg_value_in_loops.rs @@ -1,16 +1,15 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{ASTNode, Expression, NodeID}; -use crate::capture; -use crate::context::browser::ExtractMemberAccesses; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractMemberAccesses, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -84,19 +83,15 @@ struct MsgValueTracker { impl CallGraphVisitor for MsgValueTracker { fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> { if !self.has_msg_value - && ExtractMemberAccesses::from(node) - .extracted - .iter() - .any(|member_access| { - member_access.member_name == "value" - && if let Expression::Identifier(identifier) = - member_access.expression.as_ref() - { - identifier.name == "msg" - } else { - false - } - }) + && ExtractMemberAccesses::from(node).extracted.iter().any(|member_access| { + member_access.member_name == "value" + && if let Expression::Identifier(identifier) = member_access.expression.as_ref() + { + identifier.name == "msg" + } else { + false + } + }) { self.has_msg_value = true; } @@ -127,9 +122,6 @@ mod msg_value_in_loop_detector { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/multiple_constructors.rs b/aderyn_core/src/detect/high/multiple_constructors.rs index 4175b4686..e24d576e7 100644 --- a/aderyn_core/src/detect/high/multiple_constructors.rs +++ b/aderyn_core/src/detect/high/multiple_constructors.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::context::browser::ExtractFunctionDefinitions; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractFunctionDefinitions, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -82,15 +79,9 @@ mod multiple_constructors_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Contract Has Multiple Constructors") - ); + assert_eq!(detector.title(), String::from("Contract Has Multiple Constructors")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/nested_struct_in_mapping.rs b/aderyn_core/src/detect/high/nested_struct_in_mapping.rs index 3d1313654..d18d8fb3a 100644 --- a/aderyn_core/src/detect/high/nested_struct_in_mapping.rs +++ b/aderyn_core/src/detect/high/nested_struct_in_mapping.rs @@ -1,16 +1,17 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, NodeType, TypeName}; -use crate::capture; -use crate::context::browser::ExtractPragmaDirectives; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers::pragma_directive_to_semver; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractPragmaDirectives, + workspace_context::{ASTNode, WorkspaceContext}, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::pragma_directive_to_semver, + }, }; use eyre::Result; use semver::VersionReq; @@ -61,7 +62,8 @@ impl IssueDetector for NestedStructInMappingDetector { if let Some(member_type_string) = &member.type_descriptions.type_string { if member_type_string.contains("struct") { - // Check if the contract that this is in allows for solidity pragma below 0.5.0 + // Check if the contract that this is in allows for solidity + // pragma below 0.5.0 let source_unit_ast_node = context .get_closest_ancestor(mapping.id, NodeType::SourceUnit); if let Some(source_unit_ast_node) = source_unit_ast_node { @@ -127,15 +129,9 @@ mod nested_struct_in_mapping_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Nested Structs in Mappings pre-0.5.0") - ); + assert_eq!(detector.title(), String::from("Nested Structs in Mappings pre-0.5.0")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/out_of_order_retryable.rs b/aderyn_core/src/detect/high/out_of_order_retryable.rs index 433a70cd1..542120a9c 100644 --- a/aderyn_core/src/detect/high/out_of_order_retryable.rs +++ b/aderyn_core/src/detect/high/out_of_order_retryable.rs @@ -1,16 +1,18 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, MemberAccess, NodeID}; -use crate::capture; -use crate::context::browser::ExtractFunctionCalls; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractFunctionCalls, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -24,9 +26,7 @@ pub struct OutOfOrderRetryableDetector { impl IssueDetector for OutOfOrderRetryableDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for func in helpers::get_implemented_external_and_public_functions(context) { - let mut tracker = OutOfOrderRetryableTracker { - number_of_retry_calls: 0, - }; + let mut tracker = OutOfOrderRetryableTracker { number_of_retry_calls: 0 }; let callgraph = CallGraph::new(context, &[&(func.into())], CallGraphDirection::Inward)?; callgraph.accept(context, &mut tracker)?; if tracker.number_of_retry_calls >= 2 { @@ -65,11 +65,8 @@ struct OutOfOrderRetryableTracker { number_of_retry_calls: usize, } -const SEQUENCER_FUNCTIONS: [&str; 3] = [ - "createRetryableTicket", - "outboundTransferCustomRefund", - "unsafeCreateRetryableTicket", -]; +const SEQUENCER_FUNCTIONS: [&str; 3] = + ["createRetryableTicket", "outboundTransferCustomRefund", "unsafeCreateRetryableTicket"]; impl CallGraphVisitor for OutOfOrderRetryableTracker { fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> { @@ -112,9 +109,6 @@ mod out_of_order_retryable_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/pre_declared_variable_usage.rs b/aderyn_core/src/detect/high/pre_declared_variable_usage.rs index 0873765b1..239aaba21 100644 --- a/aderyn_core/src/detect/high/pre_declared_variable_usage.rs +++ b/aderyn_core/src/detect/high/pre_declared_variable_usage.rs @@ -1,15 +1,20 @@ -use std::collections::{BTreeMap, HashSet}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; use crate::ast::{ASTNode, NodeID}; -use crate::capture; -use crate::context::browser::{ExtractIdentifiers, ExtractVariableDeclarations}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractIdentifiers, ExtractVariableDeclarations}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -29,11 +34,7 @@ pub struct PreDeclaredLocalVariableUsageDetector { impl IssueDetector for PreDeclaredLocalVariableUsageDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // Since this is restricted to local variables, we examine each function independently - for function in context - .function_definitions() - .into_iter() - .filter(|&f| f.implemented) - { + for function in context.function_definitions().into_iter().filter(|&f| f.implemented) { let local_variable_declaration_ids = ExtractVariableDeclarations::from(function) .extracted .iter() @@ -45,11 +46,9 @@ impl IssueDetector for PreDeclaredLocalVariableUsageDetector { let used_local_variables = used_local_variables .iter() .filter(|identifier| { - identifier - .referenced_declaration - .is_some_and(|referenced_declaration| { - local_variable_declaration_ids.contains(&referenced_declaration) - }) + identifier.referenced_declaration.is_some_and(|referenced_declaration| { + local_variable_declaration_ids.contains(&referenced_declaration) + }) }) .collect::>(); @@ -121,15 +120,9 @@ mod pre_declared_variable_usage_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Usage of variable before declaration.") - ); + assert_eq!(detector.title(), String::from("Usage of variable before declaration.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/reused_contract_name.rs b/aderyn_core/src/detect/high/reused_contract_name.rs index 9a828124f..e73ec1b54 100644 --- a/aderyn_core/src/detect/high/reused_contract_name.rs +++ b/aderyn_core/src/detect/high/reused_contract_name.rs @@ -1,13 +1,14 @@ -use std::collections::{BTreeMap, HashMap}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashMap}, + error::Error, +}; use crate::ast::{ContractDefinition, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -24,10 +25,7 @@ impl IssueDetector for ReusedContractNameDetector { // Simplify the map filling process using the Entry API for contract in context.contract_definitions() { - contract_names - .entry(&contract.name) - .or_default() - .push(contract); + contract_names.entry(&contract.name).or_default().push(contract); } // Process duplicate contracts @@ -85,15 +83,9 @@ mod reused_contract_name_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Contract Name Reused in Different Files") - ); + assert_eq!(detector.title(), String::from("Contract Name Reused in Different Files")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/rtlo.rs b/aderyn_core/src/detect/high/rtlo.rs index 83577d0fe..5edfd7d9e 100644 --- a/aderyn_core/src/detect/high/rtlo.rs +++ b/aderyn_core/src/detect/high/rtlo.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -77,15 +75,9 @@ mod rtlo_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("RTLO character detected in file. \\u{202e}") - ); + assert_eq!(detector.title(), String::from("RTLO character detected in file. \\u{202e}")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/selfdestruct.rs b/aderyn_core/src/detect/high/selfdestruct.rs index 57132c156..55e413f07 100644 --- a/aderyn_core/src/detect/high/selfdestruct.rs +++ b/aderyn_core/src/detect/high/selfdestruct.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -69,10 +67,7 @@ mod selfdestruct_identifier_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/send_ether_no_checks.rs b/aderyn_core/src/detect/high/send_ether_no_checks.rs index 364097d27..174fcf4a8 100644 --- a/aderyn_core/src/detect/high/send_ether_no_checks.rs +++ b/aderyn_core/src/detect/high/send_ether_no_checks.rs @@ -1,16 +1,17 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::{ASTNode, WorkspaceContext}, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -53,7 +54,7 @@ impl IssueDetector for SendEtherNoChecksDetector { } fn name(&self) -> String { - IssueDetectorNamePool::SendEtherNoChecks.to_string() + IssueDetectorNamePool::SendsEtherAwayWithoutCheckingAddress.to_string() } } @@ -79,10 +80,7 @@ mod send_ether_no_checks_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 3); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/state_variable_shadowing.rs b/aderyn_core/src/detect/high/state_variable_shadowing.rs index 2823a4823..3d13c5fb8 100644 --- a/aderyn_core/src/detect/high/state_variable_shadowing.rs +++ b/aderyn_core/src/detect/high/state_variable_shadowing.rs @@ -1,21 +1,25 @@ -use std::collections::{BTreeMap, HashMap}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashMap}, + error::Error, +}; use crate::ast::{ ContractDefinition, Mutability, NodeID, NodeType, UserDefinedTypeNameOrIdentifierPath, VariableDeclaration, }; -use crate::capture; -use crate::context::browser::{ - ExtractPragmaDirectives, ExtractVariableDeclarations, GetClosestAncestorOfTypeX, -}; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers::pragma_directive_to_semver; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ + ExtractPragmaDirectives, ExtractVariableDeclarations, GetClosestAncestorOfTypeX, + }, + workspace_context::{ASTNode, WorkspaceContext}, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::pragma_directive_to_semver, + }, }; use eyre::Result; use semver::VersionReq; @@ -26,7 +30,8 @@ use semver::VersionReq; // Preprocessing that would make this detector more efficient: // 1. Inheritance/Extension Tree -// 2. Solc version based detector assignment (if solc version < 0.6.0, run this detector on the workspace context) +// 2. Solc version based detector assignment (if solc version < 0.6.0, run this detector on the +// workspace context) #[derive(Default)] pub struct StateVariableShadowingDetector { @@ -41,15 +46,11 @@ fn are_duplicate_names_in_inherited_contracts( contract_definition: &ContractDefinition, // Use reference to avoid cloning ) -> bool { // Check for duplicate variable names in the current contract - if ExtractVariableDeclarations::from(contract_definition) - .extracted - .iter() - .any(|vd| { - vd.state_variable - && vd.mutability() != Some(&Mutability::Constant) - && vd.name == variable_name - }) - { + if ExtractVariableDeclarations::from(contract_definition).extracted.iter().any(|vd| { + vd.state_variable + && vd.mutability() != Some(&Mutability::Constant) + && vd.name == variable_name + }) { return true; // Return immediately if a duplicate is found } @@ -67,9 +68,8 @@ fn are_duplicate_names_in_inherited_contracts( } } UserDefinedTypeNameOrIdentifierPath::IdentifierPath(identifier_path) => { - if let Some(ASTNode::ContractDefinition(contract)) = context - .nodes - .get(&(identifier_path.referenced_declaration as i64)) + if let Some(ASTNode::ContractDefinition(contract)) = + context.nodes.get(&(identifier_path.referenced_declaration as i64)) { if are_duplicate_names_in_inherited_contracts(context, variable_name, contract) { @@ -220,10 +220,7 @@ mod state_variable_shadowing_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/storage_array_edit_with_memory.rs b/aderyn_core/src/detect/high/storage_array_edit_with_memory.rs index 512aa191d..6057dd0fc 100644 --- a/aderyn_core/src/detect/high/storage_array_edit_with_memory.rs +++ b/aderyn_core/src/detect/high/storage_array_edit_with_memory.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, StorageLocation}; -use crate::capture; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::workspace_context::{ASTNode, WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -33,18 +30,13 @@ impl IssueDetector for StorageArrayEditWithMemoryDetector { .into_iter() .filter(|identifier| identifier.argument_types.is_some()) { - for (index, argument_type) in identifier - .argument_types - .as_ref() - .unwrap() - .iter() - .enumerate() + for (index, argument_type) in + identifier.argument_types.as_ref().unwrap().iter().enumerate() { if let Some(type_string) = &argument_type.type_string { if type_string.contains("storage ref") { - let definition_ast = context - .nodes - .get(&identifier.referenced_declaration.unwrap()); + let definition_ast = + context.nodes.get(&identifier.referenced_declaration.unwrap()); if let Some(ASTNode::FunctionDefinition(definition)) = definition_ast { let parameter = definition .parameters @@ -107,15 +99,9 @@ mod storage_array_edit_with_memory_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Storage Array Edited with Memory") - ); + assert_eq!(detector.title(), String::from("Storage Array Edited with Memory")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/storage_signed_integer_array.rs b/aderyn_core/src/detect/high/storage_signed_integer_array.rs index e1acde91d..c62ba583e 100644 --- a/aderyn_core/src/detect/high/storage_signed_integer_array.rs +++ b/aderyn_core/src/detect/high/storage_signed_integer_array.rs @@ -1,20 +1,19 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::str::FromStr; +use std::{collections::BTreeMap, error::Error, str::FromStr}; use crate::ast::{ ASTNode, Expression, Identifier, NodeID, TupleExpression, TypeDescriptions, UnaryOperation, }; -use crate::capture; -use crate::context::browser::{ - ExtractPragmaDirectives, ExtractTupleExpressions, GetImmediateParent, -}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractPragmaDirectives, ExtractTupleExpressions, GetImmediateParent}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; use lazy_regex::regex; @@ -127,11 +126,7 @@ fn is_tuple_being_assigned_to_storage_array( ) -> bool { if let Some(ASTNode::Assignment(assignment)) = tuple_expression.parent(context) { if let Expression::Identifier(Identifier { - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) = assignment.left_hand_side.as_ref() { @@ -171,10 +166,7 @@ mod storage_signed_array_detector { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/tautological_compare.rs b/aderyn_core/src/detect/high/tautological_compare.rs index 57d91dfaa..9ee25ec7a 100644 --- a/aderyn_core/src/detect/high/tautological_compare.rs +++ b/aderyn_core/src/detect/high/tautological_compare.rs @@ -1,14 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, Identifier, MemberAccess, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers::get_literal_value_or_constant_variable_value; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::get_literal_value_or_constant_variable_value, + }, }; use eyre::Result; @@ -22,9 +22,7 @@ pub struct TautologicalCompareDetector { impl IssueDetector for TautologicalCompareDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for binary_operation in context.binary_operations().into_iter().filter(|binary_op| { - ["&&", "||", ">=", ">", "<=", "<"] - .into_iter() - .any(|op| op == binary_op.operator) + ["&&", "||", ">=", ">", "<=", "<"].into_iter().any(|op| op == binary_op.operator) }) { let orientations = [ ( @@ -43,12 +41,10 @@ impl IssueDetector for TautologicalCompareDetector { ) { ( Expression::Identifier(Identifier { - referenced_declaration: Some(id0), - .. + referenced_declaration: Some(id0), .. }), Expression::Identifier(Identifier { - referenced_declaration: Some(id1), - .. + referenced_declaration: Some(id1), .. }), ) | ( @@ -185,10 +181,7 @@ mod tautological_compare_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!(detector.title(), String::from("Tautological comparison.")); // assert the description is correct diff --git a/aderyn_core/src/detect/high/tautology_or_contradiction.rs b/aderyn_core/src/detect/high/tautology_or_contradiction.rs index 953c14c34..f4f3972e3 100644 --- a/aderyn_core/src/detect/high/tautology_or_contradiction.rs +++ b/aderyn_core/src/detect/high/tautology_or_contradiction.rs @@ -1,14 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{BinaryOperation, NodeID, TypeDescriptions}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers::get_literal_value_or_constant_variable_value; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::get_literal_value_or_constant_variable_value, + }, }; use eyre::Result; use solidity_integer_helper::{ @@ -80,15 +80,9 @@ mod tautology_or_contradiction_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Tautology or Contradiction in comparison.") - ); + assert_eq!(detector.title(), String::from("Tautology or Contradiction in comparison.")); // assert the description is correct assert_eq!( detector.description(), @@ -104,14 +98,8 @@ pub trait OperationIsTautologyOrContradiction { impl OperationIsTautologyOrContradiction for BinaryOperation { fn is_tautology_or_contradiction(&self, context: &WorkspaceContext) -> Option { if let ( - Some(TypeDescriptions { - type_string: Some(lhs_type_string), - .. - }), - Some(TypeDescriptions { - type_string: Some(rhs_type_string), - .. - }), + Some(TypeDescriptions { type_string: Some(lhs_type_string), .. }), + Some(TypeDescriptions { type_string: Some(rhs_type_string), .. }), operator, ) = ( self.left_expression.as_ref().type_descriptions(), @@ -192,16 +180,14 @@ pub mod solidity_integer_helper { /// we can determine if it makes sense by calling this function /// does_operation_make_sense_with_rhs_value("uint8", ">=", "300") /// - /// This function checks for the range of integer values of uint8 and returns true if it is neither a tautology - /// nor a contradiction. + /// This function checks for the range of integer values of uint8 and returns true if it is + /// neither a tautology nor a contradiction. /// - /// Here, I define tautology as the condition where the range Ex: (>=300) FULLY COVERS thr Range of Uint8 - /// Contradiction: When the range Ex:(>=300) fully excludes the Range of Uint8 + /// Here, I define tautology as the condition where the range Ex: (>=300) FULLY COVERS thr Range + /// of Uint8 Contradiction: When the range Ex:(>=300) fully excludes the Range of Uint8 /// /// Notice how in the above example, the value is on the right hand side. /// Hence this function is called "does_...rhs_value". - /// - /// pub fn does_operation_make_sense_with_rhs_value( type_string: &str, operator: &str, @@ -213,8 +199,8 @@ pub mod solidity_integer_helper { let value_as_big_int = BigInt::parse_bytes(value.as_bytes(), 10)?; - // First and foremost if the value is out of range it's 100% either a tautology or a contradiction. - // Hence, return false. + // First and foremost if the value is out of range it's 100% either a tautology or a + // contradiction. Hence, return false. if value_as_big_int < allowed_min_val || value_as_big_int > allowed_max_val { return Some(false); } @@ -269,7 +255,6 @@ pub mod solidity_integer_helper { /// does_operation_make_sense_with_lhs_value("300", ">=", "uint8") /// /// Notice, here the value 300 is on the left hand side. - /// pub fn does_operation_make_sense_with_lhs_value( value: &str, operator: &str, @@ -328,10 +313,7 @@ pub mod solidity_integer_helper { } fn find_int_min(num_of_bits: u32) -> BigInt { - BigInt::parse_bytes(b"2", 10) - .unwrap() - .pow(num_of_bits - 1) - .neg() + BigInt::parse_bytes(b"2", 10).unwrap().pow(num_of_bits - 1).neg() } #[cfg(test)] @@ -364,11 +346,11 @@ pub mod solidity_integer_helper { #[test] fn can_find_max_of_uint256() { - // This test shows that we can calculate the biggest possible number in Solidity for uint - // which is 2^256 - 1. + // This test shows that we can calculate the biggest possible number in Solidity for + // uint which is 2^256 - 1. // hence we conclude that because we can represent 2^256 - 1, we can easily cover all - // the smaller variants of uint that is uint8, uint16, .... all the ay upto uint256 because they - // are lesser than 2^256 - 1 + // the smaller variants of uint that is uint8, uint16, .... all the ay upto uint256 + // because they are lesser than 2^256 - 1 let uint256_max = BigInt::parse_bytes(b"2", 10).unwrap().pow(256) - BigInt::one(); assert_eq!( uint256_max, diff --git a/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs b/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs index 11fd5ab99..9517a8f7c 100644 --- a/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs +++ b/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs @@ -1,15 +1,15 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, Expression, Identifier, NodeID}; -use crate::capture; -use crate::context::browser::ExtractMemberAccesses; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractMemberAccesses, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -100,8 +100,8 @@ struct MsgSenderAndTxOriginTracker { } impl MsgSenderAndTxOriginTracker { - /// To avoid FP (msg.sender == tx.origin) we require that tx.origin is present and msg.sender is absent - /// for it to be considered satisfied + /// To avoid FP (msg.sender == tx.origin) we require that tx.origin is present and msg.sender is + /// absent for it to be considered satisfied fn satisifed(&self) -> bool { self.reads_tx_origin && !self.reads_msg_sender } @@ -157,9 +157,6 @@ mod tx_origin_used_for_auth_detector { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 3); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/unchecked_calls.rs b/aderyn_core/src/detect/high/unchecked_calls.rs index 667105132..3196964cc 100644 --- a/aderyn_core/src/detect/high/unchecked_calls.rs +++ b/aderyn_core/src/detect/high/unchecked_calls.rs @@ -1,14 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::{GetClosestAncestorOfTypeX, GetImmediateParent}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{GetClosestAncestorOfTypeX, GetImmediateParent}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -24,14 +24,11 @@ impl IssueDetector for UncheckedLowLevelCallDetector { let call_types = ["call", "staticcall", "delegatecall"]; for member_access in context.member_accesses() { if call_types.iter().any(|&c| c == member_access.member_name) - && member_access - .expression - .type_descriptions() - .is_some_and(|type_desc| { - type_desc.type_string.as_ref().is_some_and(|type_string| { - type_string == "address" || type_string == "address payable" - }) + && member_access.expression.type_descriptions().is_some_and(|type_desc| { + type_desc.type_string.as_ref().is_some_and(|type_string| { + type_string == "address" || type_string == "address payable" }) + }) { if let Some(ASTNode::FunctionCall(func_call)) = member_access.closest_ancestor_of_type(context, NodeType::FunctionCall) @@ -41,8 +38,7 @@ impl IssueDetector for UncheckedLowLevelCallDetector { // We need to also check for the possibility where the function call's parent is // another function call and that has a direct parent of type block if let Some(ASTNode::ExpressionStatement(e)) = func_call.parent(context) { - if e.parent(context) - .is_some_and(|node| node.node_type() == NodeType::Block) + if e.parent(context).is_some_and(|node| node.node_type() == NodeType::Block) { capture!(self, context, func_call); } @@ -112,9 +108,6 @@ mod unchecked_low_level_calls_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 9); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/unchecked_return.rs b/aderyn_core/src/detect/high/unchecked_return.rs index a140c566e..aa848c64f 100644 --- a/aderyn_core/src/detect/high/unchecked_return.rs +++ b/aderyn_core/src/detect/high/unchecked_return.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, Expression, Identifier, MemberAccess, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::GetImmediateParent; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::GetImmediateParent, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -27,15 +24,12 @@ impl IssueDetector for UncheckedReturnDetector { // capture!(self, context, item); for function_call in context.function_calls() { - // Find the ID of FunctionDefinition that we're calling so that we may identify if there are returned params + // Find the ID of FunctionDefinition that we're calling so that we may identify if there + // are returned params match function_call.expression.as_ref() { - Expression::Identifier(Identifier { - referenced_declaration: Some(id), - .. - }) + Expression::Identifier(Identifier { referenced_declaration: Some(id), .. }) | Expression::MemberAccess(MemberAccess { - referenced_declaration: Some(id), - .. + referenced_declaration: Some(id), .. }) => { if let Some(ASTNode::ExpressionStatement(func_call_parent)) = function_call.parent(context) @@ -107,10 +101,7 @@ mod unchecked_return_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/high/unchecked_send.rs b/aderyn_core/src/detect/high/unchecked_send.rs index c0534142f..34ae3344f 100644 --- a/aderyn_core/src/detect/high/unchecked_send.rs +++ b/aderyn_core/src/detect/high/unchecked_send.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::GetImmediateParent; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::GetImmediateParent, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -23,14 +20,11 @@ impl IssueDetector for UncheckedSendDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for member_access in context.member_accesses() { if member_access.member_name == "send" - && member_access - .expression - .type_descriptions() - .is_some_and(|type_desc| { - type_desc.type_string.as_ref().is_some_and(|type_string| { - type_string == "address" || type_string == "address payable" - }) + && member_access.expression.type_descriptions().is_some_and(|type_desc| { + type_desc.type_string.as_ref().is_some_and(|type_string| { + type_string == "address" || type_string == "address payable" }) + }) { if let Some(ASTNode::FunctionCall(func_call)) = member_access.parent(context) { if let Some(ASTNode::ExpressionStatement(expr_stmnt)) = @@ -96,15 +90,9 @@ mod unchecked_send_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Unchecked `bool success` value for send call.") - ); + assert_eq!(detector.title(), String::from("Unchecked `bool success` value for send call.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/uninitialized_state_variable.rs b/aderyn_core/src/detect/high/uninitialized_state_variable.rs index 5006fa5e1..067a5c0de 100644 --- a/aderyn_core/src/detect/high/uninitialized_state_variable.rs +++ b/aderyn_core/src/detect/high/uninitialized_state_variable.rs @@ -1,13 +1,14 @@ -use std::collections::{BTreeMap, HashSet}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; use crate::ast::{Expression, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -25,8 +26,8 @@ impl IssueDetector for UninitializedStateVariableDetector { * - Gather all the storage variables (VariableDeclarations) * - Fitler out / Remove the ones where `value` property is not `None` * - Fitler out / Remove the ones that are arrays, mappings and structs - * - Now, we're left with state variables that are not initialized at the same - * line where they are declared. + * - Now, we're left with state variables that are not initialized at the same line + * where they are declared. * - Gather all the `Assignments` and collect all the `referencedDeclarations` on * `Identifier` expressions when they appear on LHS of the assginments * - Remove the above ids from the initial storage variables list @@ -70,10 +71,7 @@ impl IssueDetector for UninitializedStateVariableDetector { } for id in state_variable_ids { - context - .nodes - .get(&id) - .inspect(|&x| capture!(self, context, x)); + context.nodes.get(&id).inspect(|&x| capture!(self, context, x)); } Ok(!self.found_instances.is_empty()) @@ -131,15 +129,9 @@ mod uninitialized_state_variable_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Uninitialized State Variables") - ); + assert_eq!(detector.title(), String::from("Uninitialized State Variables")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/high/unprotected_init_function.rs b/aderyn_core/src/detect/high/unprotected_init_function.rs index b6de41ce2..c67babc81 100644 --- a/aderyn_core/src/detect/high/unprotected_init_function.rs +++ b/aderyn_core/src/detect/high/unprotected_init_function.rs @@ -22,10 +22,7 @@ impl IssueDetector for UnprotectedInitializerDetector { let has_modifiers = !function.modifiers.is_empty(); if !has_modifiers { let identifiers = ExtractIdentifiers::from(function).extracted; - if !identifiers - .iter() - .any(|x| x.name == "revert" || x.name == "require") - { + if !identifiers.iter().any(|x| x.name == "revert" || x.name == "require") { capture!(self, context, function); } } diff --git a/aderyn_core/src/detect/high/unsafe_casting.rs b/aderyn_core/src/detect/high/unsafe_casting.rs index 6c7194d42..255df4220 100644 --- a/aderyn_core/src/detect/high/unsafe_casting.rs +++ b/aderyn_core/src/detect/high/unsafe_casting.rs @@ -1,17 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, FunctionCall, FunctionCallKind, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::{ - ExtractBinaryOperations, ExtractIdentifiers, GetClosestAncestorOfTypeX, -}; -use crate::context::workspace_context::ASTNode; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractBinaryOperations, ExtractIdentifiers, GetClosestAncestorOfTypeX}, + workspace_context::{ASTNode, WorkspaceContext}, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; use phf::phf_map; @@ -48,13 +45,11 @@ impl IssueDetector for UnsafeCastingDetector { &*function_call.expression { if let Some(argument_types) = &to_expression.argument_types { - let casting_from_type = match argument_types - .first() - .and_then(|arg| arg.type_string.as_ref()) - { - Some(t) => t, - None => continue, - }; + let casting_from_type = + match argument_types.first().and_then(|arg| arg.type_string.as_ref()) { + Some(t) => t, + None => continue, + }; let casting_map = if casting_from_type.contains("uint") { &UINT_CASTING_MAP @@ -142,21 +137,13 @@ fn has_binary_operation_checks( identifier_reference_declaration_id: &NodeID, ) -> bool { if let Some(ASTNode::ContractDefinition(contract)) = contract { - return ExtractBinaryOperations::from(contract) - .extracted - .iter() - .any(|binary_operation| { - ExtractIdentifiers::from(binary_operation) - .extracted - .into_iter() - .any(|identifier| { - identifier - .referenced_declaration - .is_some_and(|reference_id| { - *identifier_reference_declaration_id == reference_id - }) - }) - }); + return ExtractBinaryOperations::from(contract).extracted.iter().any(|binary_operation| { + ExtractIdentifiers::from(binary_operation).extracted.into_iter().any(|identifier| { + identifier.referenced_declaration.is_some_and(|reference_id| { + *identifier_reference_declaration_id == reference_id + }) + }) + }); } false } @@ -286,9 +273,6 @@ mod unsafe_casting_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 94); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); } } diff --git a/aderyn_core/src/detect/high/weak_randomness.rs b/aderyn_core/src/detect/high/weak_randomness.rs index 16280ee4a..4f4f70f3c 100644 --- a/aderyn_core/src/detect/high/weak_randomness.rs +++ b/aderyn_core/src/detect/high/weak_randomness.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, FunctionCall, FunctionCallKind, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::{ASTNode, WorkspaceContext}, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -53,10 +51,8 @@ impl IssueDetector for WeakRandomnessDetector { } // check for modulo operations on block.timestamp, block.number and blockhash - for binary_operation in context - .binary_operations() - .into_iter() - .filter(|b| b.operator == "%") + for binary_operation in + context.binary_operations().into_iter().filter(|b| b.operator == "%") { // if left operand is a variable, get its definition and perform check if let Expression::Identifier(ref i) = *binary_operation.left_expression { @@ -178,10 +174,7 @@ mod weak_randomness_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 9); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct assert_eq!(detector.title(), String::from("Weak Randomness")); // assert the description is correct diff --git a/aderyn_core/src/detect/high/yul_return.rs b/aderyn_core/src/detect/high/yul_return.rs index 9bbd37650..404a6c85c 100644 --- a/aderyn_core/src/detect/high/yul_return.rs +++ b/aderyn_core/src/detect/high/yul_return.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -73,15 +71,9 @@ mod yul_return_detector_tests { assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::High - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Yul block contains `return` function call.") - ); + assert_eq!(detector.title(), String::from("Yul block contains `return` function call.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/assert_state_change.rs b/aderyn_core/src/detect/low/assert_state_change.rs index c88701239..80b262b04 100644 --- a/aderyn_core/src/detect/low/assert_state_change.rs +++ b/aderyn_core/src/detect/low/assert_state_change.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{Expression, Identifier, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -26,9 +23,7 @@ impl IssueDetector for AssertStateChangeDetector { function_call.expression.as_ref() { if name == "assert" - && function_call - .arguments_change_contract_state(context) - .is_some_and(identity) + && function_call.arguments_change_contract_state(context).is_some_and(identity) { capture!(self, context, function_call); } @@ -55,7 +50,7 @@ impl IssueDetector for AssertStateChangeDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::AssertStateChange) + format!("{}", IssueDetectorNamePool::StateChangeInAssert) } } @@ -89,17 +84,11 @@ mod assert_state_change_tracker { impl FunctionCall { pub fn arguments_change_contract_state(&self, context: &WorkspaceContext) -> Option { - let mut tracker = StateVariableChangeTracker { - has_some_state_variable_changed: false, - context, - }; - - let arguments = self - .arguments - .clone() - .into_iter() - .map(|n| n.into()) - .collect::>(); + let mut tracker = + StateVariableChangeTracker { has_some_state_variable_changed: false, context }; + + let arguments = + self.arguments.clone().into_iter().map(|n| n.into()).collect::>(); let ast_nodes: &[&ASTNode] = &(arguments.iter().collect::>()); @@ -136,9 +125,6 @@ mod asert_state_changes_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/boolean_equality.rs b/aderyn_core/src/detect/low/boolean_equality.rs index e967d933c..9eae52e2f 100644 --- a/aderyn_core/src/detect/low/boolean_equality.rs +++ b/aderyn_core/src/detect/low/boolean_equality.rs @@ -1,5 +1,4 @@ -use crate::detect::helpers::is_constant_boolean; -use crate::issue_detector; +use crate::{detect::helpers::is_constant_boolean, issue_detector}; use eyre::Result; issue_detector! { @@ -8,7 +7,7 @@ issue_detector! { severity: Low, title: "Boolean equality is not required.", desc: "If `x` is a boolean, there is no need to do `if(x == true)` or `if(x == false)`. Just use `if(x)` and `if(!x)` respectively.", - name: BooleanEquality, + name: RedundantBooleanEquality, |context| { for binary_operation in context.binary_operations() { diff --git a/aderyn_core/src/detect/low/builtin_symbol_shadowing.rs b/aderyn_core/src/detect/low/builtin_symbol_shadowing.rs index 75bd2e1ec..548c5c079 100644 --- a/aderyn_core/src/detect/low/builtin_symbol_shadowing.rs +++ b/aderyn_core/src/detect/low/builtin_symbol_shadowing.rs @@ -1,10 +1,8 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; +use crate::{capture, detect::detector::IssueDetectorNamePool}; use phf::phf_set; use crate::{ @@ -158,9 +156,6 @@ mod builtin_symbol_shadowing_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/cache_array_length.rs b/aderyn_core/src/detect/low/cache_array_length.rs index 1f9913f0b..8c2dfccdf 100644 --- a/aderyn_core/src/detect/low/cache_array_length.rs +++ b/aderyn_core/src/detect/low/cache_array_length.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{ASTNode, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -23,20 +20,23 @@ impl IssueDetector for CacheArrayLengthDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // PLAN - // - // First, look at the condition of the for loop, if it contains `.length` see if it's possible - // to cache it. + // First, look at the condition of the for loop, if it contains `.length` + // see if it's possible to cache it. // - // Investigate the body of the loop to see if anywhere the said state variable is manipulated. If no manipulations, - // it means that the state variable could be cached. + // Investigate the body of the loop to see if anywhere the said state variable is + // manipulated. If no manipulations, it means that the state variable could be + // cached. // for for_loop in context.for_statements() { if let Some(changes) = for_loop.state_variable_changes(context) { - // Find all the storage arrays on which `.length` is checked in for loop's conidition + // Find all the storage arrays on which `.length` is checked in for loop's + // conidition let state_vars = for_loop.state_variables_lengths_that_are_referenced_in_condition(context); - // Now see if any of the storage array has been manipulated. If yes, then it doesn't qualify for caching + // Now see if any of the storage array has been manipulated. If yes, then it doesn't + // qualify for caching let they_are_not_manipulated_in_the_for_loop = state_vars.iter().all(|state_var_id| { if let Some(ASTNode::VariableDeclaration(var)) = @@ -52,8 +52,8 @@ impl IssueDetector for CacheArrayLengthDetector { false }); - // Here, we know that none of the storage arrays whose length was referenced, changes in the loop - // So we report them as potential caches. + // Here, we know that none of the storage arrays whose length was referenced, + // changes in the loop So we report them as potential caches. if !state_vars.is_empty() && they_are_not_manipulated_in_the_for_loop { capture!(self, context, for_loop); } @@ -87,7 +87,7 @@ impl IssueDetector for CacheArrayLengthDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::CacheArrayLength) + format!("{}", IssueDetectorNamePool::ArrayLengthNotCached) } } @@ -118,11 +118,7 @@ mod loop_investigation_helper { } if let Expression::Identifier(Identifier { referenced_declaration: Some(id), - type_descriptions: - TypeDescriptions { - type_string: Some(type_string), - .. - }, + type_descriptions: TypeDescriptions { type_string: Some(type_string), .. }, .. }) = member_access.expression.as_ref() { @@ -142,16 +138,13 @@ mod loop_investigation_helper { state_vars_lengths_that_are_referenced } - /// Investigates the body of the for loop with the help callgraph and accumulates all the state variables - /// that have been changed + /// Investigates the body of the for loop with the help callgraph and accumulates all the + /// state variables that have been changed pub fn state_variable_changes<'a>( &self, context: &'a WorkspaceContext, ) -> Option> { - let mut tracker = StateVariableChangeTracker { - changes: None, - context, - }; + let mut tracker = StateVariableChangeTracker { changes: None, context }; let callgraph = CallGraph::new(context, &[&(self.into())], CallGraphDirection::Inward).ok()?; @@ -206,9 +199,6 @@ mod cahce_array_length_tests { assert_eq!(detector.instances().len(), 3); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/centralization_risk.rs b/aderyn_core/src/detect/low/centralization_risk.rs index 0c6573817..56783531c 100644 --- a/aderyn_core/src/detect/low/centralization_risk.rs +++ b/aderyn_core/src/detect/low/centralization_risk.rs @@ -92,15 +92,9 @@ mod centralization_risk_detector_tests { // assert that the number of instances found is 3 assert_eq!(detector.instances().len(), 3); // assert that the severity is Low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("Centralization Risk for trusted owners") - ); + assert_eq!(detector.title(), String::from("Centralization Risk for trusted owners")); // assert that the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/constant_funcs_assembly.rs b/aderyn_core/src/detect/low/constant_funcs_assembly.rs index a2a9aefa5..d26e8f20e 100644 --- a/aderyn_core/src/detect/low/constant_funcs_assembly.rs +++ b/aderyn_core/src/detect/low/constant_funcs_assembly.rs @@ -1,20 +1,23 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::str::FromStr; +use std::{collections::BTreeMap, error::Error, str::FromStr}; use crate::ast::{ASTNode, NodeID, NodeType, StateMutability}; -use crate::capture; -use crate::context::browser::{ - ExtractInlineAssemblys, ExtractPragmaDirectives, GetClosestAncestorOfTypeX, +use crate::{ + capture, + context::browser::{ + ExtractInlineAssemblys, ExtractPragmaDirectives, GetClosestAncestorOfTypeX, + }, }; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers::{self, pragma_directive_to_semver}; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + context::{ + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::{self, pragma_directive_to_semver}, + }, }; use eyre::Result; use semver::{Version, VersionReq}; @@ -46,9 +49,7 @@ impl IssueDetector for ConstantFunctionContainsAssemblyDetector { if function.state_mutability() == &StateMutability::View || function.state_mutability() == &StateMutability::Pure { - let mut tracker = AssemblyTracker { - has_assembly: false, - }; + let mut tracker = AssemblyTracker { has_assembly: false }; let callgraph = CallGraph::new( context, &[&(function.into())], @@ -118,8 +119,9 @@ impl CallGraphVisitor for AssemblyTracker { if let ASTNode::FunctionDefinition(function) = node { // Ignore checking functions that start with `_` - // Example - templegold contains math functions like `_rpow()`, etc that are used by view functions - // That should be okay .. I guess? (idk ... it's open for dicussion) + // Example - templegold contains math functions like `_rpow()`, etc that are used by + // view functions That should be okay .. I guess? (idk ... it's open for + // dicussion) if function.name.starts_with('_') { return Ok(()); } @@ -157,9 +159,6 @@ mod constant_functions_assembly_detector { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 3); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/contracts_with_todos.rs b/aderyn_core/src/detect/low/contracts_with_todos.rs index bc9cfda31..e5ef025e6 100644 --- a/aderyn_core/src/detect/low/contracts_with_todos.rs +++ b/aderyn_core/src/detect/low/contracts_with_todos.rs @@ -94,9 +94,6 @@ mod contracts_with_todos_tests { // assert that the detector finds the correct number of instances assert_eq!(detector.instances().len(), 1); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/costly_operations_inside_loops.rs b/aderyn_core/src/detect/low/costly_operations_inside_loops.rs index 1b45fa813..7341afd01 100644 --- a/aderyn_core/src/detect/low/costly_operations_inside_loops.rs +++ b/aderyn_core/src/detect/low/costly_operations_inside_loops.rs @@ -1,17 +1,15 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{ASTNode, NodeID}; -use crate::capture; -use crate::context::browser::ApproximateStorageChangeFinder; +use crate::{capture, context::browser::ApproximateStorageChangeFinder}; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + context::{ + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -71,10 +69,7 @@ impl IssueDetector for CostlyOperationsInsideLoopsDetector { fn changes_state(context: &WorkspaceContext, ast_node: &ASTNode) -> Option { // Now, investigate the function to see if there is scope for any state variable changes - let mut tracker = StateVariableChangeTracker { - state_var_has_changed: false, - context, - }; + let mut tracker = StateVariableChangeTracker { state_var_has_changed: false, context }; let callgraph = CallGraph::new(context, &[ast_node], CallGraphDirection::Inward).ok()?; callgraph.accept(context, &mut tracker).ok()?; Some(tracker.state_var_has_changed) @@ -122,9 +117,6 @@ mod costly_operations_inside_loops_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is high - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/dead_code.rs b/aderyn_core/src/detect/low/dead_code.rs index 4d499baf4..5cc428d8b 100644 --- a/aderyn_core/src/detect/low/dead_code.rs +++ b/aderyn_core/src/detect/low/dead_code.rs @@ -1,15 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, ContractKind, NodeID, NodeType, Visibility}; -use crate::capture; -use crate::context::browser::GetClosestAncestorOfTypeX; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::GetClosestAncestorOfTypeX, workspace_context::WorkspaceContext}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -23,8 +22,9 @@ pub struct DeadCodeDetector { impl IssueDetector for DeadCodeDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // Heuristic: - // Internal non overriding functions inside of non abstract contracts that have a body (implemented) and are not used - // If an internal function is marked override then, it may still be used even if it doesn't have a direct referencedDeclaration + // Internal non overriding functions inside of non abstract contracts that have a body + // (implemented) and are not used If an internal function is marked override then, + // it may still be used even if it doesn't have a direct referencedDeclaration // pointing to it. for func in context @@ -101,9 +101,6 @@ mod dead_code_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/deprecated_oz_functions.rs b/aderyn_core/src/detect/low/deprecated_oz_functions.rs index 1ef5a6d18..11e00e097 100644 --- a/aderyn_core/src/detect/low/deprecated_oz_functions.rs +++ b/aderyn_core/src/detect/low/deprecated_oz_functions.rs @@ -104,10 +104,7 @@ mod deprecated_oz_functions_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/division_before_multiplication.rs b/aderyn_core/src/detect/low/division_before_multiplication.rs index 05c66ee45..99350bdd6 100644 --- a/aderyn_core/src/detect/low/division_before_multiplication.rs +++ b/aderyn_core/src/detect/low/division_before_multiplication.rs @@ -1,14 +1,12 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ ast::Expression, + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -20,11 +18,7 @@ pub struct DivisionBeforeMultiplicationDetector { impl IssueDetector for DivisionBeforeMultiplicationDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - for op in context - .binary_operations() - .iter() - .filter(|op| op.operator == "*") - { + for op in context.binary_operations().iter().filter(|op| op.operator == "*") { if let Expression::BinaryOperation(left_op) = op.left_expression.as_ref() { if left_op.operator == "/" { capture!(self, context, left_op) @@ -74,10 +68,7 @@ mod division_before_multiplication_detector_tests { let found = detector.detect(&context).unwrap(); assert!(found); assert_eq!(detector.instances().len(), 4); - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); assert_eq!( detector.title(), String::from("Incorrect Order of Division and Multiplication") diff --git a/aderyn_core/src/detect/low/ecrecover.rs b/aderyn_core/src/detect/low/ecrecover.rs index 0b79df03d..47654c4de 100644 --- a/aderyn_core/src/detect/low/ecrecover.rs +++ b/aderyn_core/src/detect/low/ecrecover.rs @@ -49,7 +49,7 @@ impl IssueDetector for EcrecoverDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::Ecrecover) + format!("{}", IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover) } } @@ -76,10 +76,7 @@ mod ecrecover_tests { // assert that the detector found the correct ecrecover assert_eq!(detector.instances().len(), 1); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/empty_blocks.rs b/aderyn_core/src/detect/low/empty_blocks.rs index ae35f9984..19c540707 100644 --- a/aderyn_core/src/detect/low/empty_blocks.rs +++ b/aderyn_core/src/detect/low/empty_blocks.rs @@ -97,16 +97,10 @@ mod empty_block_tests { // assert that the detector returns the correct number of instances assert_eq!(detector.instances().len(), 7); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!(detector.title(), String::from("Empty Block")); // assert that the detector returns the correct description - assert_eq!( - detector.description(), - String::from("Consider removing empty blocks.") - ); + assert_eq!(detector.description(), String::from("Consider removing empty blocks.")); } } diff --git a/aderyn_core/src/detect/low/function_init_state_vars.rs b/aderyn_core/src/detect/low/function_init_state_vars.rs index 9ee709abc..de6ad3ec7 100644 --- a/aderyn_core/src/detect/low/function_init_state_vars.rs +++ b/aderyn_core/src/detect/low/function_init_state_vars.rs @@ -1,15 +1,15 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, Expression, FunctionCall, Identifier, NodeID}; -use crate::capture; -use crate::context::browser::ExtractReferencedDeclarations; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractReferencedDeclarations, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -24,13 +24,12 @@ impl IssueDetector for FunctionInitializingStateDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // PLAN // Capture state variables that are initialized directly by calling a non constant function. - // Go throough state variable declarations with initial value (this will be true when value is set outside constructor) - // See if the function references non-constant state variables. If it does, then capture it + // Go throough state variable declarations with initial value (this will be true when value + // is set outside constructor) See if the function references non-constant state + // variables. If it does, then capture it - for variable_declaration in context - .variable_declarations() - .into_iter() - .filter(|v| v.state_variable) + for variable_declaration in + context.variable_declarations().into_iter().filter(|v| v.state_variable) { if let Some(Expression::FunctionCall(FunctionCall { expression, .. })) = variable_declaration.value.as_ref() @@ -90,10 +89,7 @@ struct NonConstantStateVariableReferenceDeclarationTracker<'a> { impl<'a> NonConstantStateVariableReferenceDeclarationTracker<'a> { fn new(context: &'a WorkspaceContext) -> Self { - Self { - makes_a_reference: false, - context, - } + Self { makes_a_reference: false, context } } } @@ -142,9 +138,6 @@ mod function_initializing_state_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 3); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/function_pointer_in_constructor.rs b/aderyn_core/src/detect/low/function_pointer_in_constructor.rs index 6b324f4dc..2195597f0 100644 --- a/aderyn_core/src/detect/low/function_pointer_in_constructor.rs +++ b/aderyn_core/src/detect/low/function_pointer_in_constructor.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{FunctionKind, NodeID}; -use crate::capture; -use crate::context::browser::ExtractVariableDeclarations; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractVariableDeclarations, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -99,6 +96,22 @@ mod func_compilation_solc_pragma_helper { } false } + pub fn compiles_for_solc_below_0_6_5(&self, context: &WorkspaceContext) -> bool { + if let Some(source_unit) = self.closest_ancestor_of_type(context, NodeType::SourceUnit) + { + let pragma_directives = ExtractPragmaDirectives::from(source_unit).extracted; + + if let Some(pragma_directive) = pragma_directives.first() { + if let Ok(pragma_semver) = helpers::pragma_directive_to_semver(pragma_directive) + { + if version_req_allows_below_0_6_5(&pragma_semver) { + return true; + } + } + } + } + false + } } fn version_req_allows_below_0_5_9(version_req: &VersionReq) -> bool { @@ -121,6 +134,33 @@ mod func_compilation_solc_pragma_helper { // Else, return false false } + fn version_req_allows_below_0_6_5(version_req: &VersionReq) -> bool { + // If it matches any 0.4.0 to 0.4.26, return true + for i in 0..=26 { + let version = Version::from_str(&format!("0.4.{}", i)).unwrap(); + if version_req.matches(&version) { + return true; + } + } + + // If it matches any 0.5.0 to 0.5.17, return true + for i in 0..=17 { + let version = Version::from_str(&format!("0.5.{}", i)).unwrap(); + if version_req.matches(&version) { + return true; + } + } + + // If it matches any 0.6.0 to 0.6.4, return true + for i in 0..=4 { + let version = Version::from_str(&format!("0.4.{}", i)).unwrap(); + if version_req.matches(&version) { + return true; + } + } + // Else, return false + false + } } #[cfg(test)] @@ -146,9 +186,6 @@ mod function_pointers_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/inconsistent_type_names.rs b/aderyn_core/src/detect/low/inconsistent_type_names.rs index 5289c4d02..fb414d8a0 100644 --- a/aderyn_core/src/detect/low/inconsistent_type_names.rs +++ b/aderyn_core/src/detect/low/inconsistent_type_names.rs @@ -178,10 +178,7 @@ mod inconsistent_type_names_tests { assert_eq!(detector.instances().len(), 7); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } #[test] @@ -199,9 +196,6 @@ mod inconsistent_type_names_tests { assert_eq!(detector.instances().len(), 2); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/large_literal_value.rs b/aderyn_core/src/detect/low/large_literal_value.rs index d35bc3512..aef084725 100644 --- a/aderyn_core/src/detect/low/large_literal_value.rs +++ b/aderyn_core/src/detect/low/large_literal_value.rs @@ -17,11 +17,7 @@ pub struct LargeLiteralValueDetector { impl IssueDetector for LargeLiteralValueDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - for numeric_literal in context - .literals() - .iter() - .filter(|x| x.kind == LiteralKind::Number) - { + for numeric_literal in context.literals().iter().filter(|x| x.kind == LiteralKind::Number) { if let Some(value) = numeric_literal.value.clone() { // Strip any underscore separators let value_no_underscores = value.replace('_', ""); @@ -82,10 +78,7 @@ mod large_literal_values { // assert that the detector finds the correct number of instances assert_eq!(detector.instances().len(), 22); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/constants_instead_of_literals.rs b/aderyn_core/src/detect/low/literals_instead_of_constants.rs similarity index 85% rename from aderyn_core/src/detect/low/constants_instead_of_literals.rs rename to aderyn_core/src/detect/low/literals_instead_of_constants.rs index 53464e0d4..e077afca3 100644 --- a/aderyn_core/src/detect/low/constants_instead_of_literals.rs +++ b/aderyn_core/src/detect/low/literals_instead_of_constants.rs @@ -18,13 +18,13 @@ use crate::{ use eyre::Result; #[derive(Default)] -pub struct ConstantsInsteadOfLiteralsDetector { +pub struct LiteralsInsteadOfConstantsDetector { // Keys are: [0] source file name, [1] line number, [2] character location of node. // Do not add items manually, use `capture!` to add nodes to this BTreeMap. found_instances: BTreeMap<(String, usize, String), NodeID>, } -impl IssueDetector for ConstantsInsteadOfLiteralsDetector { +impl IssueDetector for LiteralsInsteadOfConstantsDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // Get all contracts // For each contract @@ -37,10 +37,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { for contract in context.contract_definitions() { let mut literal_values_found: HashMap> = HashMap::new(); - for function in ExtractFunctionDefinitions::from(contract) - .extracted - .into_iter() - { + for function in ExtractFunctionDefinitions::from(contract).extracted.into_iter() { for literal in ExtractLiterals::from(&function).extracted.into_iter() { if (literal.kind == LiteralKind::Number && literal.value != Some(String::from("0")) @@ -56,10 +53,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { if let Some(literal_value) = literal.value.as_ref() { if literal_values_found.contains_key(literal_value) { - literal_values_found - .get_mut(literal_value) - .unwrap() - .push(literal); + literal_values_found.get_mut(literal_value).unwrap().push(literal); } else { literal_values_found.insert(literal_value.clone(), vec![literal]); } @@ -68,10 +62,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { } } - for modifier in ExtractModifierDefinitions::from(contract) - .extracted - .into_iter() - { + for modifier in ExtractModifierDefinitions::from(contract).extracted.into_iter() { for literal in ExtractLiterals::from(&modifier).extracted.into_iter() { if (literal.kind == LiteralKind::Number && literal.value != Some(String::from("0")) @@ -87,10 +78,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { if let Some(literal_value) = literal.value.as_ref() { if literal_values_found.contains_key(literal_value) { - literal_values_found - .get_mut(literal_value) - .unwrap() - .push(literal); + literal_values_found.get_mut(literal_value).unwrap().push(literal); } else { literal_values_found.insert(literal_value.clone(), vec![literal]); } @@ -128,7 +116,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::ConstantsInsteadOfLiterals) + format!("{}", IssueDetectorNamePool::LiteralInsteadOfConstant) } } @@ -136,10 +124,9 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { mod constants_instead_of_literals_tests { use serial_test::serial; + use super::LiteralsInsteadOfConstantsDetector; use crate::detect::detector::IssueDetector; - use super::ConstantsInsteadOfLiteralsDetector; - #[test] #[serial] fn test_constants_instead_of_literals_by_loading_contract_directly() { @@ -147,17 +134,14 @@ mod constants_instead_of_literals_tests { "../tests/contract-playground/src/ConstantsLiterals.sol", ); - let mut detector = ConstantsInsteadOfLiteralsDetector::default(); + let mut detector = LiteralsInsteadOfConstantsDetector::default(); // assert that the detector finds the public Function let found = detector.detect(&context).unwrap(); assert!(found); // assert that the detector finds the correct number of instances assert_eq!(detector.instances().len(), 8); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/local_variable_shadowing.rs b/aderyn_core/src/detect/low/local_variable_shadowing.rs index 910423b1c..5d4a52307 100644 --- a/aderyn_core/src/detect/low/local_variable_shadowing.rs +++ b/aderyn_core/src/detect/low/local_variable_shadowing.rs @@ -1,15 +1,14 @@ -use std::collections::BTreeMap; -use std::convert::identity; -use std::error::Error; +use std::{collections::BTreeMap, convert::identity, error::Error}; use crate::ast::{ContractKind, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::{ExtractVariableDeclarations, GetClosestAncestorOfTypeX}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractVariableDeclarations, GetClosestAncestorOfTypeX}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -36,11 +35,9 @@ impl IssueDetector for LocalVariableShadowingDetector { contract.get_all_state_variables_in_linearized_base_contracts_chain(context) { for local_contract_variable in local_contract_variables { - if state_variables - .iter() - .any(|v| v.name == local_contract_variable.name) - { - // It's okay to allow EventDefinitions/ ErrorDefinitions to shadow the state variable name + if state_variables.iter().any(|v| v.name == local_contract_variable.name) { + // It's okay to allow EventDefinitions/ ErrorDefinitions to shadow the state + // variable name if local_contract_variable .closest_ancestor_of_type(context, NodeType::EventDefinition) .is_some() @@ -97,11 +94,8 @@ mod contract_hirearchy_variable_helpers { for contract_id in contracts { if let ASTNode::ContractDefinition(c) = context.nodes.get(contract_id)? { let variable_declarations = ExtractVariableDeclarations::from(c).extracted; - all_state_variable_ids.extend( - variable_declarations - .into_iter() - .filter(|v| v.state_variable), - ) + all_state_variable_ids + .extend(variable_declarations.into_iter().filter(|v| v.state_variable)) } } Some(all_state_variable_ids) @@ -134,9 +128,6 @@ mod local_variable_shadowing_tests { assert_eq!(detector.instances().len(), 3); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/missing_inheritance.rs b/aderyn_core/src/detect/low/missing_inheritance.rs index e26eb089e..cca19ec73 100644 --- a/aderyn_core/src/detect/low/missing_inheritance.rs +++ b/aderyn_core/src/detect/low/missing_inheritance.rs @@ -1,15 +1,15 @@ -use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::convert::identity; -use std::error::Error; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + convert::identity, + error::Error, +}; use crate::ast::{ASTNode, ContractKind, NodeID}; -use crate::capture; -use crate::context::browser::ExtractVariableDeclarations; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractVariableDeclarations, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -102,14 +102,9 @@ impl IssueDetector for MissingInheritanceDetector { { if c.kind == ContractKind::Interface || c.is_abstract.map_or(false, identity) { // Check that the contract is compatible with the missing inheritance - if missing_function_selectors - .iter() - .all(|s| contract_selectors.contains(s)) + if missing_function_selectors.iter().all(|s| contract_selectors.contains(s)) { - results - .entry(*contract_id) - .or_default() - .insert(c.name.clone()); + results.entry(*contract_id).or_default().insert(c.name.clone()); } } } @@ -120,10 +115,7 @@ impl IssueDetector for MissingInheritanceDetector { if let Some(ASTNode::ContractDefinition(c)) = context.nodes.get(&contract) { // If the contract c already has some inheritance, don't report it because we want // to respect the developer's choice. - if c.linearized_base_contracts - .as_ref() - .is_some_and(|chain| chain.len() != 1) - { + if c.linearized_base_contracts.as_ref().is_some_and(|chain| chain.len() != 1) { continue; } let missing_inheritances_vector = @@ -192,9 +184,6 @@ mod missing_inheritance_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/mod.rs b/aderyn_core/src/detect/low/mod.rs index 0d0855b73..4091db088 100644 --- a/aderyn_core/src/detect/low/mod.rs +++ b/aderyn_core/src/detect/low/mod.rs @@ -4,7 +4,6 @@ pub(crate) mod builtin_symbol_shadowing; pub(crate) mod cache_array_length; pub(crate) mod centralization_risk; pub(crate) mod constant_funcs_assembly; -pub(crate) mod constants_instead_of_literals; pub(crate) mod contracts_with_todos; pub(crate) mod costly_operations_inside_loops; pub(crate) mod dead_code; @@ -16,6 +15,7 @@ pub(crate) mod function_init_state_vars; pub(crate) mod function_pointer_in_constructor; pub(crate) mod inconsistent_type_names; pub(crate) mod large_literal_value; +pub(crate) mod literals_instead_of_constants; pub(crate) mod local_variable_shadowing; pub(crate) mod missing_inheritance; pub(crate) mod multiple_placeholders; @@ -29,6 +29,7 @@ pub(crate) mod reverts_and_requries_in_loops; pub(crate) mod solmate_safe_transfer_lib; pub(crate) mod state_variable_changes_without_events; pub(crate) mod state_variable_could_be_constant; +pub(crate) mod state_variable_could_be_immutable; pub(crate) mod unindexed_events; pub(crate) mod uninitialized_local_variables; pub(crate) mod unsafe_erc20_functions; @@ -49,7 +50,6 @@ pub use builtin_symbol_shadowing::BuiltinSymbolShadowDetector; pub use cache_array_length::CacheArrayLengthDetector; pub use centralization_risk::CentralizationRiskDetector; pub use constant_funcs_assembly::ConstantFunctionContainsAssemblyDetector; -pub use constants_instead_of_literals::ConstantsInsteadOfLiteralsDetector; pub use contracts_with_todos::ContractsWithTodosDetector; pub use costly_operations_inside_loops::CostlyOperationsInsideLoopsDetector; pub use dead_code::DeadCodeDetector; @@ -61,6 +61,7 @@ pub use function_init_state_vars::FunctionInitializingStateDetector; pub use function_pointer_in_constructor::FucntionPointerInConstructorDetector; pub use inconsistent_type_names::InconsistentTypeNamesDetector; pub use large_literal_value::LargeLiteralValueDetector; +pub use literals_instead_of_constants::LiteralsInsteadOfConstantsDetector; pub use local_variable_shadowing::LocalVariableShadowingDetector; pub use missing_inheritance::MissingInheritanceDetector; pub use multiple_placeholders::MultiplePlaceholdersDetector; @@ -74,6 +75,7 @@ pub use reverts_and_requries_in_loops::RevertsAndRequiresInLoopsDetector; pub use solmate_safe_transfer_lib::SolmateSafeTransferLibDetector; pub use state_variable_changes_without_events::StateVariableChangesWithoutEventDetector; pub use state_variable_could_be_constant::StateVariableCouldBeConstantDetector; +pub use state_variable_could_be_immutable::StateVariableCouldBeImmutableDetector; pub use unindexed_events::UnindexedEventsDetector; pub use uninitialized_local_variables::UninitializedLocalVariableDetector; pub use unsafe_erc20_functions::UnsafeERC20FunctionsDetector; diff --git a/aderyn_core/src/detect/low/multiple_placeholders.rs b/aderyn_core/src/detect/low/multiple_placeholders.rs index 9820885b6..ab54a8bb3 100644 --- a/aderyn_core/src/detect/low/multiple_placeholders.rs +++ b/aderyn_core/src/detect/low/multiple_placeholders.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::NodeID; -use crate::capture; -use crate::context::browser::ExtractPlaceholderStatements; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractPlaceholderStatements, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -84,9 +81,6 @@ mod multiple_placeholder_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/non_reentrant_before_others.rs b/aderyn_core/src/detect/low/non_reentrant_before_others.rs index c814e2953..fb4c9b86c 100644 --- a/aderyn_core/src/detect/low/non_reentrant_before_others.rs +++ b/aderyn_core/src/detect/low/non_reentrant_before_others.rs @@ -20,11 +20,7 @@ impl IssueDetector for NonReentrantBeforeOthersDetector { for definition in function_definitions { if definition.modifiers.len() > 1 { for (index, modifier) in definition.modifiers.iter().enumerate() { - if modifier - .modifier_name - .name() - .to_lowercase() - .contains("nonreentrant") + if modifier.modifier_name.name().to_lowercase().contains("nonreentrant") && index != 0 { capture!(self, context, modifier); @@ -52,7 +48,7 @@ impl IssueDetector for NonReentrantBeforeOthersDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::NonReentrantBeforeOthers) + format!("{}", IssueDetectorNamePool::NonReentrantIsNotBeforeOthers) } } @@ -80,10 +76,7 @@ mod non_reentrant_before_others_tests { let (_, line_number, _) = detector.instances().keys().next().unwrap().clone(); assert_eq!(line_number, 10); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/public_variable_read_in_external_context.rs b/aderyn_core/src/detect/low/public_variable_read_in_external_context.rs index a0c95573f..4bd1fd1af 100644 --- a/aderyn_core/src/detect/low/public_variable_read_in_external_context.rs +++ b/aderyn_core/src/detect/low/public_variable_read_in_external_context.rs @@ -1,16 +1,19 @@ -use std::collections::{BTreeMap, HashSet}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; use crate::ast::{ ASTNode, ContractDefinition, Expression, Identifier, MemberAccess, NodeID, Visibility, }; -use crate::capture; -use crate::context::browser::{ExtractFunctionCalls, ExtractVariableDeclarations}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractFunctionCalls, ExtractVariableDeclarations}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::{eyre, Result}; @@ -92,15 +95,14 @@ fn find_all_public_member_names_called_using_this_keyword_in_contract<'a>( member_names } -// Scans the linearized base contracts and returns a list of all the NodeIDs of public variable declarations +// Scans the linearized base contracts and returns a list of all the NodeIDs of public variable +// declarations fn find_all_public_state_variables_names_for_contract( context: &WorkspaceContext, contract: &ContractDefinition, ) -> Result, Box> { - let inheritance_ancestors = contract - .linearized_base_contracts - .as_ref() - .ok_or(eyre!("base contracts not found!"))?; + let inheritance_ancestors = + contract.linearized_base_contracts.as_ref().ok_or(eyre!("base contracts not found!"))?; Ok(inheritance_ancestors .iter() @@ -148,10 +150,7 @@ mod public_variable_read_in_external_context_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/push_0_opcode.rs b/aderyn_core/src/detect/low/push_0_opcode.rs index 215845c31..aa03e2c19 100644 --- a/aderyn_core/src/detect/low/push_0_opcode.rs +++ b/aderyn_core/src/detect/low/push_0_opcode.rs @@ -109,15 +109,9 @@ mod unspecific_solidity_pragma_tests { // assert that the number of instances is correct assert_eq!(detector.instances().len(), 1); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("PUSH0 is not supported by all chains") - ); + assert_eq!(detector.title(), String::from("PUSH0 is not supported by all chains")); // assert that the description is correct assert_eq!( detector.description(), @@ -141,15 +135,9 @@ mod unspecific_solidity_pragma_tests { // assert that the number of instances is correct assert_eq!(detector.instances().len(), 1); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("PUSH0 is not supported by all chains") - ); + assert_eq!(detector.title(), String::from("PUSH0 is not supported by all chains")); // assert that the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/redundant_statements.rs b/aderyn_core/src/detect/low/redundant_statements.rs index b9616ec72..a73ca7f68 100644 --- a/aderyn_core/src/detect/low/redundant_statements.rs +++ b/aderyn_core/src/detect/low/redundant_statements.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{Expression, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::GetImmediateParent; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::GetImmediateParent, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -94,15 +91,9 @@ mod redundant_statements_detector { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 6); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Redundant statements have no effect.") - ); + assert_eq!(detector.title(), String::from("Redundant statements have no effect.")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/require_with_string.rs b/aderyn_core/src/detect/low/require_with_string.rs index a5b231fdf..d6de47372 100644 --- a/aderyn_core/src/detect/low/require_with_string.rs +++ b/aderyn_core/src/detect/low/require_with_string.rs @@ -51,7 +51,7 @@ impl IssueDetector for RequireWithStringDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::RequireWithString) + format!("{}", IssueDetectorNamePool::RequireWithoutString) } } @@ -77,15 +77,9 @@ mod require_with_string_tests { // assert that the detector returns the correct number of instances assert_eq!(detector.instances().len(), 2); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title - assert_eq!( - detector.title(), - String::from("Empty `require()` / `revert()` statements") - ); + assert_eq!(detector.title(), String::from("Empty `require()` / `revert()` statements")); // assert that the detector returns the correct description assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/return_bomb.rs b/aderyn_core/src/detect/low/return_bomb.rs index c49ef4c91..bf1483fff 100644 --- a/aderyn_core/src/detect/low/return_bomb.rs +++ b/aderyn_core/src/detect/low/return_bomb.rs @@ -1,17 +1,19 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, MemberAccess, NodeID}; -use crate::ast::NodeType; -use crate::capture; -use crate::context::browser::GetClosestAncestorOfTypeX; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + ast::NodeType, + capture, + context::{ + browser::GetClosestAncestorOfTypeX, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -25,28 +27,34 @@ pub struct ReturnBombDetector { impl IssueDetector for ReturnBombDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // PLAN - // Look for calls on addresses that are unprotected. (non state variable address that has not undergone any binary checks) + // Look for calls on addresses that are unprotected. (non state variable address that has + // not undergone any binary checks) - // Capture the ones where no gas limit is explicitly set *and* there is a `returndatacopy` operation - // Basially you are checking for the 2nd element in the tuple - (bool success, bytes memory ret) which invokes the - // above operation. + // Capture the ones where no gas limit is explicitly set *and* there is a `returndatacopy` + // operation Basially you are checking for the 2nd element in the tuple - (bool + // success, bytes memory ret) which invokes the above operation. for func in helpers::get_implemented_external_and_public_functions(context) { let mut tracker = CallNoAddressChecksTracker { has_address_checks: false, - calls_on_non_state_variable_addresses: vec![], // collection of all `address.call` Member Accesses where address is not a state variable + calls_on_non_state_variable_addresses: vec![], /* collection of all + * `address.call` Member Accesses + * where address is not a state + * variable */ context, }; let callgraph = CallGraph::new(context, &[&(func.into())], CallGraphDirection::Inward)?; callgraph.accept(context, &mut tracker)?; if !tracker.has_address_checks { - // Now we assume that in this region all addresses are unprotected (because they are not involved in any binary ops/checks) + // Now we assume that in this region all addresses are unprotected (because they are + // not involved in any binary ops/checks) for member_access in tracker.calls_on_non_state_variable_addresses { - // Now we need to see if address.call{gas: xxx}() has been called with options and if so, - // scan to see if the gaslimit is set. If it is, then it is not a vulnerability because - // OOG is likely not possible when there is defined gas limit - // Therefore, continue the for loop and investigate other instances + // Now we need to see if address.call{gas: xxx}() has been called with options + // and if so, scan to see if the gaslimit is set. If it is, + // then it is not a vulnerability because OOG is likely not + // possible when there is defined gas limit Therefore, + // continue the for loop and investigate other instances if let Some(ASTNode::FunctionCallOptions(function_call_ops)) = member_access .closest_ancestor_of_type(context, NodeType::FunctionCallOptions) @@ -56,20 +64,23 @@ impl IssueDetector for ReturnBombDetector { } } - // Here, we know that there is no gas limit set for the call. So we need to only check - // for the cases where `returndatacopy` happens and then capture it. + // Here, we know that there is no gas limit set for the call. So we need to only + // check for the cases where `returndatacopy` happens and + // then capture it. if let Some(ASTNode::FunctionCall(function_call)) = member_access.closest_ancestor_of_type(context, NodeType::FunctionCall) { - // In this case there are no options like gas, etc, passed to the `address.call()` - // So we need to check if `returndatacopy` is triggered. If yes, then it is a problem + // In this case there are no options like gas, etc, passed to the + // `address.call()` So we need to check if + // `returndatacopy` is triggered. If yes, then it is a problem if let Some(ASTNode::Assignment(assignment)) = function_call.closest_ancestor_of_type(context, NodeType::Assignment) { - // The following check will ensure that the last paramter which is `bytes memory retData` - // is not unpacked. (there is nothing after comma) + // The following check will ensure that the last paramter which is + // `bytes memory retData` is not unpacked. + // (there is nothing after comma) if !assignment.left_hand_side.type_descriptions().is_some_and( |type_desc| { type_desc @@ -155,9 +166,6 @@ mod return_bomb_detector_tests { assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/reverts_and_requries_in_loops.rs b/aderyn_core/src/detect/low/reverts_and_requries_in_loops.rs index 937e5969b..b46e6e11f 100644 --- a/aderyn_core/src/detect/low/reverts_and_requries_in_loops.rs +++ b/aderyn_core/src/detect/low/reverts_and_requries_in_loops.rs @@ -1,14 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{NodeID, NodeType}; -use crate::capture; -use crate::context::browser::GetClosestAncestorOfTypeX; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::GetClosestAncestorOfTypeX, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -89,15 +86,9 @@ mod reevrts_and_requires_in_loops { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert the title is correct - assert_eq!( - detector.title(), - String::from("Loop contains `require`/`revert` statements") - ); + assert_eq!(detector.title(), String::from("Loop contains `require`/`revert` statements")); // assert the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/solmate_safe_transfer_lib.rs b/aderyn_core/src/detect/low/solmate_safe_transfer_lib.rs index c3940717f..552310b7c 100644 --- a/aderyn_core/src/detect/low/solmate_safe_transfer_lib.rs +++ b/aderyn_core/src/detect/low/solmate_safe_transfer_lib.rs @@ -18,17 +18,10 @@ pub struct SolmateSafeTransferLibDetector { impl IssueDetector for SolmateSafeTransferLibDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for import_directive in context.import_directives() { - // If the import directive absolute_path contains the strings "solmate" and "SafeTransferLib", flip the found_solmate_import flag to true - if import_directive - .absolute_path - .as_ref() - .unwrap() - .contains("solmate") - && import_directive - .absolute_path - .as_ref() - .unwrap() - .contains("SafeTransferLib") + // If the import directive absolute_path contains the strings "solmate" and + // "SafeTransferLib", flip the found_solmate_import flag to true + if import_directive.absolute_path.as_ref().unwrap().contains("solmate") + && import_directive.absolute_path.as_ref().unwrap().contains("SafeTransferLib") { capture!(self, context, import_directive); } @@ -78,10 +71,7 @@ mod solmate_safe_transfer_lib_tests { // assert that the detector found the correct number of instances (1) assert_eq!(detector.instances().len(), 1); // assert the severity is Low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/state_variable_changes_without_events.rs b/aderyn_core/src/detect/low/state_variable_changes_without_events.rs index 3638a5569..d883cafe1 100644 --- a/aderyn_core/src/detect/low/state_variable_changes_without_events.rs +++ b/aderyn_core/src/detect/low/state_variable_changes_without_events.rs @@ -1,16 +1,18 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{FunctionKind, NodeID}; -use crate::capture; -use crate::context::browser::ExtractEmitStatements; -use crate::context::graph::{CallGraph, CallGraphDirection, CallGraphVisitor}; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::ExtractEmitStatements, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -27,9 +29,7 @@ impl IssueDetector for StateVariableChangesWithoutEventDetector { if *func.kind() == FunctionKind::Constructor { continue; } - let mut event_tracker = EventEmissionTracker { - does_emit_events: false, - }; + let mut event_tracker = EventEmissionTracker { does_emit_events: false }; let investigator = CallGraph::new(context, &[&(func.into())], CallGraphDirection::Inward)?; @@ -67,10 +67,7 @@ impl IssueDetector for StateVariableChangesWithoutEventDetector { } fn name(&self) -> String { - format!( - "{}", - IssueDetectorNamePool::StateVariableChangesWithoutEvents - ) + format!("{}", IssueDetectorNamePool::StateVariableChangesWithoutEvents) } } @@ -122,9 +119,6 @@ mod state_variable_changes_without_events_tests { println!("{:?}", detector.instances()); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/state_variable_could_be_constant.rs b/aderyn_core/src/detect/low/state_variable_could_be_constant.rs index 1ae04508e..c9403f2a1 100644 --- a/aderyn_core/src/detect/low/state_variable_could_be_constant.rs +++ b/aderyn_core/src/detect/low/state_variable_could_be_constant.rs @@ -1,14 +1,14 @@ -use std::collections::{BTreeMap, HashSet}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; use crate::ast::{FunctionCallKind, Mutability, NodeID}; -use crate::capture; -use crate::context::browser::ExtractFunctionCalls; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractFunctionCalls, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; #[derive(Default)] @@ -21,8 +21,10 @@ pub struct StateVariableCouldBeConstantDetector { impl IssueDetector for StateVariableCouldBeConstantDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // PLAN - // 1. Collect all state variables that are not marked constant or immutable and are also not structs/mappings/contracts (collection A) - // 2. Investigate every function and collect all the state variables that could change (collection B) + // 1. Collect all state variables that are not marked constant or immutable and are also not + // structs/mappings/contracts (collection A) + // 2. Investigate every function and collect all the state variables that could change + // (collection B) // 3. Result = collection A - collection B let mut collection_a = Vec::new(); @@ -35,10 +37,7 @@ impl IssueDetector for StateVariableCouldBeConstantDetector { if let Some(rhs_value) = variable.value.as_ref() { let function_calls = ExtractFunctionCalls::from(rhs_value).extracted; - if function_calls - .iter() - .any(|f| f.kind == FunctionCallKind::FunctionCall) - { + if function_calls.iter().any(|f| f.kind == FunctionCallKind::FunctionCall) { continue; } } @@ -48,14 +47,9 @@ impl IssueDetector for StateVariableCouldBeConstantDetector { } // Do not report it if it's a struct / mapping - if variable - .type_descriptions - .type_string - .as_ref() - .is_some_and(|type_string| { - type_string.starts_with("mapping") || type_string.starts_with("struct") - }) - { + if variable.type_descriptions.type_string.as_ref().is_some_and(|type_string| { + type_string.starts_with("mapping") || type_string.starts_with("struct") + }) { continue; } @@ -112,10 +106,7 @@ impl IssueDetector for StateVariableCouldBeConstantDetector { } fn name(&self) -> String { - format!( - "{}", - IssueDetectorNamePool::StateVariableCouldBeDeclaredConstant - ) + format!("{}", IssueDetectorNamePool::StateVariableCouldBeDeclaredConstant) } } @@ -130,16 +121,13 @@ mod function_state_changes_finder_helper { }; impl FunctionDefinition { - /// Investigates the function with the help callgraph and accumulates all the state variables - /// that have been changed. + /// Investigates the function with the help callgraph and accumulates all the state + /// variables that have been changed. pub fn state_variable_changes<'a>( &self, context: &'a WorkspaceContext, ) -> Option> { - let mut tracker = StateVariableChangeTracker { - changes: None, - context, - }; + let mut tracker = StateVariableChangeTracker { changes: None, context }; let investigator = CallGraph::new(context, &[&(self.into())], CallGraphDirection::Inward).ok()?; @@ -193,9 +181,6 @@ mod state_variable_could_be_constant_tests { println!("{:?}", detector.instances()); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs new file mode 100644 index 000000000..563b9e80c --- /dev/null +++ b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs @@ -0,0 +1,181 @@ +use std::{collections::BTreeMap, error::Error}; + +use crate::ast::{FunctionKind, Mutability, NodeID}; + +use crate::{ + capture, + context::{browser::ApproximateStorageChangeFinder, workspace_context::WorkspaceContext}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, +}; + +#[derive(Default)] +pub struct StateVariableCouldBeImmutableDetector { + // Keys are: [0] source file name, [1] line number, [2] character location of node. + // Do not add items manually, use `capture!` to add nodes to this BTreeMap. + found_instances: BTreeMap<(String, usize, String), NodeID>, +} + +impl IssueDetector for StateVariableCouldBeImmutableDetector { + fn detect(&mut self, context: &WorkspaceContext) -> Result> { + // PLAN + // 1. Collect all state variables that are not marked constant or immutable and are also + // not structs/mappings/contracts (collection A) + // + // 2. Investigate every non constructor function and collect all the state variables that + // could change (collection B) + // + // 3. Investigate every constructor function and collect all the state variables that could + // change (Collection C) + // + // 4. Let collection R1 = collection C - collection B + // This represent subset of state variables that only change in the constructor + // + // 5. Let collection R2 = collection A intersection R1 + // This is the final result + + let mut collection_a = Vec::new(); + + for variable in context.variable_declarations() { + // If it's already marked immutable, ignore it! + if variable.mutability() == Some(&Mutability::Immutable) { + continue; + } + + // Doesn't make sense to look for possible immutability if it's already declared + // constant + if variable.mutability() == Some(&Mutability::Constant) { + continue; + } + + // If the variable has already been initialized at it's definition then, later when + // it's changed in the constructor, it cannot be marked immutable. + // + // This condition is opposite for detecting potentially constant variables. Over there, + // we had to make sure that variable _had_ a value at the time of initializing. + if variable.value.is_some() { + continue; + } + + // Do not report it if it's a struct / mapping + if variable.type_descriptions.type_string.as_ref().is_some_and(|type_string| { + type_string.starts_with("mapping") || type_string.starts_with("struct") + }) { + continue; + } + + if variable.overrides.is_some() { + continue; + } + + if variable.state_variable && !variable.constant { + collection_a.push(variable); + } + } + + let mut state_var_changed_from_non_constructors = None; + let mut state_var_changed_from_constructors = None; + + // Gather the state changes that happen from non constructor functions + for func in helpers::get_implemented_external_and_public_functions(context) { + if *func.kind() == FunctionKind::Constructor { + continue; + } + // Uses callgraph to explore inward + if let Some(delta) = func.state_variable_changes(context) { + if let Some(changes) = state_var_changed_from_non_constructors { + let new_changes = delta + changes; + state_var_changed_from_non_constructors = Some(new_changes); + } else { + state_var_changed_from_non_constructors = Some(delta); + } + } + } + + // Gather state changes that happen from constructor function only + for func in helpers::get_implemented_external_and_public_functions(context) { + if *func.kind() != FunctionKind::Constructor { + continue; + } + if func.compiles_for_solc_below_0_6_5(context) { + // The immutable keyword was introduced in 0.6.5 + continue; + } + // In the case of constructors, we shouldn't explore the callgraph due to the reasons + // stated in this detector's solidity test file + if let Some(changes) = state_var_changed_from_constructors { + let new_changes = ApproximateStorageChangeFinder::from(context, func) + changes; + state_var_changed_from_constructors = Some(new_changes); + } else { + state_var_changed_from_constructors = + Some(ApproximateStorageChangeFinder::from(context, func)); + } + } + + // Collection A intersection with (collection C - collection B) + if let (Some(collection_b), Some(collection_c)) = + (state_var_changed_from_non_constructors, state_var_changed_from_constructors) + { + let collection_c = collection_c.fetch_non_exhaustive_manipulated_state_variables(); + let collection_b = collection_b.fetch_non_exhaustive_manipulated_state_variables(); + for state_variable in collection_a { + if collection_c.contains(&state_variable) && !collection_b.contains(&state_variable) + { + capture!(self, context, state_variable); + } + } + } + + Ok(!self.found_instances.is_empty()) + } + + fn severity(&self) -> IssueSeverity { + IssueSeverity::Low + } + + fn title(&self) -> String { + String::from("State variable could be declared immutable") + } + + fn description(&self) -> String { + String::from("State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor") + } + + fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> { + self.found_instances.clone() + } + + fn name(&self) -> String { + format!("{}", IssueDetectorNamePool::StateVariableCouldBeDeclaredImmutable) + } +} + +#[cfg(test)] +mod state_variable_could_be_immutable_tests { + use serial_test::serial; + + use crate::detect::{ + detector::IssueDetector, + low::state_variable_could_be_immutable::StateVariableCouldBeImmutableDetector, + }; + + #[test] + #[serial] + fn test_state_variable_could_be_declared_immutable() { + let context = crate::detect::test_utils::load_solidity_source_unit( + "../tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol", + ); + + let mut detector = StateVariableCouldBeImmutableDetector::default(); + let found = detector.detect(&context).unwrap(); + // assert that the detector found an issue + assert!(found); + // assert that the detector found the correct number of instances + assert_eq!(detector.instances().len(), 2); + println!("{:?}", detector.instances()); + // assert the severity is low + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); + } +} diff --git a/aderyn_core/src/detect/low/unindexed_events.rs b/aderyn_core/src/detect/low/unindexed_events.rs index 653383dc6..3b90eff83 100644 --- a/aderyn_core/src/detect/low/unindexed_events.rs +++ b/aderyn_core/src/detect/low/unindexed_events.rs @@ -82,10 +82,7 @@ mod unindexed_event_tests { // assert that the detector finds the correct number of unindexed events assert_eq!(detector.instances().len(), 1); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!(detector.title(), "Event is missing `indexed` fields"); // assert that the detector returns the correct description diff --git a/aderyn_core/src/detect/low/uninitialized_local_variables.rs b/aderyn_core/src/detect/low/uninitialized_local_variables.rs index 16c3f580f..29980761b 100644 --- a/aderyn_core/src/detect/low/uninitialized_local_variables.rs +++ b/aderyn_core/src/detect/low/uninitialized_local_variables.rs @@ -1,14 +1,14 @@ -use std::collections::{BTreeMap, HashSet}; -use std::error::Error; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; use crate::ast::{ASTNode, NodeID}; -use crate::capture; -use crate::context::browser::ExtractReferencedDeclarations; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ExtractReferencedDeclarations, workspace_context::WorkspaceContext}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -22,8 +22,9 @@ pub struct UninitializedLocalVariableDetector { impl IssueDetector for UninitializedLocalVariableDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { // Assumption: - // VariableDeclarationStatements consists of statements that look like `uint x;` `uint y, z;`, `uint p = 12;` - // but are not declared at the contract level (state level) but rather within functions and modifiers + // VariableDeclarationStatements consists of statements that look like `uint x;` `uint y, + // z;`, `uint p = 12;` but are not declared at the contract level (state level) but + // rather within functions and modifiers let mut potentially_uninitialized_local_variables = HashSet::new(); @@ -33,21 +34,18 @@ impl IssueDetector for UninitializedLocalVariableDetector { .filter(|s| s.initial_value.is_none()) { potentially_uninitialized_local_variables.extend( - variable_declaration_statement - .declarations - .iter() - .flat_map(|s| { - if let Some(ref s) = s { - return Some(s.id); - } - None - }), + variable_declaration_statement.declarations.iter().flat_map(|s| { + if let Some(ref s) = s { + return Some(s.id); + } + None + }), ); } // We can filter out the initialized variables by looking at LHS of assignments. - // This trick works for local variables because it's not possible to have structs, mappings, dynamic arrays - // declared local to the function. + // This trick works for local variables because it's not possible to have structs, mappings, + // dynamic arrays declared local to the function. for assignment in context.assignments() { let references = ExtractReferencedDeclarations::from(assignment.left_hand_side.as_ref()).extracted; @@ -65,7 +63,8 @@ impl IssueDetector for UninitializedLocalVariableDetector { for id in potentially_uninitialized_local_variables { if let Some(ASTNode::VariableDeclaration(v)) = context.nodes.get(&id) { if !blacklist_variable_names.contains(&v.name) { - // Ignore memory structs because they can have an initializeMethod of their own. So not covered under the assignment operator + // Ignore memory structs because they can have an initializeMethod of their own. + // So not covered under the assignment operator if v.type_descriptions .type_string .as_ref() @@ -130,9 +129,6 @@ mod uninitialized_local_variables_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 12); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/unsafe_erc20_functions.rs b/aderyn_core/src/detect/low/unsafe_erc20_functions.rs index a6468df69..a1ce45845 100644 --- a/aderyn_core/src/detect/low/unsafe_erc20_functions.rs +++ b/aderyn_core/src/detect/low/unsafe_erc20_functions.rs @@ -71,15 +71,9 @@ mod unsafe_erc20_functions_tests { // failure0, failure1 and failure3 assert_eq!(detector.instances().len(), 5); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("Unsafe ERC20 Operations should not be used") - ); + assert_eq!(detector.title(), String::from("Unsafe ERC20 Operations should not be used")); // assert that the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/unsafe_oz_erc721_mint.rs b/aderyn_core/src/detect/low/unsafe_oz_erc721_mint.rs index eb40fe994..d63da7438 100644 --- a/aderyn_core/src/detect/low/unsafe_oz_erc721_mint.rs +++ b/aderyn_core/src/detect/low/unsafe_oz_erc721_mint.rs @@ -96,15 +96,9 @@ mod unsafe_erc721_mint_tests { // assert that the detector found the correct number of instance assert_eq!(detector.instances().len(), 1); // assert that the severity is Low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("Using `ERC721::_mint()` can be dangerous") - ); + assert_eq!(detector.title(), String::from("Using `ERC721::_mint()` can be dangerous")); // assert that the description is correct assert_eq!( detector.description(), diff --git a/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs b/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs index 72dbb42a7..014fa3c49 100644 --- a/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs +++ b/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs @@ -1,9 +1,12 @@ use std::{collections::BTreeMap, error::Error}; use crate::{ - ast::NodeID, + ast::{ContractKind, NodeID, NodeType}, capture, - context::workspace_context::WorkspaceContext, + context::{ + browser::{ExtractContractDefinitions, GetClosestAncestorOfTypeX}, + workspace_context::WorkspaceContext, + }, detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -18,8 +21,17 @@ pub struct UnspecificSolidityPragmaDetector { impl IssueDetector for UnspecificSolidityPragmaDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for pragma_directive in context.pragma_directives() { + let Some(source_unit) = + pragma_directive.closest_ancestor_of_type(context, NodeType::SourceUnit) + else { + continue; + }; + let contracts_in_source_unit = ExtractContractDefinitions::from(source_unit).extracted; + if contracts_in_source_unit.iter().any(|c| c.kind == ContractKind::Library) { + continue; + } for literal in &pragma_directive.literals { - if literal.contains('^') || literal.contains('>') { + if literal.contains('^') || literal.contains('>') || literal.contains('<') { capture!(self, context, pragma_directive); break; } @@ -71,15 +83,9 @@ mod unspecific_solidity_pragma_tests { // failure0, failure1 and failure3 assert_eq!(detector.instances().len(), 1); // assert that the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct - assert_eq!( - detector.title(), - String::from("Solidity pragma should be specific, not wide") - ); + assert_eq!(detector.title(), String::from("Solidity pragma should be specific, not wide")); // assert that the description is correct assert_eq!( detector.description(), @@ -88,4 +94,17 @@ mod unspecific_solidity_pragma_tests { ) ); } + + #[test] + #[serial] + fn test_unspecific_solidity_pragma_detector_by_loading_contract_directly_on_library() { + let context = crate::detect::test_utils::load_solidity_source_unit( + "../tests/contract-playground/src/OnlyLibrary.sol", + ); + + let mut detector = UnspecificSolidityPragmaDetector::default(); + let found = detector.detect(&context).unwrap(); + // assert that the detector found an abi encode packed + assert!(!found); + } } diff --git a/aderyn_core/src/detect/low/unused_imports.rs b/aderyn_core/src/detect/low/unused_imports.rs index 79bfa991e..4b3107cfc 100644 --- a/aderyn_core/src/detect/low/unused_imports.rs +++ b/aderyn_core/src/detect/low/unused_imports.rs @@ -1,16 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, NodeID, NodeType}; -use crate::capture; -use crate::context::browser::{ - ExtractReferencedDeclarationsConditionally, GetClosestAncestorOfTypeX, -}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ExtractReferencedDeclarationsConditionally, GetClosestAncestorOfTypeX}, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -179,17 +177,10 @@ mod source_unit_graph_analysis { let from_node = self .source_units .entry(from_source_unit) - .or_insert_with(|| GNode { - source_unit: to_source_unit, - edges: vec![], - }); + .or_insert_with(|| GNode { source_unit: to_source_unit, edges: vec![] }); // Create the relationship edge - let relationship = GEdge { - symbols, - to: to_source_unit, - import_statement, - }; + let relationship = GEdge { symbols, to: to_source_unit, import_statement }; from_node.edges.push(relationship); @@ -197,10 +188,7 @@ mod source_unit_graph_analysis { _ = self .source_units .entry(to_source_unit) - .or_insert_with(|| GNode { - source_unit: to_source_unit, - edges: vec![], - }); + .or_insert_with(|| GNode { source_unit: to_source_unit, edges: vec![] }); } pub fn mark_used_pathways( @@ -250,10 +238,7 @@ mod source_unit_graph_analysis { for node in self.source_units.values() { for relationship in &node.edges { - if !self - .useful_symbols - .contains_key(&relationship.import_statement) - { + if !self.useful_symbols.contains_key(&relationship.import_statement) { useless_imports.push(relationship.import_statement); } } @@ -289,9 +274,6 @@ mod unused_imports_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 2); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/unused_state_variable.rs b/aderyn_core/src/detect/low/unused_state_variable.rs index e780f56c8..d9e925998 100644 --- a/aderyn_core/src/detect/low/unused_state_variable.rs +++ b/aderyn_core/src/detect/low/unused_state_variable.rs @@ -1,17 +1,20 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::convert::identity; -use std::error::Error; +use std::{ + collections::{BTreeMap, BTreeSet}, + convert::identity, + error::Error, +}; use crate::ast::{ASTNode, ContractKind, NodeID, NodeType, Visibility}; -use crate::capture; -use crate::context::browser::{ - ExtractReferencedDeclarations, ExtractVariableDeclarations, GetClosestAncestorOfTypeX, -}; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{ + browser::{ + ExtractReferencedDeclarations, ExtractVariableDeclarations, GetClosestAncestorOfTypeX, + }, + workspace_context::WorkspaceContext, + }, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -53,7 +56,8 @@ impl IssueDetector for UnusedStateVariablesDetector { if let Some(ASTNode::ContractDefinition(contract)) = node.closest_ancestor_of_type(context, NodeType::ContractDefinition) { - // If this variable is defined inside a contract, make sure it's not an abstract contract before capturing it + // If this variable is defined inside a contract, make sure it's not an abstract + // contract before capturing it if !contract.is_abstract.is_some_and(identity) && contract.kind == ContractKind::Contract { @@ -113,9 +117,6 @@ mod unused_detector_tests { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 4); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/useless_error.rs b/aderyn_core/src/detect/low/useless_error.rs index a48db6d03..02935bd77 100644 --- a/aderyn_core/src/detect/low/useless_error.rs +++ b/aderyn_core/src/detect/low/useless_error.rs @@ -5,7 +5,10 @@ use crate::{ detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; -use std::{collections::BTreeMap, collections::HashSet, error::Error}; +use std::{ + collections::{BTreeMap, HashSet}, + error::Error, +}; #[derive(Default)] pub struct UselessErrorDetector { @@ -87,10 +90,7 @@ mod useless_error_tests { // Assert that the detector returns the correct number of instances assert_eq!(detector.instances().len(), 2); // Assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // Assert that the detector returns the correct title assert_eq!(detector.title(), String::from("Unused Custom Error")); // Assert that the detector returns the correct description diff --git a/aderyn_core/src/detect/low/useless_internal_function.rs b/aderyn_core/src/detect/low/useless_internal_function.rs index c670d103a..3c7b6c42b 100644 --- a/aderyn_core/src/detect/low/useless_internal_function.rs +++ b/aderyn_core/src/detect/low/useless_internal_function.rs @@ -20,13 +20,9 @@ pub struct UselessInternalFunctionDetector { impl IssueDetector for UselessInternalFunctionDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - let internal_functions = context - .function_definitions() - .into_iter() - .filter(|&function| { - matches!(function.visibility, Visibility::Internal) - && !function.name.starts_with('_') - }); + let internal_functions = context.function_definitions().into_iter().filter(|&function| { + matches!(function.visibility, Visibility::Internal) && !function.name.starts_with('_') + }); for internal_function in internal_functions { if count_identifiers_that_reference_an_id(context, internal_function.id) == 1 { @@ -80,10 +76,7 @@ mod uselss_internal_function { assert_eq!(detector.instances().len(), 1); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/useless_modifier.rs b/aderyn_core/src/detect/low/useless_modifier.rs index 1abef5774..287b894a4 100644 --- a/aderyn_core/src/detect/low/useless_modifier.rs +++ b/aderyn_core/src/detect/low/useless_modifier.rs @@ -85,10 +85,7 @@ mod useless_modifier_tests { // assert that the detector returns the correct number of instances assert_eq!(detector.instances().len(), 1); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/useless_public_function.rs b/aderyn_core/src/detect/low/useless_public_function.rs index a8da1af53..21064d4a0 100644 --- a/aderyn_core/src/detect/low/useless_public_function.rs +++ b/aderyn_core/src/detect/low/useless_public_function.rs @@ -24,14 +24,11 @@ pub struct UselessPublicFunctionDetector { impl IssueDetector for UselessPublicFunctionDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { let unreferenced_public_functions = - context - .function_definitions() - .into_iter() - .filter(|&function| { - matches!(function.visibility, Visibility::Public) - && !matches!(function.kind(), &FunctionKind::Constructor) - && count_identifiers_that_reference_an_id(context, function.id) == 0 - }); + context.function_definitions().into_iter().filter(|&function| { + matches!(function.visibility, Visibility::Public) + && !matches!(function.kind(), &FunctionKind::Constructor) + && count_identifiers_that_reference_an_id(context, function.id) == 0 + }); for unreferenced_public_function in unreferenced_public_functions { if let Some(ASTNode::ContractDefinition(parent_contract)) = unreferenced_public_function @@ -91,10 +88,7 @@ mod useless_public_function_tests { // assert that the detector returns the correct number of instances assert_eq!(detector.instances().len(), 1); // assert that the detector returns the correct severity - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the detector returns the correct title assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/void_constructor.rs b/aderyn_core/src/detect/low/void_constructor.rs index 73f72563e..f36ca7f36 100644 --- a/aderyn_core/src/detect/low/void_constructor.rs +++ b/aderyn_core/src/detect/low/void_constructor.rs @@ -1,13 +1,11 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{ASTNode, FunctionKind, ModifierInvocationKind, NodeID}; -use crate::capture; -use crate::detect::detector::IssueDetectorNamePool; use crate::{ + capture, context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, }; use eyre::Result; @@ -100,9 +98,6 @@ mod template_void_constructors { // assert that the detector found the correct number of instances assert_eq!(detector.instances().len(), 1); // assert the severity is low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); } } diff --git a/aderyn_core/src/detect/low/zero_address_check.rs b/aderyn_core/src/detect/low/zero_address_check.rs index 2e0312117..0bcfe8ad0 100644 --- a/aderyn_core/src/detect/low/zero_address_check.rs +++ b/aderyn_core/src/detect/low/zero_address_check.rs @@ -34,11 +34,7 @@ impl IssueDetector for ZeroAddressCheckDetector { if !var_decl.constant && matches!(var_decl.mutability(), Some(Mutability::Mutable)) && var_decl.state_variable - && (var_decl - .type_descriptions - .type_string - .as_deref() - .unwrap_or("") + && (var_decl.type_descriptions.type_string.as_deref().unwrap_or("") == "address" || var_decl .type_descriptions @@ -47,7 +43,8 @@ impl IssueDetector for ZeroAddressCheckDetector { .unwrap_or("") .contains("contract ")) { - Some((var_decl.id, (*var_decl).clone())) // Deref and clone the VariableDeclaration. + Some((var_decl.id, (*var_decl).clone())) // Deref and clone the + // VariableDeclaration. } else { None } @@ -106,28 +103,23 @@ impl IssueDetector for ZeroAddressCheckDetector { .filter(|x| { let left_hand_side = x.left_hand_side.as_ref(); if let Expression::Identifier(left_identifier) = left_hand_side { - left_identifier - .referenced_declaration - .is_some_and(|reference_id| { - self.mutable_address_state_variables - .contains_key(&reference_id) - }) + left_identifier.referenced_declaration.is_some_and(|reference_id| { + self.mutable_address_state_variables.contains_key(&reference_id) + }) } else { let left_identifiers = ExtractIdentifiers::from(left_hand_side).extracted; left_identifiers.into_iter().any(|identifier| { - identifier - .referenced_declaration - .is_some_and(|reference_id| { - self.mutable_address_state_variables - .contains_key(&reference_id) - }) + identifier.referenced_declaration.is_some_and(|reference_id| { + self.mutable_address_state_variables.contains_key(&reference_id) + }) }) } }) .collect(); - // For each assignment, if the right hand side is in the identifier_reference_declaration_ids_in_binary_checks - // and is also in the Function.parameters, then add the assignment to the found_instances + // For each assignment, if the right hand side is in the + // identifier_reference_declaration_ids_in_binary_checks and is also in the + // Function.parameters, then add the assignment to the found_instances for assignment in assignments { if let Expression::Identifier(right_identifier) = &*assignment.right_hand_side { if let Some(reference_id) = right_identifier.referenced_declaration { @@ -184,7 +176,7 @@ impl IssueDetector for ZeroAddressCheckDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::ZeroAddressCheck) + format!("{}", IssueDetectorNamePool::NoZeroAddressCheck) } } @@ -237,10 +229,7 @@ mod zero_address_check_tests { } } // assert that the severity is Low - assert_eq!( - detector.severity(), - crate::detect::detector::IssueSeverity::Low - ); + assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::Low); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/mod.rs b/aderyn_core/src/detect/mod.rs index a1a375501..021f8eec0 100644 --- a/aderyn_core/src/detect/mod.rs +++ b/aderyn_core/src/detect/mod.rs @@ -11,22 +11,19 @@ pub mod test_utils; macro_rules! capture { ($self:ident, $context:ident, $item:expr) => { if let Some(id) = $context.get_node_id_of_capturable(&$item.clone().into()) { - $self.found_instances.insert( - $context.get_node_sort_key_from_capturable(&$item.clone().into()), - id, - ); + $self + .found_instances + .insert($context.get_node_sort_key_from_capturable(&$item.clone().into()), id); } else { - $self.found_instances.insert( - $context.get_node_sort_key_from_capturable(&$item.clone().into()), - 0, - ); + $self + .found_instances + .insert($context.get_node_sort_key_from_capturable(&$item.clone().into()), 0); } }; ($self:ident, $context:ident, $item:expr, $hint:tt) => { - $self.hints.insert( - $context.get_node_sort_key_from_capturable(&$item.clone().into()), - $hint, - ); + $self + .hints + .insert($context.get_node_sort_key_from_capturable(&$item.clone().into()), $hint); capture!($self, $context, $item); }; } diff --git a/aderyn_core/src/detect/test_utils/load_source_unit.rs b/aderyn_core/src/detect/test_utils/load_source_unit.rs index df11f8588..89f81c72b 100644 --- a/aderyn_core/src/detect/test_utils/load_source_unit.rs +++ b/aderyn_core/src/detect/test_utils/load_source_unit.rs @@ -7,9 +7,12 @@ use std::{ use crate::{ ast::SourceUnit, - context::{graph::Transpose, workspace_context::WorkspaceContext}, + context::{ + graph::{Transpose, WorkspaceCallGraph}, + workspace_context::WorkspaceContext, + }, + visitor::ast_visitor::Node, }; -use crate::{context::graph::WorkspaceCallGraph, visitor::ast_visitor::Node}; use super::ensure_valid_solidity_file; @@ -21,21 +24,13 @@ pub fn load_solidity_source_unit(filepath: &str) -> WorkspaceContext { let compiler_input = CompilerInput::new(solidity_file.as_path()).unwrap(); let compiler_input = compiler_input.first().unwrap(); // There's only 1 file in the path - let version = Solc::detect_version(&Source { - content: Arc::new(solidity_content.clone()), - }) - .unwrap(); + let version = + Solc::detect_version(&Source { content: Arc::new(solidity_content.clone()) }).unwrap(); let solc = Solc::find_or_install_svm_version(format!("{}", version)).unwrap(); let solc_bin = solc.solc.to_str().unwrap(); - let file_arg = compiler_input - .sources - .first_key_value() - .unwrap() - .0 - .to_str() - .unwrap(); + let file_arg = compiler_input.sources.first_key_value().unwrap().0.to_str().unwrap(); let command = Command::new(solc_bin) .args(["--ast-compact-json", file_arg]) @@ -93,9 +88,8 @@ pub fn load_solidity_source_unit(filepath: &str) -> WorkspaceContext { fn load_callgraphs(context: &mut WorkspaceContext) { let inward_callgraph = WorkspaceCallGraph::from_context(context).unwrap(); - let outward_callgraph = WorkspaceCallGraph { - raw_callgraph: inward_callgraph.raw_callgraph.reverse(), - }; + let outward_callgraph = + WorkspaceCallGraph { raw_callgraph: inward_callgraph.raw_callgraph.reverse() }; context.inward_callgraph = Some(inward_callgraph); context.outward_callgraph = Some(outward_callgraph); } @@ -149,10 +143,9 @@ pub fn load_multiple_solidity_source_units_into_single_context( let file_arg = std::fs::canonicalize(solidity_file).unwrap(); let file_arg = file_arg.to_string_lossy().to_string(); - let version = Solc::detect_version(&Source { - content: Arc::new(this_solidity_content.clone()), - }) - .unwrap(); + let version = + Solc::detect_version(&Source { content: Arc::new(this_solidity_content.clone()) }) + .unwrap(); let solc = Solc::find_or_install_svm_version(format!("{}", version)).unwrap(); let this_solc_bin = solc.solc.to_string_lossy().to_string(); diff --git a/aderyn_core/src/detect/test_utils/mod.rs b/aderyn_core/src/detect/test_utils/mod.rs index 10a76c7fd..0b8252139 100644 --- a/aderyn_core/src/detect/test_utils/mod.rs +++ b/aderyn_core/src/detect/test_utils/mod.rs @@ -5,8 +5,9 @@ use once_cell::sync::Lazy; use std::path::PathBuf; // Using `solc` to read AST given a source unit (i.e Solidity file) -pub use load_source_unit::load_multiple_solidity_source_units_into_single_context; -pub use load_source_unit::load_solidity_source_unit; +pub use load_source_unit::{ + load_multiple_solidity_source_units_into_single_context, load_solidity_source_unit, +}; pub(crate) fn take_loader_lock() -> impl Drop { static LOCK: Lazy> = Lazy::new(|| std::sync::Mutex::new(())); @@ -27,10 +28,7 @@ fn ensure_valid_solidity_file(filepath: &str) -> PathBuf { }); if extension != "sol" { - eprintln!( - "Please make sure {} represents a solidity file!", - filepath.to_string_lossy() - ); + eprintln!("Please make sure {} represents a solidity file!", filepath.to_string_lossy()); std::process::exit(1); } diff --git a/aderyn_core/src/fscloc/cloc.rs b/aderyn_core/src/fscloc/cloc.rs index 08a2bac4b..6c3ce5fee 100644 --- a/aderyn_core/src/fscloc/cloc.rs +++ b/aderyn_core/src/fscloc/cloc.rs @@ -35,18 +35,13 @@ pub struct TokenInsightGroup { impl TokenInsightGroup { fn last_token_insight_has_code_in_its_last_line(&self) -> bool { - self.token_insights - .last() - .is_some_and(|insight| insight.code_lines.last_line_has_code) + self.token_insights.last().is_some_and(|insight| insight.code_lines.last_line_has_code) } } pub fn get_stats(r_content: &str, skip_cloc: bool) -> Stats { if r_content.is_empty() { - return Stats { - code: 0, - ignore_lines: vec![], - }; + return Stats { code: 0, ignore_lines: vec![] }; } let token_descriptors = tokenize(r_content); @@ -58,7 +53,8 @@ pub fn get_stats(r_content: &str, skip_cloc: bool) -> Stats { let insights = token_descriptors .iter() .inspect(|x| { - content.push_str(&x.content); // will be used to verify if original content is preserved + content.push_str(&x.content); // will be used to verify if original content is + // preserved }) .map(|tok_dsc| tok_dsc.into()) .collect::>(); @@ -132,10 +128,7 @@ pub fn get_stats(r_content: &str, skip_cloc: bool) -> Stats { let len = groups.len(); if len == 0 { - return Stats { - code: 0, - ignore_lines: vec![], - }; + return Stats { code: 0, ignore_lines: vec![] }; } let mut prev = &groups[0]; @@ -164,10 +157,7 @@ pub fn get_stats(r_content: &str, skip_cloc: bool) -> Stats { let ignore_lines = get_lines_to_ignore(&token_descriptors); - Stats { - code: code_lines, - ignore_lines, - } + Stats { code: code_lines, ignore_lines } } fn get_lines_to_ignore(token_descriptors: &Vec) -> Vec { @@ -209,15 +199,9 @@ fn get_lines_to_ignore(token_descriptors: &Vec) -> Vec CodeLines { info_lines: 0, // we don't care about these values diff --git a/aderyn_core/src/fscloc/mod.rs b/aderyn_core/src/fscloc/mod.rs index c51e1bbae..fac1a24f4 100644 --- a/aderyn_core/src/fscloc/mod.rs +++ b/aderyn_core/src/fscloc/mod.rs @@ -29,9 +29,7 @@ mod cloc_tests { false, ); let result = sol.lock().unwrap(); - result - .iter() - .for_each(|element| println!("{} - {}", element.0, element.1.code)); + result.iter().for_each(|element| println!("{} - {}", element.0, element.1.code)); assert_eq!( result .get("../tests/contract-playground/src/cloc/HeavilyCommentedContract.sol") diff --git a/aderyn_core/src/lib.rs b/aderyn_core/src/lib.rs index b0925aecb..f9b560a3b 100644 --- a/aderyn_core/src/lib.rs +++ b/aderyn_core/src/lib.rs @@ -15,19 +15,17 @@ use prettytable::Row; use rayon::iter::{ IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, }; -use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, HashMap}; -use std::error::Error; -use std::fs::{remove_file, File}; -use std::io::{self}; -use std::path::{Path, PathBuf}; +use std::{ + collections::{btree_map::Entry, BTreeMap, HashMap}, + error::Error, + fs::{remove_file, File}, + io::{self}, + path::{Path, PathBuf}, +}; -use crate::context::workspace_context::WorkspaceContext; -use crate::detect::detector::IssueSeverity; +use crate::{context::workspace_context::WorkspaceContext, detect::detector::IssueSeverity}; -use crate::report::printer::ReportPrinter; -use crate::report::reporter::Report; -use crate::report::Issue; +use crate::report::{printer::ReportPrinter, reporter::Report, Issue}; #[allow(clippy::too_many_arguments)] pub fn run( @@ -122,24 +120,112 @@ pub fn get_report( let collection_of_instances = contexts .into_par_iter() - .flat_map(|context| { + .map(|context| { let mut d = detector.skeletal_clone(); if let Ok(found) = d.detect(context) { if found { let instances = d.instances(); let hints = d.hints(); - return Some((instances, hints)); + return (instances, hints, context.src_filepaths.clone()); } } - None + (Default::default(), Default::default(), context.src_filepaths.clone()) }) .collect::>(); - for (instances, hints) in collection_of_instances { - detectors_instances.extend(instances); + // Commit detector instances + // + // NOTE: Possible merge conflict here + // + // For a given detector D, in a file F, + // + // Context C1 captures instances A, B, C + // Context C2 captures instances B, C, D + // + // This is a conflict! + // + // We need a strategy to resolve this and it depends on the detector + // + // For example, if the detector determines that A, B, C are immutable when considering + // one set of files but B, C, D when considering another set of files, it is only safe + // to conclude that the B, C are immutable. + // + // Such a technique to resolve this conflict would be called INTERSECTION strategy + // + // Alternative way would be UNION strategy + // + + // NOTE: Intersection strategy logic + #[allow(clippy::complexity)] + let mut grouped_instances: BTreeMap< + String, + Vec>, + > = Default::default(); + + for (instances, hints, src_filepaths) in collection_of_instances { + let mut grouped_instances_context: BTreeMap< + String, + BTreeMap<(String, usize, String), i64>, + > = BTreeMap::new(); + + for (key, value) in instances { + match grouped_instances_context.entry(key.0.clone()) { + Entry::Vacant(v) => { + let mut mini_btree = BTreeMap::new(); + mini_btree.insert(key, value); + v.insert(mini_btree); + } + Entry::Occupied(mut o) => { + o.get_mut().insert(key, value); + } + }; + } + + for key in src_filepaths { + if let Entry::Vacant(v) = grouped_instances_context.entry(key) { + v.insert(Default::default()); + } + } + + for (key, value) in grouped_instances_context { + match grouped_instances.entry(key.clone()) { + Entry::Vacant(v) => { + v.insert(vec![value]); + } + Entry::Occupied(mut o) => { + o.get_mut().push(value); + } + } + } + detector_hints.extend(hints); } + for (_filename, value) in grouped_instances { + // Find the common instances across all the contexts' BTrees. + + let mut selected_instances = BTreeMap::new(); + + for instances in &value { + for instance in instances { + if value.iter().all(|tree| tree.contains_key(&instance.0.clone())) { + selected_instances.insert(instance.0.clone(), *instance.1); + } + } + } + + detectors_instances.extend(selected_instances); + } + // NOTE: Union strategy would work something like this + // + // for (instances, hints, _src_filepaths) in collection_of_instances.into_iter() { + // if instances.is_empty() { + // continue; + // } + // detectors_instances.extend(instances); + // detector_hints.extend(hints); + // } + if detectors_instances.is_empty() { return None; } @@ -223,10 +309,8 @@ where println!("Running {} detectors", detectors.len()); - let detectors_used = &detectors - .iter() - .map(|d| (d.name(), d.severity().to_string())) - .collect::>(); + let detectors_used = + &detectors.iter().map(|d| (d.name(), d.severity().to_string())).collect::>(); println!("Detectors run, processing found issues"); let report = get_report(contexts, &root_rel_path, detectors)?; diff --git a/aderyn_core/src/report/json_printer.rs b/aderyn_core/src/report/json_printer.rs index 745b3ed39..e742a7e6d 100644 --- a/aderyn_core/src/report/json_printer.rs +++ b/aderyn_core/src/report/json_printer.rs @@ -53,9 +53,7 @@ impl ReportPrinter<()> for JsonPrinter { all_files_details = all_files_details + &context.files_details(); } - all_files_details - .files_details - .sort_by(|a, b| a.file_path.cmp(&b.file_path)); + all_files_details.files_details.sort_by(|a, b| a.file_path.cmp(&b.file_path)); let mut all_files_summary = FilesSummary::default(); for details in &all_files_details.files_details { diff --git a/aderyn_core/src/report/markdown_printer.rs b/aderyn_core/src/report/markdown_printer.rs index 86282e7e6..716691bd0 100644 --- a/aderyn_core/src/report/markdown_printer.rs +++ b/aderyn_core/src/report/markdown_printer.rs @@ -33,16 +33,8 @@ impl ReportPrinter<()> for MarkdownReportPrinter { let output_rel_path = output_rel_path.unwrap(); let all_issues = vec![ - ( - report.high_issues(file_contents).issues, - "# High Issues\n", - "H", - ), - ( - report.low_issues(file_contents).issues, - "# Low Issues\n", - "L", - ), + (report.high_issues(file_contents).issues, "# High Issues\n", "H"), + (report.low_issues(file_contents).issues, "# Low Issues\n", "L"), ]; for (issues, heading, severity) in all_issues { @@ -118,11 +110,7 @@ impl MarkdownReportPrinter { // Start the markdown table writeln!(writer, "| Key | Value |")?; writeln!(writer, "| --- | --- |")?; - writeln!( - writer, - "| .sol Files | {} |", - all_files_summary.total_source_units - )?; + writeln!(writer, "| .sol Files | {} |", all_files_summary.total_source_units)?; writeln!(writer, "| Total nSLOC | {} |", all_files_summary.total_sloc)?; writeln!(writer, "\n")?; // Add an extra newline for spacing @@ -137,19 +125,13 @@ impl MarkdownReportPrinter { writeln!(writer, "| --- | --- |")?; let mut files_details = all_files_details; - files_details - .files_details - .sort_by(|a, b| a.file_path.cmp(&b.file_path)); + files_details.files_details.sort_by(|a, b| a.file_path.cmp(&b.file_path)); files_details.files_details.iter().for_each(|detail| { writeln!(writer, "| {} | {} |", detail.file_path, detail.n_sloc).unwrap(); }); - writeln!( - writer, - "| **Total** | **{}** |", - all_files_summary.total_sloc - )?; + writeln!(writer, "| **Total** | **{}** |", all_files_summary.total_sloc)?; writeln!(writer, "\n")?; // Add an extra newline for spacing } @@ -287,12 +269,7 @@ impl MarkdownReportPrinter { let line = file_data.get(&instance.contract_path).unwrap(); - let line_preview = line - .split('\n') - .skip(instance.line_no - 1) - .take(1) - .next() - .unwrap(); + let line_preview = line.split('\n').skip(instance.line_no - 1).take(1).next().unwrap(); if let Some(hint) = instance.hint.as_ref() { writeln!( diff --git a/aderyn_core/src/report/mod.rs b/aderyn_core/src/report/mod.rs index a7a42a036..580f5c92b 100644 --- a/aderyn_core/src/report/mod.rs +++ b/aderyn_core/src/report/mod.rs @@ -41,11 +41,7 @@ impl Add<&FilesDetails> for FilesDetails { type Output = FilesDetails; fn add(mut self, rhs: &FilesDetails) -> Self::Output { for fd in &rhs.files_details { - if self - .files_details - .iter() - .all(|x| x.file_path != fd.file_path) - { + if self.files_details.iter().all(|x| x.file_path != fd.file_path) { self.files_details.push(fd.clone()); } } @@ -192,10 +188,7 @@ impl WorkspaceContext { let filepath = source_unit.absolute_path.as_ref()?; if seen_paths.insert(filepath.clone()) { let report = sloc_stats.iter().find(|r| r.0.contains(filepath))?; - Some(FilesDetail { - file_path: filepath.to_owned(), - n_sloc: *report.1, - }) + Some(FilesDetail { file_path: filepath.to_owned(), n_sloc: *report.1 }) } else { None } diff --git a/aderyn_core/src/report/printer.rs b/aderyn_core/src/report/printer.rs index 3e662d38e..38908e58e 100644 --- a/aderyn_core/src/report/printer.rs +++ b/aderyn_core/src/report/printer.rs @@ -16,7 +16,8 @@ pub trait ReportPrinter { report: &Report, contexts: &[WorkspaceContext], root_rel_path: PathBuf, - output_rel_path: Option, // you writer 'W' may or may not be writing a file. Eg: it can simply consume and forget :P + output_rel_path: Option, /* you writer 'W' may or may not be writing a file. Eg: + * it can simply consume and forget :P */ no_snippets: bool, stdout: bool, detectors_used: &[(String, String)], diff --git a/aderyn_core/src/report/reporter.rs b/aderyn_core/src/report/reporter.rs index 438c23169..38dfaa765 100644 --- a/aderyn_core/src/report/reporter.rs +++ b/aderyn_core/src/report/reporter.rs @@ -10,21 +10,14 @@ pub struct Report { impl Report { pub fn issue_count(&self) -> IssueCount { - IssueCount { - high: self.highs.len(), - low: self.lows.len(), - } + IssueCount { high: self.highs.len(), low: self.lows.len() } } pub fn high_issues(&self, file_contents: &HashMap) -> HighIssues { - HighIssues { - issues: extract_issue_bodies(&self.highs, file_contents), - } + HighIssues { issues: extract_issue_bodies(&self.highs, file_contents) } } pub fn low_issues(&self, file_contents: &HashMap) -> LowIssues { - LowIssues { - issues: extract_issue_bodies(&self.lows, file_contents), - } + LowIssues { issues: extract_issue_bodies(&self.lows, file_contents) } } } diff --git a/aderyn_core/src/report/sarif_printer.rs b/aderyn_core/src/report/sarif_printer.rs index 69ad6c661..22a87dde8 100644 --- a/aderyn_core/src/report/sarif_printer.rs +++ b/aderyn_core/src/report/sarif_printer.rs @@ -209,11 +209,8 @@ fn create_sarif_results(report: &Report) -> Vec { fn create_sarif_locations(issue: &Issue) -> Vec { let mut locations: Vec = Vec::new(); for ((filename, _line_number, source_location), _value) in issue.instances.iter() { - let hint = issue.hints.get(&( - filename.to_string(), - *_line_number, - source_location.to_string(), - )); + let hint = + issue.hints.get(&(filename.to_string(), *_line_number, source_location.to_string())); let message = { if hint.is_some() { diff --git a/aderyn_core/src/report/util.rs b/aderyn_core/src/report/util.rs index ecec49266..dc55da31b 100644 --- a/aderyn_core/src/report/util.rs +++ b/aderyn_core/src/report/util.rs @@ -41,10 +41,9 @@ pub fn carve_shortest_path(from_file: PathBuf, to_file: PathBuf) -> PathBuf { // Now, we are at the common place // High level 2 step plan to get to the `to_file` - // 1. Do '../' until you reach a common place - // |==> you can reverse this problem (since we only care about no. of steps) - // |==> ask how many directories forward you should go to reach `from_file` - // |==> That's how many times you must come back! + // 1. Do '../' until you reach a common place |==> you can reverse this problem (since we only + // care about no. of steps) |==> ask how many directories forward you should go to reach + // `from_file` |==> That's how many times you must come back! // 2. Now, go forward till you reach the `to_file` // STEP 1 @@ -63,9 +62,7 @@ pub fn carve_shortest_path(from_file: PathBuf, to_file: PathBuf) -> PathBuf { curr_ffc = from_file_comps.next(); } - let mut backward_comps = (0..count_back) - .map(|_| Component::ParentDir) - .collect::>(); + let mut backward_comps = (0..count_back).map(|_| Component::ParentDir).collect::>(); // STEP 2 // Now, let's capture the forward path for `to_file` @@ -78,10 +75,7 @@ pub fn carve_shortest_path(from_file: PathBuf, to_file: PathBuf) -> PathBuf { // Finally, concatenate both components backward_comps.extend(forward_comps.iter()); - let final_route = backward_comps - .iter() - .map(|c| c.as_os_str()) - .collect::(); + let final_route = backward_comps.iter().map(|c| c.as_os_str()).collect::(); final_route } diff --git a/aderyn_core/src/visitor/workspace_visitor.rs b/aderyn_core/src/visitor/workspace_visitor.rs index ec48735c7..803b43dfc 100644 --- a/aderyn_core/src/visitor/workspace_visitor.rs +++ b/aderyn_core/src/visitor/workspace_visitor.rs @@ -1,13 +1,16 @@ -use super::ast_visitor::ASTConstVisitor; -use super::macros::generate_visit_methods_for_workspace_context_with_insert_node; -use crate::ast::*; -use crate::context::workspace_context::{NodeContext, WorkspaceContext}; +use super::{ + ast_visitor::ASTConstVisitor, + macros::generate_visit_methods_for_workspace_context_with_insert_node, +}; +use crate::{ + ast::*, + context::workspace_context::{NodeContext, WorkspaceContext}, +}; use eyre::Result; impl ASTConstVisitor for WorkspaceContext { fn visit_contract_definition(&mut self, node: &ContractDefinition) -> Result { - self.nodes - .insert(node.id, ASTNode::ContractDefinition(node.clone())); + self.nodes.insert(node.id, ASTNode::ContractDefinition(node.clone())); self.contract_definitions_context.insert( node.clone(), NodeContext { @@ -27,8 +30,7 @@ impl ASTConstVisitor for WorkspaceContext { } fn visit_function_definition(&mut self, node: &FunctionDefinition) -> Result { - self.nodes - .insert(node.id, ASTNode::FunctionDefinition(node.clone())); + self.nodes.insert(node.id, ASTNode::FunctionDefinition(node.clone())); self.function_definitions_context.insert( node.clone(), NodeContext { @@ -48,8 +50,7 @@ impl ASTConstVisitor for WorkspaceContext { } fn visit_modifier_definition(&mut self, node: &ModifierDefinition) -> Result { - self.nodes - .insert(node.id, ASTNode::ModifierDefinition(node.clone())); + self.nodes.insert(node.id, ASTNode::ModifierDefinition(node.clone())); self.modifier_definitions_context.insert( node.clone(), NodeContext { @@ -69,8 +70,7 @@ impl ASTConstVisitor for WorkspaceContext { } fn visit_source_unit(&mut self, node: &SourceUnit) -> Result { - self.nodes - .insert(node.id, ASTNode::SourceUnit(node.clone())); + self.nodes.insert(node.id, ASTNode::SourceUnit(node.clone())); self.source_units_context.push(node.clone()); self.last_source_unit_id = node.id; Ok(true) diff --git a/aderyn_driver/src/config_helpers.rs b/aderyn_driver/src/config_helpers.rs index 63f2da21e..081b042e7 100644 --- a/aderyn_driver/src/config_helpers.rs +++ b/aderyn_driver/src/config_helpers.rs @@ -136,13 +136,7 @@ fn interpret_aderyn_config( } } - ( - local_root, - local_src, - local_exclude, - local_remappings, - local_include, - ) + (local_root, local_src, local_exclude, local_remappings, local_include) } /// Append the src, remappings, and exclude from the foundry.toml file. @@ -188,11 +182,7 @@ fn interpret_foundry_config( let mut local_exclude = exclude.clone(); let script = format!("{}/", config.script.to_string_lossy()); let test = format!("{}/", config.test.to_string_lossy()); - let libs = config - .libs - .iter() - .map(|x| format!("{}/", x.to_string_lossy())) - .collect::>(); + let libs = config.libs.iter().map(|x| format!("{}/", x.to_string_lossy())).collect::>(); if let Some(local_exclude) = &mut local_exclude { local_exclude.push(test); local_exclude.push(script); @@ -208,19 +198,11 @@ fn interpret_foundry_config( // remappings let mut local_remappings = remappings.clone(); if let Some(local_remappings) = &mut local_remappings { - local_remappings.extend( - config - .get_all_remappings() - .map(|x| x.to_string()) - .collect::>(), - ); + local_remappings + .extend(config.get_all_remappings().map(|x| x.to_string()).collect::>()); } else { - local_remappings = Some( - config - .get_all_remappings() - .map(|x| x.to_string()) - .collect::>(), - ); + local_remappings = + Some(config.get_all_remappings().map(|x| x.to_string()).collect::>()); } (local_src, local_exclude, local_remappings) @@ -244,22 +226,13 @@ mod tests { let root = std::path::Path::new("ARG_ROOT"); let src = Some(vec!["ARG_SRC".to_string()]); - let exclude = Some(vec![ - "ARG_EXCLUDE_1".to_string(), - "ARG_EXCLUDE_2".to_string(), - ]); - let remappings = Some(vec![ - "ARG_REMAPPINGS_1".to_string(), - "ARG_REMAPPINGS_2".to_string(), - ]); + let exclude = Some(vec!["ARG_EXCLUDE_1".to_string(), "ARG_EXCLUDE_2".to_string()]); + let remappings = Some(vec!["ARG_REMAPPINGS_1".to_string(), "ARG_REMAPPINGS_2".to_string()]); let include = Some(vec!["ARG_SCOPE_1".to_string(), "ARG_SCOPE_2".to_string()]); let result = super::interpret_aderyn_config(config, root, &src, &exclude, &remappings, &include); assert_eq!(result.0, std::path::Path::new("ARG_ROOT/CONFIG_ROOT")); - assert_eq!( - result.1, - Some(vec!["ARG_SRC".to_string(), "CONFIG_SRC".to_string()]) - ); + assert_eq!(result.1, Some(vec!["ARG_SRC".to_string(), "CONFIG_SRC".to_string()])); assert_eq!( result.2, Some(vec![ @@ -303,14 +276,10 @@ mod tests { config.remappings = vec![rel_remap]; let src = Some(vec!["ADERYN_SRC".to_string()]); - let exclude: Option> = Some(vec![ - "ADERYN_EXCLUDE_1".to_string(), - "ADERYN_EXCLUDE_2".to_string(), - ]); - let remappings = Some(vec![ - "ADERYN_REMAPPINGS_1".to_string(), - "ADERYN_REMAPPINGS_2".to_string(), - ]); + let exclude: Option> = + Some(vec!["ADERYN_EXCLUDE_1".to_string(), "ADERYN_EXCLUDE_2".to_string()]); + let remappings = + Some(vec!["ADERYN_REMAPPINGS_1".to_string(), "ADERYN_REMAPPINGS_2".to_string()]); let result = super::interpret_foundry_config(config, &src, &exclude, &remappings); assert_eq!(result.0, Some(vec!["ADERYN_SRC".to_string()])); diff --git a/aderyn_driver/src/driver.rs b/aderyn_driver/src/driver.rs index cf2fe23a6..da79c75c6 100644 --- a/aderyn_driver/src/driver.rs +++ b/aderyn_driver/src/driver.rs @@ -203,18 +203,14 @@ fn make_context(args: &Args) -> WorkspaceContextWrapper { context.set_ignore_lines_stats(ignore_line_stats); let inward_callgraph = WorkspaceCallGraph::from_context(context).unwrap(); - let outward_callgraph = WorkspaceCallGraph { - raw_callgraph: inward_callgraph.raw_callgraph.reverse(), - }; + let outward_callgraph = + WorkspaceCallGraph { raw_callgraph: inward_callgraph.raw_callgraph.reverse() }; context.inward_callgraph = Some(inward_callgraph); context.outward_callgraph = Some(outward_callgraph); } // Using the source path, calculate the sloc - WorkspaceContextWrapper { - contexts, - root_path, - } + WorkspaceContextWrapper { contexts, root_path } } /// Supplement the arguments with values from aderyn.toml and foundry.toml @@ -222,13 +218,7 @@ fn make_context(args: &Args) -> WorkspaceContextWrapper { fn obtain_config_values( args: &Args, ) -> Result< - ( - PathBuf, - Option>, - Option>, - Option>, - Option>, - ), + (PathBuf, Option>, Option>, Option>, Option>), Box, > { let mut root_path = PathBuf::from(&args.root); @@ -241,19 +231,14 @@ fn obtain_config_values( let aderyn_path = root_path.join("aderyn.toml"); // Process aderyn.toml if it exists if aderyn_path.exists() { - ( - root_path, - local_src, - local_exclude, - local_remappings, - local_include, - ) = derive_from_aderyn_toml( - &root_path, - &local_src, - &local_exclude, - &local_remappings, - &local_include, - ); + (root_path, local_src, local_exclude, local_remappings, local_include) = + derive_from_aderyn_toml( + &root_path, + &local_src, + &local_exclude, + &local_remappings, + &local_include, + ); } let foundry_path = root_path.join("foundry.toml"); @@ -262,7 +247,8 @@ fn obtain_config_values( (local_src, local_exclude, local_remappings) = append_from_foundry_toml(&root_path, &local_src, &local_exclude, &local_remappings); } else { - // If foundry.toml wasn't found see if it makes sense to set src as `contracts/` for hardhat projects + // If foundry.toml wasn't found see if it makes sense to set src as `contracts/` for hardhat + // projects let hardhat_config_js_path = root_path.join("hardhat.config.js"); let hardhat_config_ts_path = root_path.join("hardhat.config.ts"); @@ -284,11 +270,5 @@ fn obtain_config_values( } } - Ok(( - root_path, - local_src, - local_exclude, - local_remappings, - local_include, - )) + Ok((root_path, local_src, local_exclude, local_remappings, local_include)) } diff --git a/aderyn_driver/src/foundry_compiler_helpers.rs b/aderyn_driver/src/foundry_compiler_helpers.rs index 0d1f2b617..62f7336ea 100644 --- a/aderyn_driver/src/foundry_compiler_helpers.rs +++ b/aderyn_driver/src/foundry_compiler_helpers.rs @@ -14,10 +14,8 @@ use crate::{passes_exclude, passes_scope, passes_src, read_remappings}; /// CompilerInput is a module that allows us to locate all solidity files in a root pub fn get_compiler_input(root: &Path) -> CompilerInput { let compiler_input = CompilerInput::new(root).unwrap(); - let solidity_files = compiler_input - .into_iter() - .filter(|c| c.language == *"Solidity") - .collect::>(); + let solidity_files = + compiler_input.into_iter().filter(|c| c.language == *"Solidity").collect::>(); if solidity_files.is_empty() { eprintln!("No solidity files found in {}!", root.to_string_lossy()); exit(1); @@ -34,34 +32,20 @@ pub fn get_remappings(root: &Path) -> (Vec, Vec) { remappings.dedup(); } - let foundry_compilers_remappings = remappings - .iter() - .filter_map(|x| Remapping::from_str(x).ok()) - .collect::>(); + let foundry_compilers_remappings = + remappings.iter().filter_map(|x| Remapping::from_str(x).ok()).collect::>(); (remappings, foundry_compilers_remappings) } /// Get FC remappings pub fn get_fc_remappings(remappings: &[String]) -> Vec { - remappings - .iter() - .filter_map(|x| Remapping::from_str(x).ok()) - .collect::>() + remappings.iter().filter_map(|x| Remapping::from_str(x).ok()).collect::>() } pub fn get_project(root: &Path, remappings: Vec) -> Project { - let paths = ProjectPathsConfig::builder() - .root(root) - .remappings(remappings) - .build() - .unwrap(); - Project::builder() - .no_artifacts() - .paths(paths) - .ephemeral() - .build() - .unwrap() + let paths = ProjectPathsConfig::builder().root(root).remappings(remappings).build().unwrap(); + Project::builder().no_artifacts().paths(paths).ephemeral().build().unwrap() } pub fn get_relevant_sources( diff --git a/aderyn_driver/src/lib.rs b/aderyn_driver/src/lib.rs index 8c4b94a06..7ffe29f69 100644 --- a/aderyn_driver/src/lib.rs +++ b/aderyn_driver/src/lib.rs @@ -4,13 +4,9 @@ pub(crate) mod foundry_compiler_helpers; pub mod lsp_report; pub(crate) mod process_auto; pub(crate) mod project_compiler_tests; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -pub use aderyn_core::ast as core_ast; -pub use aderyn_core::context; -pub use aderyn_core::detect as detection_modules; -pub use aderyn_core::detect::detector; +pub use aderyn_core::{ast as core_ast, context, detect as detection_modules, detect::detector}; use cyfrin_foundry_compilers::utils; pub use foundry_compiler_helpers::*; pub use process_auto::with_project_root_at; @@ -73,7 +69,8 @@ fn passes_exclude( // Return a list of remappings in the format ["a=b", "c=d", "e=f"] // where direct imports a,c,e map to b,d,f fn read_remappings(root_path: &Path) -> Option> { - // Look for a file called `remappings` in the project root. If not present, assume project doesn't require remappings + // Look for a file called `remappings` in the project root. If not present, assume project + // doesn't require remappings let remappings_file = root_path.join("remappings"); let remappings_txt_file = root_path.join("remappings.txt"); @@ -92,13 +89,7 @@ fn read_remappings(root_path: &Path) -> Option> { Some( remappings_content .lines() - .filter_map(|x| { - if !x.is_empty() { - Some(x.to_owned()) - } else { - None - } - }) + .filter_map(|x| if !x.is_empty() { Some(x.to_owned()) } else { None }) .collect(), ) } diff --git a/aderyn_driver/src/lsp_report.rs b/aderyn_driver/src/lsp_report.rs index 5d9dc01de..1c3bf9e97 100644 --- a/aderyn_driver/src/lsp_report.rs +++ b/aderyn_driver/src/lsp_report.rs @@ -25,10 +25,7 @@ impl LspReport { // Character position and range from the start of the line number let (pos_start, pos_range) = instance.src_char2.split_once(':')?; - let pos_start = pos_start - .parse::() - .unwrap_or_default() - .checked_sub(1)?; + let pos_start = pos_start.parse::().unwrap_or_default().checked_sub(1)?; let pos_range = pos_range.parse::().unwrap_or_default(); // Craft the diagnostic message @@ -53,14 +50,8 @@ impl LspReport { // Make the diagnostic that LSP can understand let diagnostic = Diagnostic { range: Range { - start: Position { - line: line_no as u32, - character: pos_start, - }, - end: Position { - line: line_no as u32, - character: pos_start + pos_range, - }, + start: Position { line: line_no as u32, character: pos_start }, + end: Position { line: line_no as u32, character: pos_start + pos_range }, }, severity: Some(severity), message, @@ -117,10 +108,6 @@ impl LspReport { file_diagnostics.push(diagnostic); } } - Self { - low_issues, - high_issues, - diagnostics, - } + Self { low_issues, high_issues, diagnostics } } } diff --git a/aderyn_driver/src/process_auto.rs b/aderyn_driver/src/process_auto.rs index 10b7d4e0c..c0fb45404 100644 --- a/aderyn_driver/src/process_auto.rs +++ b/aderyn_driver/src/process_auto.rs @@ -67,10 +67,7 @@ pub fn with_project_root_at( .args(remappings.clone()) .arg("--ast-compact-json") .args( - files - .iter() - .map(|x| x.strip_prefix(root.clone()).unwrap()) - .collect::>(), + files.iter().map(|x| x.strip_prefix(root.clone()).unwrap()).collect::>(), ) .args(solc.args.clone()) // --allowed-paths for older versions of sol .current_dir(root.clone()) @@ -88,7 +85,8 @@ pub fn with_project_root_at( eprintln!("cwd = {}", root.display()); // print_running_command(solc_bin, &remappings, &files, &root); eprintln!("Error running solc command ^^^"); - // For now, we do not panic because it will prevent us from analyzing other contexts which can compile successfully + // For now, we do not panic because it will prevent us from analyzing other + // contexts which can compile successfully } else { let context = create_workspace_context_from_stdout( stdout, &src, scope, exclude, root_path, @@ -114,9 +112,7 @@ fn create_workspace_context_from_stdout( exclude: &Option>, root_path: &Path, ) -> WorkspaceContext { - let absolute_root_path_str = &ensure_valid_root_path(root_path) - .to_string_lossy() - .to_string(); + let absolute_root_path_str = &ensure_valid_root_path(root_path).to_string_lossy().to_string(); let mut context = WorkspaceContext::default(); // dbg!(&stdout) @@ -202,22 +198,14 @@ fn is_demarcation_line( } if passes_scope( scope, - utils::canonicalize(root_path.join(filepath)) - .unwrap() - .as_path(), + utils::canonicalize(root_path.join(filepath)).unwrap().as_path(), absolute_root_path_str, ) && passes_exclude( exclude, - utils::canonicalize(root_path.join(filepath)) - .unwrap() - .as_path(), + utils::canonicalize(root_path.join(filepath)).unwrap().as_path(), absolute_root_path_str, - ) && passes_src( - src, - utils::canonicalize(root_path.join(filepath)) - .unwrap() - .as_path(), - ) { + ) && passes_src(src, utils::canonicalize(root_path.join(filepath)).unwrap().as_path()) + { return (true, Some(filepath.to_string_lossy().to_string())); } return (true, None); diff --git a/aderyn_driver/src/project_compiler_tests.rs b/aderyn_driver/src/project_compiler_tests.rs index c6082d677..1b2806cad 100644 --- a/aderyn_driver/src/project_compiler_tests.rs +++ b/aderyn_driver/src/project_compiler_tests.rs @@ -66,10 +66,7 @@ mod project_compiler_grouping_tests { .args(remappings.clone()) .arg("--ast-compact-json") .args( - files - .iter() - .map(|x| x.strip_prefix(root.clone()).unwrap()) - .collect::>(), + files.iter().map(|x| x.strip_prefix(root.clone()).unwrap()).collect::>(), ) .args(solc.args.clone()) // --allowed-paths for older versions of sol .current_dir(root.clone()) @@ -90,7 +87,8 @@ mod project_compiler_grouping_tests { &root, ); eprintln!("Error running solc command ^^^"); - // For now, we do not panic because it will prevent us from analyzing other contexts which can compile successfully + // For now, we do not panic because it will prevent us from analyzing other + // contexts which can compile successfully } else { // TODO: Create workspace context from stdout } @@ -116,10 +114,7 @@ mod project_compiler_grouping_tests { command.push_str(&format!("{} ", remap)); } for file in files { - command.push_str(&format!( - "{} ", - file.strip_prefix(root).unwrap().to_string_lossy() - )); + command.push_str(&format!("{} ", file.strip_prefix(root).unwrap().to_string_lossy())); } eprintln!("{}", command); } @@ -168,11 +163,7 @@ mod project_compiler_grouping_tests { let command_result = Command::new(solc.solc) .args(remappings.clone()) .arg("--ast-compact-json") - .args([ - "src/BasicNft.sol", - "src/inner-core-modules/ICM.sol", - "src/Initializer.sol", - ]) + .args(["src/BasicNft.sol", "src/inner-core-modules/ICM.sol", "src/Initializer.sol"]) .current_dir(root.clone()) .stdout(Stdio::piped()) .output() @@ -186,9 +177,7 @@ mod project_compiler_grouping_tests { #[test] fn test_no_files_found_in_scope_id_detected_by_context_src_filepaths() { let contexts = process_auto::with_project_root_at( - &PathBuf::from("../tests/contract-playground") - .canonicalize() - .unwrap(), + &PathBuf::from("../tests/contract-playground").canonicalize().unwrap(), &None, &None, &None, @@ -201,10 +190,8 @@ mod project_compiler_grouping_tests { #[test] fn test_compiler_input_returns_empty_vector_when_no_solidity_files_present() { let compiler_input = CompilerInput::new("../tests/no-sol-files").unwrap(); - let solidity_files = compiler_input - .into_iter() - .filter(|c| c.language == *"Solidity") - .collect::>(); + let solidity_files = + compiler_input.into_iter().filter(|c| c.language == *"Solidity").collect::>(); assert!(solidity_files.is_empty()); } } diff --git a/aderyn_py/src/lib.rs b/aderyn_py/src/lib.rs index 62f023bde..bcc4b09ec 100644 --- a/aderyn_py/src/lib.rs +++ b/aderyn_py/src/lib.rs @@ -4,8 +4,10 @@ use aderyn_driver::driver; use field_access::{FieldAccess, FieldMut}; fn main() { - use pyo3::prelude::*; - use pyo3::types::{PyBool, PyDict}; + use pyo3::{ + prelude::*, + types::{PyBool, PyDict}, + }; #[pyfunction] #[pyo3(signature = (root, output, **py_kwargs))] diff --git a/cli/reportgen.sh b/cli/reportgen.sh index ba414adfa..b89a2a6b4 100755 --- a/cli/reportgen.sh +++ b/cli/reportgen.sh @@ -44,3 +44,4 @@ cargo run -- ./tests/adhoc-sol-files -o ./reports/adhoc-sol-files-highs-only-re cargo run -- ./tests/contract-playground -o ./reports/report.sarif --skip-update-check & wait + diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 794c02e1b..8a953244d 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -144,9 +144,9 @@ ] }, { - "title": "Delegatecall made by the function without checks on any adress.", + "title": "Delegatecall made by the function without checks on any address.", "description": "Introduce checks on the address", - "detector_name": "delegate-call-unchecked-address", + "detector_name": "delegate-call-on-unchecked-address", "instances": [ { "contract_path": "inheritance/ExtendedInheritance.sol", @@ -176,8 +176,8 @@ }, "detectors_used": [ "delegate-call-in-loop", - "avoid-abi-encode-packed", - "block-timestamp-deadline", + "hash-collision-due-to-abi-encode-packed", + "block-timestamp-is-weak-deadline", "arbitrary-transfer-from", "unprotected-initializer", "unsafe-casting-detector", @@ -196,8 +196,8 @@ "state-variable-shadowing", "unchecked-send", "misused-boolean", - "send-ether-no-checks", - "delegate-call-unchecked-address", + "sends-ether-away-without-checking-address", + "delegate-call-on-unchecked-address", "tautological-compare", "rtlo", "unchecked-return", diff --git a/reports/adhoc-sol-files-report.md b/reports/adhoc-sol-files-report.md index 718a34129..54f803537 100644 --- a/reports/adhoc-sol-files-report.md +++ b/reports/adhoc-sol-files-report.md @@ -10,7 +10,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [High Issues](#high-issues) - [H-1: Using `delegatecall` in loop](#h-1-using-delegatecall-in-loop) - [H-2: Uninitialized State Variables](#h-2-uninitialized-state-variables) - - [H-3: Delegatecall made by the function without checks on any adress.](#h-3-delegatecall-made-by-the-function-without-checks-on-any-adress) + - [H-3: Delegatecall made by the function without checks on any address.](#h-3-delegatecall-made-by-the-function-without-checks-on-any-address) - [H-4: Unchecked Low level calls](#h-4-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) @@ -34,6 +34,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-19: Unused Imports](#l-19-unused-imports) - [L-20: State variable could be declared constant](#l-20-state-variable-could-be-declared-constant) - [L-21: State variable changes but no event is emitted.](#l-21-state-variable-changes-but-no-event-is-emitted) + - [L-22: State variable could be declared immutable](#l-22-state-variable-could-be-declared-immutable) # Summary @@ -78,7 +79,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 4 | -| Low | 21 | +| Low | 22 | # High Issues @@ -141,7 +142,7 @@ Solidity does initialize variables by default when you declare them, however it' -## H-3: Delegatecall made by the function without checks on any adress. +## H-3: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -936,3 +937,32 @@ State variable changes in this function but no event is emitted. +## L-22: State variable could be declared immutable + +State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor + +
3 Found Instances + + +- Found in InconsistentUints.sol [Line: 5](../tests/adhoc-sol-files/InconsistentUints.sol#L5) + + ```solidity + uint public uintVariable; // 1 + ``` + +- Found in InconsistentUints.sol [Line: 6](../tests/adhoc-sol-files/InconsistentUints.sol#L6) + + ```solidity + uint256 public uint256Variable; // 1 + ``` + +- Found in InternalFunctions.sol [Line: 5](../tests/adhoc-sol-files/InternalFunctions.sol#L5) + + ```solidity + address public owner; + ``` + +
+ + + diff --git a/reports/ccip-functions-report.md b/reports/ccip-functions-report.md index ebb9b7ca3..c6a976fd2 100644 --- a/reports/ccip-functions-report.md +++ b/reports/ccip-functions-report.md @@ -772,7 +772,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
52 Found Instances +
46 Found Instances - Found in src/v0.8/functions/dev/v1_X/FunctionsBilling.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol#L2) @@ -865,24 +865,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.19; ``` -- Found in src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - -- Found in src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - -- Found in src/v0.8/functions/dev/v1_X/libraries/FunctionsResponse.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsResponse.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - - Found in src/v0.8/functions/dev/v1_X/ocr/OCR2Abstract.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Abstract.sol#L2) ```solidity @@ -985,18 +967,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.19; ``` -- Found in src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - -- Found in src/v0.8/functions/v1_0_0/libraries/FunctionsResponse.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsResponse.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - - Found in src/v0.8/functions/v1_0_0/ocr/OCR2Abstract.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/ocr/OCR2Abstract.sol#L2) ```solidity @@ -1021,12 +991,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.19; ``` -- Found in src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol#L2) - - ```solidity - pragma solidity ^0.8.19; - ``` - - Found in src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol [Line: 2](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol#L2) ```solidity diff --git a/reports/hardhat-playground-report.md b/reports/hardhat-playground-report.md index 856df9649..044fb7da1 100644 --- a/reports/hardhat-playground-report.md +++ b/reports/hardhat-playground-report.md @@ -11,7 +11,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-1: Using `delegatecall` in loop](#h-1-using-delegatecall-in-loop) - [H-2: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`](#h-2-abiencodepacked-should-not-be-used-with-dynamic-types-when-passing-the-result-to-a-hash-function-such-as-keccak256) - [H-3: Uninitialized State Variables](#h-3-uninitialized-state-variables) - - [H-4: Delegatecall made by the function without checks on any adress.](#h-4-delegatecall-made-by-the-function-without-checks-on-any-adress) + - [H-4: Delegatecall made by the function without checks on any address.](#h-4-delegatecall-made-by-the-function-without-checks-on-any-address) - [H-5: Unchecked Low level calls](#h-5-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: `ecrecover` is susceptible to signature malleability](#l-1-ecrecover-is-susceptible-to-signature-malleability) @@ -26,6 +26,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-10: Unused Imports](#l-10-unused-imports) - [L-11: State variable could be declared constant](#l-11-state-variable-could-be-declared-constant) - [L-12: State variable changes but no event is emitted.](#l-12-state-variable-changes-but-no-event-is-emitted) + - [L-13: State variable could be declared immutable](#l-13-state-variable-could-be-declared-immutable) # Summary @@ -57,7 +58,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 5 | -| Low | 12 | +| Low | 13 | # High Issues @@ -138,7 +139,7 @@ Solidity does initialize variables by default when you declare them, however it' -## H-4: Delegatecall made by the function without checks on any adress. +## H-4: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -576,3 +577,26 @@ State variable changes in this function but no event is emitted. +## L-13: State variable could be declared immutable + +State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor + +
2 Found Instances + + +- Found in contracts/Lock.sol [Line: 8](../tests/hardhat-js-playground/contracts/Lock.sol#L8) + + ```solidity + uint public unlockTime; + ``` + +- Found in contracts/Lock.sol [Line: 9](../tests/hardhat-js-playground/contracts/Lock.sol#L9) + + ```solidity + address payable public owner; + ``` + +
+ + + diff --git a/reports/prb-math-report.md b/reports/prb-math-report.md index bd793bcd1..1dd24ad1c 100644 --- a/reports/prb-math-report.md +++ b/reports/prb-math-report.md @@ -145,7 +145,7 @@ The caret operator is usually mistakenly thought of as an exponentiation operato Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
30 Found Instances +
27 Found Instances - Found in src/Common.sol [Line: 2](../tests/prb-math/src/Common.sol#L2) @@ -178,24 +178,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity >=0.8.19; ``` -- Found in src/casting/Uint128.sol [Line: 2](../tests/prb-math/src/casting/Uint128.sol#L2) - - ```solidity - pragma solidity >=0.8.19; - ``` - -- Found in src/casting/Uint256.sol [Line: 2](../tests/prb-math/src/casting/Uint256.sol#L2) - - ```solidity - pragma solidity >=0.8.19; - ``` - -- Found in src/casting/Uint40.sol [Line: 2](../tests/prb-math/src/casting/Uint40.sol#L2) - - ```solidity - pragma solidity >=0.8.19; - ``` - - Found in src/sd1x18/Casting.sol [Line: 2](../tests/prb-math/src/sd1x18/Casting.sol#L2) ```solidity diff --git a/reports/report.json b/reports/report.json index 4dfcf10f3..938507cae 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { - "total_source_units": 110, - "total_sloc": 3904 + "total_source_units": 112, + "total_sloc": 3954 }, "files_details": { "files_details": [ @@ -67,7 +67,7 @@ }, { "file_path": "src/ContractLocksEther.sol", - "n_sloc": 121 + "n_sloc": 142 }, { "file_path": "src/ContractWithTodo.sol", @@ -209,6 +209,10 @@ "file_path": "src/OnceModifierExample.sol", "n_sloc": 8 }, + { + "file_path": "src/OnlyLibrary.sol", + "n_sloc": 2 + }, { "file_path": "src/OutOfOrderRetryable.sol", "n_sloc": 165 @@ -249,6 +253,10 @@ "file_path": "src/StateVariableCouldBeDeclaredConstant.sol", "n_sloc": 27 }, + { + "file_path": "src/StateVariableCouldBeDeclaredImmutable.sol", + "n_sloc": 22 + }, { "file_path": "src/StateVariables.sol", "n_sloc": 58 @@ -399,7 +407,7 @@ }, { "file_path": "src/inheritance/ExtendedInheritance.sol", - "n_sloc": 17 + "n_sloc": 18 }, { "file_path": "src/inheritance/IContractInheritance.sol", @@ -407,7 +415,7 @@ }, { "file_path": "src/inheritance/InheritanceBase.sol", - "n_sloc": 8 + "n_sloc": 12 }, { "file_path": "src/nested/1/Nested.sol", @@ -449,7 +457,7 @@ }, "issue_count": { "high": 42, - "low": 44 + "low": 45 }, "high_issues": { "issues": [ @@ -469,7 +477,7 @@ { "title": "`abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`", "description": "Use `abi.encode()` instead which will pad items to 32 bytes, which will [prevent hash collisions](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode) (e.g. `abi.encodePacked(0x123,0x456)` => `0x123456` => `abi.encodePacked(0x1,0x23456)`, but `abi.encode(0x123,0x456)` => `0x0...1230...456`). Unless there is a compelling reason, `abi.encode` should be preferred. If there is only one argument to `abi.encodePacked()` it can often be cast to `bytes()` or `bytes32()` [instead](https://ethereum.stackexchange.com/questions/30912/how-to-compare-strings-in-solidity#answer-82739).\nIf all arguments are strings and or bytes, `bytes.concat()` should be used instead.", - "detector_name": "avoid-abi-encode-packed", + "detector_name": "hash-collision-due-to-abi-encode-packed", "instances": [ { "contract_path": "src/KeccakContract.sol", @@ -494,7 +502,7 @@ { "title": "Using `block.timestamp` for swap deadline offers no protection", "description": "In the PoS model, proposers know well in advance if they will propose one or consecutive blocks ahead of time. In such a scenario, a malicious validator can hold back the transaction and execute it at a more favourable block number.Consider allowing function caller to specify swap deadline input parameter.", - "detector_name": "block-timestamp-deadline", + "detector_name": "block-timestamp-is-weak-deadline", "instances": [ { "contract_path": "src/Trump.sol", @@ -1779,7 +1787,7 @@ { "title": "Functions send eth away from contract but performs no checks on any address.", "description": "Consider introducing checks for `msg.sender` to ensure the recipient of the money is as intended.", - "detector_name": "send-ether-no-checks", + "detector_name": "sends-ether-away-without-checking-address", "instances": [ { "contract_path": "src/CallGraphTests.sol", @@ -1904,9 +1912,9 @@ ] }, { - "title": "Delegatecall made by the function without checks on any adress.", + "title": "Delegatecall made by the function without checks on any address.", "description": "Introduce checks on the address", - "detector_name": "delegate-call-unchecked-address", + "detector_name": "delegate-call-on-unchecked-address", "instances": [ { "contract_path": "src/DelegateCallWithoutAddressCheck.sol", @@ -2702,13 +2710,13 @@ { "title": "`ecrecover` is susceptible to signature malleability", "description": "The `ecrecover` function is susceptible to signature malleability. This means that the same message can be signed in multiple ways, allowing an attacker to change the message signature without invalidating it. This can lead to unexpected behavior in smart contracts, such as the loss of funds or the ability to bypass access control. Consider using OpenZeppelin's ECDSA library instead of the built-in function.", - "detector_name": "ecrecover", + "detector_name": "signature-malleability-due-to-raw-ecrecover", "instances": [ { "contract_path": "src/inheritance/ExtendedInheritance.sol", - "line_no": 21, - "src": "705:9", - "src_char": "705:9" + "line_no": 22, + "src": "737:9", + "src_char": "737:9" } ] }, @@ -2989,12 +2997,6 @@ "src": "32:23", "src_char": "32:23" }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 2, - "src": "32:23", - "src_char": "32:23" - }, { "contract_path": "src/TautologyOrContradiction.sol", "line_no": 2, @@ -3078,7 +3080,7 @@ { "title": "Missing checks for `address(0)` when assigning values to address state variables", "description": "Check for `address(0)` when assigning values to address state variables.", - "detector_name": "zero-address-check", + "detector_name": "no-zero-address-check", "instances": [ { "contract_path": "src/ArbitraryTransferFrom.sol", @@ -3128,6 +3130,12 @@ "src": "1327:23", "src_char": "1327:23" }, + { + "contract_path": "src/inheritance/ExtendedInheritance.sol", + "line_no": 18, + "src": "579:22", + "src_char": "579:22" + }, { "contract_path": "src/uniswap/UniswapV2Swapper.sol", "line_no": 11, @@ -3524,7 +3532,7 @@ { "title": "Define and use `constant` variables instead of using literals", "description": "If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract.", - "detector_name": "constants-instead-of-literals", + "detector_name": "literal-instead-of-constant", "instances": [ { "contract_path": "src/AssertStateChange.sol", @@ -4150,7 +4158,7 @@ { "title": "Empty `require()` / `revert()` statements", "description": "Use descriptive reason strings or custom errors for revert paths.", - "detector_name": "require-with-string", + "detector_name": "require-without-string", "instances": [ { "contract_path": "src/CallGraphTests.sol", @@ -4295,7 +4303,7 @@ { "title": "The `nonReentrant` `modifier` should occur before all other modifiers", "description": "This is a best-practice to protect against reentrancy in other modifiers.", - "detector_name": "non-reentrant-before-others", + "detector_name": "non-reentrant-is-not-before-others", "instances": [ { "contract_path": "src/AdminContract.sol", @@ -4431,6 +4439,12 @@ "src": "32:23", "src_char": "32:23" }, + { + "contract_path": "src/OnlyLibrary.sol", + "line_no": 2, + "src": "32:23", + "src_char": "32:23" + }, { "contract_path": "src/OutOfOrderRetryable.sol", "line_no": 2, @@ -5143,6 +5157,12 @@ "src": "1551:11", "src_char": "1551:11" }, + { + "contract_path": "src/StateVariableCouldBeDeclaredImmutable.sol", + "line_no": 26, + "src": "706:14", + "src_char": "706:14" + }, { "contract_path": "src/StorageParameters.sol", "line_no": 17, @@ -5715,7 +5735,7 @@ { "title": "Boolean equality is not required.", "description": "If `x` is a boolean, there is no need to do `if(x == true)` or `if(x == false)`. Just use `if(x)` and `if(!x)` respectively.", - "detector_name": "boolean-equality", + "detector_name": "redundant-boolean-equality", "instances": [ { "contract_path": "src/BooleanEquality.sol", @@ -6015,7 +6035,7 @@ { "title": "Loop condition contains `state_variable.length` that could be cached outside.", "description": "Cache the lengths of storage arrays if they are used and not modified in for loops.", - "detector_name": "cache-array-length", + "detector_name": "array-length-not-cached", "instances": [ { "contract_path": "src/CacheArrayLength.sol", @@ -6040,7 +6060,7 @@ { "title": "Incorrect use of `assert()`", "description": "Argument to `assert()` modifies the state. Use `require` for invariants modifying state.", - "detector_name": "assert-state-change", + "detector_name": "state-change-in-assert", "instances": [ { "contract_path": "src/AssertStateChange.sol", @@ -6827,6 +6847,12 @@ "src": "996:8", "src_char": "996:8" }, + { + "contract_path": "src/StateVariableCouldBeDeclaredImmutable.sol", + "line_no": 37, + "src": "1455:21", + "src_char": "1455:21" + }, { "contract_path": "src/StateVariables.sol", "line_no": 47, @@ -7103,6 +7129,12 @@ "src": "1286:4", "src_char": "1286:4" }, + { + "contract_path": "src/inheritance/ExtendedInheritance.sol", + "line_no": 14, + "src": "391:15", + "src_char": "391:15" + }, { "contract_path": "src/nested/1/Nested.sol", "line_no": 10, @@ -7123,6 +7155,199 @@ } ] }, + { + "title": "State variable could be declared immutable", + "description": "State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor", + "detector_name": "state-variable-could-be-declared-immutable", + "instances": [ + { + "contract_path": "src/ArbitraryTransferFrom.sol", + "line_no": 9, + "src": "217:7", + "src_char": "217:7" + }, + { + "contract_path": "src/InconsistentUints.sol", + "line_no": 5, + "src": "122:12", + "src_char": "122:12" + }, + { + "contract_path": "src/InconsistentUints.sol", + "line_no": 6, + "src": "160:15", + "src_char": "160:15" + }, + { + "contract_path": "src/InternalFunctions.sol", + "line_no": 5, + "src": "111:5", + "src_char": "111:5" + }, + { + "contract_path": "src/MultiplePlaceholders.sol", + "line_no": 5, + "src": "110:5", + "src_char": "110:5" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 39, + "src": "1052:5", + "src_char": "1052:5" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 40, + "src": "1078:10", + "src_char": "1078:10" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 41, + "src": "1109:17", + "src_char": "1109:17" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 42, + "src": "1147:8", + "src_char": "1147:8" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 43, + "src": "1176:12", + "src_char": "1176:12" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 44, + "src": "1209:3", + "src_char": "1209:3" + }, + { + "contract_path": "src/StateVariableCouldBeDeclaredConstant.sol", + "line_no": 11, + "src": "281:13", + "src_char": "281:13" + }, + { + "contract_path": "src/StateVariableCouldBeDeclaredConstant.sol", + "line_no": 29, + "src": "811:13", + "src_char": "811:13" + }, + { + "contract_path": "src/StateVariableCouldBeDeclaredImmutable.sol", + "line_no": 6, + "src": "162:24", + "src_char": "162:24" + }, + { + "contract_path": "src/StateVariableCouldBeDeclaredImmutable.sol", + "line_no": 9, + "src": "245:24", + "src_char": "245:24" + }, + { + "contract_path": "src/StorageConditionals.sol", + "line_no": 5, + "src": "108:18", + "src_char": "108:18" + }, + { + "contract_path": "src/StorageConditionals.sol", + "line_no": 6, + "src": "148:23", + "src_char": "148:23" + }, + { + "contract_path": "src/TestERC20.sol", + "line_no": 7, + "src": "144:4", + "src_char": "144:4" + }, + { + "contract_path": "src/TestERC20.sol", + "line_no": 8, + "src": "168:6", + "src_char": "168:6" + }, + { + "contract_path": "src/Trump.sol", + "line_no": 128, + "src": "3847:10", + "src_char": "3847:10" + }, + { + "contract_path": "src/Trump.sol", + "line_no": 129, + "src": "3887:11", + "src_char": "3887:11" + }, + { + "contract_path": "src/TxOriginUsedForAuth.sol", + "line_no": 5, + "src": "107:5", + "src_char": "107:5" + }, + { + "contract_path": "src/UninitializedStateVariable.sol", + "line_no": 9, + "src": "291:8", + "src_char": "291:8" + }, + { + "contract_path": "src/UninitializedStateVariable.sol", + "line_no": 37, + "src": "1079:5", + "src_char": "1079:5" + }, + { + "contract_path": "src/UnprotectedInitialize.sol", + "line_no": 7, + "src": "146:5", + "src_char": "146:5" + }, + { + "contract_path": "src/auditor_mode/ExternalCalls.sol", + "line_no": 9, + "src": "205:6", + "src_char": "205:6" + }, + { + "contract_path": "src/auditor_mode/ExternalCalls.sol", + "line_no": 10, + "src": "251:14", + "src_char": "251:14" + }, + { + "contract_path": "src/eth2/DepositContract.sol", + "line_no": 66, + "src": "4907:11", + "src_char": "3419:11" + }, + { + "contract_path": "src/reused_contract_name/ContractA.sol", + "line_no": 5, + "src": "99:1", + "src_char": "99:1" + }, + { + "contract_path": "src/reused_contract_name/ContractB.sol", + "line_no": 5, + "src": "102:1", + "src_char": "102:1" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 8, + "src": "312:8", + "src_char": "312:8" + } + ] + }, { "title": "Modifier has multiple placeholders.", "description": "Design the modifier to only contain 1 placeholder statement. If it's not possible, split the logic into multiple modifiers.", @@ -7142,18 +7367,18 @@ "delegate-call-in-loop", "centralization-risk", "solmate-safe-transfer-lib", - "avoid-abi-encode-packed", - "ecrecover", + "hash-collision-due-to-abi-encode-packed", + "signature-malleability-due-to-raw-ecrecover", "deprecated-oz-functions", "unsafe-erc20-functions", "unspecific-solidity-pragma", - "zero-address-check", + "no-zero-address-check", "useless-public-function", - "constants-instead-of-literals", + "literal-instead-of-constant", "unindexed-events", - "require-with-string", - "non-reentrant-before-others", - "block-timestamp-deadline", + "require-without-string", + "non-reentrant-is-not-before-others", + "block-timestamp-is-weak-deadline", "unsafe-oz-erc721-mint", "push-zero-opcode", "arbitrary-transfer-from", @@ -7183,8 +7408,8 @@ "state-variable-shadowing", "unchecked-send", "misused-boolean", - "send-ether-no-checks", - "delegate-call-unchecked-address", + "sends-ether-away-without-checking-address", + "delegate-call-on-unchecked-address", "tautological-compare", "rtlo", "unchecked-return", @@ -7199,7 +7424,7 @@ "delete-nested-mapping", "unused-state-variable", "constant-functions-assembly", - "boolean-equality", + "redundant-boolean-equality", "tx-origin-used-for-auth", "msg-value-in-loop", "contract-locks-ether", @@ -7211,8 +7436,8 @@ "out-of-order-retryable", "function-initializing-state", "dead-code", - "cache-array-length", - "assert-state-change", + "array-length-not-cached", + "state-change-in-assert", "costly-operations-inside-loops", "constant-function-changing-state", "builtin-symbol-shadow", @@ -7224,6 +7449,7 @@ "function-pointer-in-constructor", "state-variable-could-be-declared-constant", "state-variable-changes-without-events", + "state-variable-could-be-declared-immutable", "multiple-placeholders" ] } \ No newline at end of file diff --git a/reports/report.md b/reports/report.md index 8d2530c79..c30290fb9 100644 --- a/reports/report.md +++ b/reports/report.md @@ -30,7 +30,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-20: Unchecked `bool success` value for send call.](#h-20-unchecked-bool-success-value-for-send-call) - [H-21: Misused boolean with logical operators](#h-21-misused-boolean-with-logical-operators) - [H-22: Functions send eth away from contract but performs no checks on any address.](#h-22-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) - - [H-23: Delegatecall made by the function without checks on any adress.](#h-23-delegatecall-made-by-the-function-without-checks-on-any-adress) + - [H-23: Delegatecall made by the function without checks on any address.](#h-23-delegatecall-made-by-the-function-without-checks-on-any-address) - [H-24: Tautological comparison.](#h-24-tautological-comparison) - [H-25: RTLO character detected in file. \u{202e}](#h-25-rtlo-character-detected-in-file-u202e) - [H-26: Return value of the function call is not checked.](#h-26-return-value-of-the-function-call-is-not-checked) @@ -94,7 +94,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-41: Function pointers used in constructors.](#l-41-function-pointers-used-in-constructors) - [L-42: State variable could be declared constant](#l-42-state-variable-could-be-declared-constant) - [L-43: State variable changes but no event is emitted.](#l-43-state-variable-changes-but-no-event-is-emitted) - - [L-44: Modifier has multiple placeholders.](#l-44-modifier-has-multiple-placeholders) + - [L-44: State variable could be declared immutable](#l-44-state-variable-could-be-declared-immutable) + - [L-45: Modifier has multiple placeholders.](#l-45-modifier-has-multiple-placeholders) # Summary @@ -103,8 +104,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | -| .sol Files | 110 | -| Total nSLOC | 3904 | +| .sol Files | 112 | +| Total nSLOC | 3954 | ## Files Details @@ -126,7 +127,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/ConstFuncChangeState.sol | 15 | | src/ConstantFuncsAssembly.sol | 26 | | src/ConstantsLiterals.sol | 28 | -| src/ContractLocksEther.sol | 121 | +| src/ContractLocksEther.sol | 142 | | src/ContractWithTodo.sol | 7 | | src/CostlyOperationsInsideLoops.sol | 17 | | src/Counter.sol | 20 | @@ -162,6 +163,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/MultipleConstructorSchemes.sol | 10 | | src/MultiplePlaceholders.sol | 14 | | src/OnceModifierExample.sol | 8 | +| src/OnlyLibrary.sol | 2 | | src/OutOfOrderRetryable.sol | 165 | | src/PreDeclaredVarUsage.sol | 9 | | src/PublicVariableReadInExternalContext.sol | 32 | @@ -172,6 +174,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/SendEtherNoChecks.sol | 58 | | src/StateShadowing.sol | 17 | | src/StateVariableCouldBeDeclaredConstant.sol | 27 | +| src/StateVariableCouldBeDeclaredImmutable.sol | 22 | | src/StateVariables.sol | 58 | | src/StateVariablesChangesWithoutEvents.sol | 80 | | src/StateVariablesManipulation.sol | 250 | @@ -209,9 +212,9 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/cloc/EmptyContractFile.sol | 0 | | src/cloc/HeavilyCommentedContract.sol | 21 | | src/eth2/DepositContract.sol | 96 | -| src/inheritance/ExtendedInheritance.sol | 17 | +| src/inheritance/ExtendedInheritance.sol | 18 | | src/inheritance/IContractInheritance.sol | 4 | -| src/inheritance/InheritanceBase.sol | 8 | +| src/inheritance/InheritanceBase.sol | 12 | | src/nested/1/Nested.sol | 10 | | src/nested/2/Nested.sol | 7 | | src/nested_mappings/LaterVersion.sol | 10 | @@ -221,7 +224,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/reused_contract_name/ContractB.sol | 7 | | src/uniswap/UniswapV2Swapper.sol | 50 | | src/uniswap/UniswapV3Swapper.sol | 150 | -| **Total** | **3904** | +| **Total** | **3954** | ## Issue Summary @@ -229,7 +232,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 42 | -| Low | 44 | +| Low | 45 | # High Issues @@ -1774,7 +1777,7 @@ Consider introducing checks for `msg.sender` to ensure the recipient of the mone -## H-23: Delegatecall made by the function without checks on any adress. +## H-23: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -2668,7 +2671,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th
1 Found Instances -- Found in src/inheritance/ExtendedInheritance.sol [Line: 21](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L21) +- Found in src/inheritance/ExtendedInheritance.sol [Line: 22](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L22) ```solidity return ecrecover(theHash, v, r, s); @@ -2830,7 +2833,7 @@ ERC20 functions may not behave as expected. For example: return values are not a Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
36 Found Instances +
35 Found Instances - Found in src/BuiltinSymbolShadow.sol [Line: 2](../tests/contract-playground/src/BuiltinSymbolShadow.sol#L2) @@ -2965,12 +2968,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.4.0; ``` -- Found in src/StateVariablesManipulation.sol [Line: 2](../tests/contract-playground/src/StateVariablesManipulation.sol#L2) - - ```solidity - pragma solidity ^0.8.0; - ``` - - Found in src/TautologyOrContradiction.sol [Line: 2](../tests/contract-playground/src/TautologyOrContradiction.sol#L2) ```solidity @@ -3057,7 +3054,7 @@ Consider using a specific version of Solidity in your contracts instead of a wid Check for `address(0)` when assigning values to address state variables. -
9 Found Instances +
10 Found Instances - Found in src/ArbitraryTransferFrom.sol [Line: 12](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L12) @@ -3108,6 +3105,12 @@ Check for `address(0)` when assigning values to address state variables. token = IERC20(newAddr); ``` +- Found in src/inheritance/ExtendedInheritance.sol [Line: 18](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L18) + + ```solidity + s_baseAddress = target; + ``` + - Found in src/uniswap/UniswapV2Swapper.sol [Line: 11](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L11) ```solidity @@ -4310,7 +4313,7 @@ Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support Solc compiler version 0.8.20 switches the default target EVM version to Shanghai, which means that the generated bytecode will include PUSH0 opcodes. Be sure to select the appropriate EVM version in case you intend to deploy on a chain other than mainnet like L2 chains that may not support PUSH0, otherwise deployment of your contracts will fail. -
42 Found Instances +
43 Found Instances - Found in src/AdminContract.sol [Line: 2](../tests/contract-playground/src/AdminContract.sol#L2) @@ -4415,6 +4418,12 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai pragma solidity ^0.8.0; ``` +- Found in src/OnlyLibrary.sol [Line: 2](../tests/contract-playground/src/OnlyLibrary.sol#L2) + + ```solidity + pragma solidity ^0.8.0; + ``` + - Found in src/OutOfOrderRetryable.sol [Line: 2](../tests/contract-playground/src/OutOfOrderRetryable.sol#L2) ```solidity @@ -5056,7 +5065,7 @@ Use `e` notation, for example: `1e18`, instead of its full numeric value. Instead of separating the logic into a separate function, consider inlining the logic into the calling function. This can reduce the number of function calls and improve readability. -
18 Found Instances +
19 Found Instances - Found in src/CallGraphTests.sol [Line: 6](../tests/contract-playground/src/CallGraphTests.sol#L6) @@ -5143,6 +5152,12 @@ Instead of separating the logic into a separate function, consider inlining the function sendBalance(address x) internal { ``` +- Found in src/StateVariableCouldBeDeclaredImmutable.sol [Line: 26](../tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol#L26) + + ```solidity + function callSecretFunc() internal { + ``` + - Found in src/StorageParameters.sol [Line: 17](../tests/contract-playground/src/StorageParameters.sol#L17) ```solidity @@ -6631,7 +6646,7 @@ State variables that are not updated following deployment should be declared con State variable changes in this function but no event is emitted. -
98 Found Instances +
100 Found Instances - Found in src/AbstractContract.sol [Line: 6](../tests/contract-playground/src/AbstractContract.sol#L6) @@ -6928,6 +6943,12 @@ State variable changes in this function but no event is emitted. function changeIt() external { ``` +- Found in src/StateVariableCouldBeDeclaredImmutable.sol [Line: 37](../tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol#L37) + + ```solidity + function changeNotImmutableVar() external { + ``` + - Found in src/StateVariables.sol [Line: 47](../tests/contract-playground/src/StateVariables.sol#L47) ```solidity @@ -7204,6 +7225,12 @@ State variable changes in this function but no event is emitted. function bad3(address newAddr) external { ``` +- Found in src/inheritance/ExtendedInheritance.sol [Line: 14](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L14) + + ```solidity + function doSomethingElse(address target) external { + ``` + - Found in src/nested/1/Nested.sol [Line: 10](../tests/contract-playground/src/nested/1/Nested.sol#L10) ```solidity @@ -7226,7 +7253,204 @@ State variable changes in this function but no event is emitted. -## L-44: Modifier has multiple placeholders. +## L-44: State variable could be declared immutable + +State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor + +
31 Found Instances + + +- Found in src/ArbitraryTransferFrom.sol [Line: 9](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L9) + + ```solidity + IERC20 s_token; + ``` + +- Found in src/InconsistentUints.sol [Line: 5](../tests/contract-playground/src/InconsistentUints.sol#L5) + + ```solidity + uint public uintVariable; // 1 + ``` + +- Found in src/InconsistentUints.sol [Line: 6](../tests/contract-playground/src/InconsistentUints.sol#L6) + + ```solidity + uint256 public uint256Variable; // 1 + ``` + +- Found in src/InternalFunctions.sol [Line: 5](../tests/contract-playground/src/InternalFunctions.sol#L5) + + ```solidity + address public owner; + ``` + +- Found in src/MultiplePlaceholders.sol [Line: 5](../tests/contract-playground/src/MultiplePlaceholders.sol#L5) + + ```solidity + address internal owner; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 39](../tests/contract-playground/src/OutOfOrderRetryable.sol#L39) + + ```solidity + address public inbox; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 40](../tests/contract-playground/src/OutOfOrderRetryable.sol#L40) + + ```solidity + address public l2contract; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 41](../tests/contract-playground/src/OutOfOrderRetryable.sol#L41) + + ```solidity + uint256 public maxSubmissionCost; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 42](../tests/contract-playground/src/OutOfOrderRetryable.sol#L42) + + ```solidity + uint256 public gasLimit; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 43](../tests/contract-playground/src/OutOfOrderRetryable.sol#L43) + + ```solidity + uint256 public maxFeePerGas; + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 44](../tests/contract-playground/src/OutOfOrderRetryable.sol#L44) + + ```solidity + uint256 public gas; + ``` + +- Found in src/StateVariableCouldBeDeclaredConstant.sol [Line: 11](../tests/contract-playground/src/StateVariableCouldBeDeclaredConstant.sol#L11) + + ```solidity + uint256 public variableValue; // This one cannot be marked constant. (It can be marked immutable) + ``` + +- Found in src/StateVariableCouldBeDeclaredConstant.sol [Line: 29](../tests/contract-playground/src/StateVariableCouldBeDeclaredConstant.sol#L29) + + ```solidity + uint256 public variableValue; + ``` + +- Found in src/StateVariableCouldBeDeclaredImmutable.sol [Line: 6](../tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol#L6) + + ```solidity + uint256 potentiallyImmutableUint; + ``` + +- Found in src/StateVariableCouldBeDeclaredImmutable.sol [Line: 9](../tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol#L9) + + ```solidity + address potentiallyImmutableAddr; + ``` + +- Found in src/StorageConditionals.sol [Line: 5](../tests/contract-playground/src/StorageConditionals.sol#L5) + + ```solidity + uint256 private s_sameConditionals; + ``` + +- Found in src/StorageConditionals.sol [Line: 6](../tests/contract-playground/src/StorageConditionals.sol#L6) + + ```solidity + uint256 private s_differentConditionals; + ``` + +- Found in src/TestERC20.sol [Line: 7](../tests/contract-playground/src/TestERC20.sol#L7) + + ```solidity + string public name; + ``` + +- Found in src/TestERC20.sol [Line: 8](../tests/contract-playground/src/TestERC20.sol#L8) + + ```solidity + string public symbol; + ``` + +- Found in src/Trump.sol [Line: 128](../tests/contract-playground/src/Trump.sol#L128) + + ```solidity + address payable private _taxWallet; + ``` + +- Found in src/Trump.sol [Line: 129](../tests/contract-playground/src/Trump.sol#L129) + + ```solidity + address payable private _teamWallet; + ``` + +- Found in src/TxOriginUsedForAuth.sol [Line: 5](../tests/contract-playground/src/TxOriginUsedForAuth.sol#L5) + + ```solidity + address public owner; + ``` + +- Found in src/UninitializedStateVariable.sol [Line: 9](../tests/contract-playground/src/UninitializedStateVariable.sol#L9) + + ```solidity + uint256 public numPages; // GOOD (because it's initialized in constructor) + ``` + +- Found in src/UninitializedStateVariable.sol [Line: 37](../tests/contract-playground/src/UninitializedStateVariable.sol#L37) + + ```solidity + uint256 public myVar; // initialized in extension, hence not captured + ``` + +- Found in src/UnprotectedInitialize.sol [Line: 7](../tests/contract-playground/src/UnprotectedInitialize.sol#L7) + + ```solidity + address private owner; + ``` + +- Found in src/auditor_mode/ExternalCalls.sol [Line: 9](../tests/contract-playground/src/auditor_mode/ExternalCalls.sol#L9) + + ```solidity + address private target; + ``` + +- Found in src/auditor_mode/ExternalCalls.sol [Line: 10](../tests/contract-playground/src/auditor_mode/ExternalCalls.sol#L10) + + ```solidity + ExternalContractInterface private targetContract; + ``` + +- Found in src/eth2/DepositContract.sol [Line: 66](../tests/contract-playground/src/eth2/DepositContract.sol#L66) + + ```solidity + bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes; + ``` + +- Found in src/reused_contract_name/ContractA.sol [Line: 5](../tests/contract-playground/src/reused_contract_name/ContractA.sol#L5) + + ```solidity + uint public x; + ``` + +- Found in src/reused_contract_name/ContractB.sol [Line: 5](../tests/contract-playground/src/reused_contract_name/ContractB.sol#L5) + + ```solidity + address public x; + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 8](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L8) + + ```solidity + address private s_router; + ``` + +
+ + + +## L-45: Modifier has multiple placeholders. Design the modifier to only contain 1 placeholder statement. If it's not possible, split the logic into multiple modifiers. diff --git a/reports/report.sarif b/reports/report.sarif index bf5e74064..824261d60 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -64,7 +64,7 @@ "message": { "text": "Use `abi.encode()` instead which will pad items to 32 bytes, which will [prevent hash collisions](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode) (e.g. `abi.encodePacked(0x123,0x456)` => `0x123456` => `abi.encodePacked(0x1,0x23456)`, but `abi.encode(0x123,0x456)` => `0x0...1230...456`). Unless there is a compelling reason, `abi.encode` should be preferred. If there is only one argument to `abi.encodePacked()` it can often be cast to `bytes()` or `bytes32()` [instead](https://ethereum.stackexchange.com/questions/30912/how-to-compare-strings-in-solidity#answer-82739).\nIf all arguments are strings and or bytes, `bytes.concat()` should be used instead." }, - "ruleId": "avoid-abi-encode-packed" + "ruleId": "hash-collision-due-to-abi-encode-packed" }, { "level": "warning", @@ -260,7 +260,7 @@ "message": { "text": "In the PoS model, proposers know well in advance if they will propose one or consecutive blocks ahead of time. In such a scenario, a malicious validator can hold back the transaction and execute it at a more favourable block number.Consider allowing function caller to specify swap deadline input parameter." }, - "ruleId": "block-timestamp-deadline" + "ruleId": "block-timestamp-is-weak-deadline" }, { "level": "warning", @@ -2576,7 +2576,7 @@ "message": { "text": "Consider introducing checks for `msg.sender` to ensure the recipient of the money is as intended." }, - "ruleId": "send-ether-no-checks" + "ruleId": "sends-ether-away-without-checking-address" }, { "level": "warning", @@ -2640,7 +2640,7 @@ "message": { "text": "Introduce checks on the address" }, - "ruleId": "delegate-call-unchecked-address" + "ruleId": "delegate-call-on-unchecked-address" }, { "level": "warning", @@ -3958,7 +3958,7 @@ }, "region": { "byteLength": 9, - "byteOffset": 705 + "byteOffset": 737 } } } @@ -3966,7 +3966,7 @@ "message": { "text": "The `ecrecover` function is susceptible to signature malleability. This means that the same message can be signed in multiple ways, allowing an attacker to change the message signature without invalidating it. This can lead to unexpected behavior in smart contracts, such as the loss of funds or the ability to bypass access control. Consider using OpenZeppelin's ECDSA library instead of the built-in function." }, - "ruleId": "ecrecover" + "ruleId": "signature-malleability-due-to-raw-ecrecover" }, { "level": "note", @@ -4462,17 +4462,6 @@ } } }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 23, - "byteOffset": 32 - } - } - }, { "physicalLocation": { "artifactLocation": { @@ -4713,6 +4702,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/inheritance/ExtendedInheritance.sol" + }, + "region": { + "byteLength": 22, + "byteOffset": 579 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -4728,7 +4728,7 @@ "message": { "text": "Check for `address(0)` when assigning values to address state variables." }, - "ruleId": "zero-address-check" + "ruleId": "no-zero-address-check" }, { "level": "note", @@ -6297,7 +6297,7 @@ "message": { "text": "If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract." }, - "ruleId": "constants-instead-of-literals" + "ruleId": "literal-instead-of-constant" }, { "level": "note", @@ -6832,7 +6832,7 @@ "message": { "text": "Use descriptive reason strings or custom errors for revert paths." }, - "ruleId": "require-with-string" + "ruleId": "require-without-string" }, { "level": "note", @@ -6863,7 +6863,7 @@ "message": { "text": "This is a best-practice to protect against reentrancy in other modifiers." }, - "ruleId": "non-reentrant-before-others" + "ruleId": "non-reentrant-is-not-before-others" }, { "level": "note", @@ -7075,6 +7075,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OnlyLibrary.sol" + }, + "region": { + "byteLength": 23, + "byteOffset": 32 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -8365,6 +8376,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredImmutable.sol" + }, + "region": { + "byteLength": 14, + "byteOffset": 706 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -9427,7 +9449,7 @@ "message": { "text": "If `x` is a boolean, there is no need to do `if(x == true)` or `if(x == false)`. Just use `if(x)` and `if(!x)` respectively." }, - "ruleId": "boolean-equality" + "ruleId": "redundant-boolean-equality" }, { "level": "note", @@ -9943,7 +9965,7 @@ "message": { "text": "Cache the lengths of storage arrays if they are used and not modified in for loops." }, - "ruleId": "cache-array-length" + "ruleId": "array-length-not-cached" }, { "level": "note", @@ -9963,7 +9985,7 @@ "message": { "text": "Argument to `assert()` modifies the state. Use `require` for invariants modifying state." }, - "ruleId": "assert-state-change" + "ruleId": "state-change-in-assert" }, { "level": "note", @@ -11360,6 +11382,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredImmutable.sol" + }, + "region": { + "byteLength": 21, + "byteOffset": 1455 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -11866,6 +11899,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/inheritance/ExtendedInheritance.sol" + }, + "region": { + "byteLength": 15, + "byteOffset": 391 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -11905,6 +11949,356 @@ }, "ruleId": "state-variable-changes-without-events" }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/ArbitraryTransferFrom.sol" + }, + "region": { + "byteLength": 7, + "byteOffset": 217 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/InconsistentUints.sol" + }, + "region": { + "byteLength": 12, + "byteOffset": 122 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/InconsistentUints.sol" + }, + "region": { + "byteLength": 15, + "byteOffset": 160 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/InternalFunctions.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 111 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/MultiplePlaceholders.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 110 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 1052 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 10, + "byteOffset": 1078 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 17, + "byteOffset": 1109 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 8, + "byteOffset": 1147 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 12, + "byteOffset": 1176 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 3, + "byteOffset": 1209 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredConstant.sol" + }, + "region": { + "byteLength": 13, + "byteOffset": 281 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredConstant.sol" + }, + "region": { + "byteLength": 13, + "byteOffset": 811 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredImmutable.sol" + }, + "region": { + "byteLength": 24, + "byteOffset": 162 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredImmutable.sol" + }, + "region": { + "byteLength": 24, + "byteOffset": 245 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StorageConditionals.sol" + }, + "region": { + "byteLength": 18, + "byteOffset": 108 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StorageConditionals.sol" + }, + "region": { + "byteLength": 23, + "byteOffset": 148 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/TestERC20.sol" + }, + "region": { + "byteLength": 4, + "byteOffset": 144 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/TestERC20.sol" + }, + "region": { + "byteLength": 6, + "byteOffset": 168 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/Trump.sol" + }, + "region": { + "byteLength": 10, + "byteOffset": 3847 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/Trump.sol" + }, + "region": { + "byteLength": 11, + "byteOffset": 3887 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/TxOriginUsedForAuth.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 107 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UninitializedStateVariable.sol" + }, + "region": { + "byteLength": 8, + "byteOffset": 291 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UninitializedStateVariable.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 1079 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UnprotectedInitialize.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 146 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/auditor_mode/ExternalCalls.sol" + }, + "region": { + "byteLength": 6, + "byteOffset": 205 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/auditor_mode/ExternalCalls.sol" + }, + "region": { + "byteLength": 14, + "byteOffset": 251 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/eth2/DepositContract.sol" + }, + "region": { + "byteLength": 11, + "byteOffset": 4907 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/reused_contract_name/ContractA.sol" + }, + "region": { + "byteLength": 1, + "byteOffset": 99 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/reused_contract_name/ContractB.sol" + }, + "region": { + "byteLength": 1, + "byteOffset": 102 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 8, + "byteOffset": 312 + } + } + } + ], + "message": { + "text": "State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor" + }, + "ruleId": "state-variable-could-be-declared-immutable" + }, { "level": "note", "locations": [ diff --git a/reports/sablier-aderyn-toml-nested-root.md b/reports/sablier-aderyn-toml-nested-root.md index e5be85177..4b3a0bcfe 100644 --- a/reports/sablier-aderyn-toml-nested-root.md +++ b/reports/sablier-aderyn-toml-nested-root.md @@ -69,7 +69,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
20 Found Instances +
15 Found Instances - Found in src/SablierV2LockupDynamic.sol [Line: 2](../tests/2024-05-Sablier/v2-core/src/SablierV2LockupDynamic.sol#L2) @@ -162,36 +162,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity >=0.8.22; ``` -- Found in src/libraries/Errors.sol [Line: 2](../tests/2024-05-Sablier/v2-core/src/libraries/Errors.sol#L2) - - ```solidity - pragma solidity >=0.8.22; - ``` - -- Found in src/libraries/Helpers.sol [Line: 2](../tests/2024-05-Sablier/v2-core/src/libraries/Helpers.sol#L2) - - ```solidity - pragma solidity >=0.8.22; - ``` - -- Found in src/libraries/NFTSVG.sol [Line: 3](../tests/2024-05-Sablier/v2-core/src/libraries/NFTSVG.sol#L3) - - ```solidity - pragma solidity >=0.8.22; - ``` - -- Found in src/libraries/SVGElements.sol [Line: 3](../tests/2024-05-Sablier/v2-core/src/libraries/SVGElements.sol#L3) - - ```solidity - pragma solidity >=0.8.22; - ``` - -- Found in src/types/DataTypes.sol [Line: 2](../tests/2024-05-Sablier/v2-core/src/types/DataTypes.sol#L2) - - ```solidity - pragma solidity >=0.8.22; - ``` -
diff --git a/reports/templegold-report.md b/reports/templegold-report.md index 17ba60338..0a6db60f6 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -44,6 +44,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-24: Potentially missing inheritance for contract.](#l-24-potentially-missing-inheritance-for-contract) - [L-25: Unused Imports](#l-25-unused-imports) - [L-26: State variable changes but no event is emitted.](#l-26-state-variable-changes-but-no-event-is-emitted) + - [L-27: State variable could be declared immutable](#l-27-state-variable-could-be-declared-immutable) # Summary @@ -197,7 +198,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 9 | -| Low | 26 | +| Low | 27 | # High Issues @@ -340,7 +341,7 @@ When compiling contracts with certain development frameworks (for example: Truff Solidity does initialize variables by default when you declare them, however it's good practice to explicitly declare an initial value. For example, if you transfer money to an address we must make sure that the address has been initialized. -
8 Found Instances +
7 Found Instances - Found in contracts/amm/TempleUniswapV2Pair.sol [Line: 29](../tests/2024-07-templegold/protocol/contracts/amm/TempleUniswapV2Pair.sol#L29) @@ -367,12 +368,6 @@ Solidity does initialize variables by default when you declare them, however it' uint256 public lastUpdateTime; ``` -- Found in contracts/templegold/AuctionBase.sol [Line: 13](../tests/2024-07-templegold/protocol/contracts/templegold/AuctionBase.sol#L13) - - ```solidity - uint256 internal _currentEpochId; - ``` - - Found in contracts/templegold/TempleGoldStaking.sol [Line: 40](../tests/2024-07-templegold/protocol/contracts/templegold/TempleGoldStaking.sol#L40) ```solidity @@ -1168,7 +1163,7 @@ ERC20 functions may not behave as expected. For example: return values are not a Consider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;` -
80 Found Instances +
71 Found Instances - Found in contracts/admin/TempleTeamPayments.sol [Line: 2](../tests/2024-07-templegold/protocol/contracts/admin/TempleTeamPayments.sol#L2) @@ -1213,24 +1208,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.20; ``` -- Found in contracts/common/CommonEventsAndErrors.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/common/CommonEventsAndErrors.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - -- Found in contracts/common/SafeCast.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/common/SafeCast.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - -- Found in contracts/common/TempleMath.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/common/TempleMath.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - - Found in contracts/core/Exposure.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/core/Exposure.sol#L1) ```solidity @@ -1255,12 +1232,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.4; ``` -- Found in contracts/core/OpsManagerLib.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/core/OpsManagerLib.sol#L1) - - ```solidity - pragma solidity ^0.8.4; - ``` - - Found in contracts/core/OtcOffer.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/core/OtcOffer.sol#L1) ```solidity @@ -1489,12 +1460,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.20; ``` -- Found in contracts/templegold/EpochLib.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/templegold/EpochLib.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - - Found in contracts/templegold/SpiceAuction.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/templegold/SpiceAuction.sol#L1) ```solidity @@ -1531,18 +1496,6 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.20; ``` -- Found in contracts/util/ABDKMath64x64.sol [Line: 6](../tests/2024-07-templegold/protocol/contracts/util/ABDKMath64x64.sol#L6) - - ```solidity - pragma solidity ^0.8.4; - ``` - -- Found in contracts/util/ABDKMathQuad.sol [Line: 6](../tests/2024-07-templegold/protocol/contracts/util/ABDKMathQuad.sol#L6) - - ```solidity - pragma solidity ^0.8.0; - ``` - - Found in contracts/v2/TempleDebtToken.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L1) ```solidity @@ -1585,24 +1538,12 @@ Consider using a specific version of Solidity in your contracts instead of a wid pragma solidity ^0.8.20; ``` -- Found in contracts/v2/interestRate/CompoundedInterest.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/v2/interestRate/CompoundedInterest.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - - Found in contracts/v2/interestRate/LinearWithKinkInterestRateModel.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/v2/interestRate/LinearWithKinkInterestRateModel.sol#L1) ```solidity pragma solidity ^0.8.20; ``` -- Found in contracts/v2/safeGuards/SafeForked.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/v2/safeGuards/SafeForked.sol#L1) - - ```solidity - pragma solidity ^0.8.20; - ``` - - Found in contracts/v2/safeGuards/ThresholdSafeGuard.sol [Line: 1](../tests/2024-07-templegold/protocol/contracts/v2/safeGuards/ThresholdSafeGuard.sol#L1) ```solidity @@ -8368,7 +8309,7 @@ Consider keeping the naming convention consistent in a given contract. Explicit it is recommended that the definition be removed when custom error is unused -
25 Found Instances +
12 Found Instances - Found in contracts/amo/helpers/AMOCommon.sol [Line: 6](../tests/2024-07-templegold/protocol/contracts/amo/helpers/AMOCommon.sol#L6) @@ -8395,24 +8336,6 @@ it is recommended that the definition be removed when custom error is unused error Paused(); ``` -- Found in contracts/common/CommonEventsAndErrors.sol [Line: 7](../tests/2024-07-templegold/protocol/contracts/common/CommonEventsAndErrors.sol#L7) - - ```solidity - error InsufficientBalance(address token, uint256 required, uint256 balance); - ``` - -- Found in contracts/common/CommonEventsAndErrors.sol [Line: 11](../tests/2024-07-templegold/protocol/contracts/common/CommonEventsAndErrors.sol#L11) - - ```solidity - error InvalidAmount(address token, uint256 amount); - ``` - -- Found in contracts/common/CommonEventsAndErrors.sol [Line: 13](../tests/2024-07-templegold/protocol/contracts/common/CommonEventsAndErrors.sol#L13) - - ```solidity - error Unimplemented(); - ``` - - Found in contracts/core/VaultEarlyWithdraw.sol [Line: 31](../tests/2024-07-templegold/protocol/contracts/core/VaultEarlyWithdraw.sol#L31) ```solidity @@ -8443,78 +8366,18 @@ it is recommended that the definition be removed when custom error is unused error NotCandidate(uint256 discordId); ``` -- Found in contracts/interfaces/templegold/IDaiGoldAuction.sol [Line: 14](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/IDaiGoldAuction.sol#L14) - - ```solidity - error LowGoldDistributed(uint256 epochGoldAmount); - ``` - - Found in contracts/interfaces/templegold/ISpiceAuction.sol [Line: 14](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ISpiceAuction.sol#L14) ```solidity error NoConfig(); ``` -- Found in contracts/interfaces/templegold/ITempleGold.sol [Line: 47](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGold.sol#L47) - - ```solidity - error InvalidTotalShare(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGold.sol [Line: 48](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGold.sol#L48) - - ```solidity - error MissingParameter(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGold.sol [Line: 49](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGold.sol#L49) - - ```solidity - error NonTransferrable(address from, address to); - ``` - -- Found in contracts/interfaces/templegold/ITempleGold.sol [Line: 50](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGold.sol#L50) - - ```solidity - error WrongChain(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGold.sol [Line: 51](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGold.sol#L51) - - ```solidity - error CannotCompose(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGoldStaking.sol [Line: 20](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGoldStaking.sol#L20) - - ```solidity - error CannotDistribute(); - ``` - - Found in contracts/interfaces/templegold/ITempleGoldStaking.sol [Line: 21](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGoldStaking.sol#L21) ```solidity error CannotDelegate(); ``` -- Found in contracts/interfaces/templegold/ITempleGoldStaking.sol [Line: 22](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGoldStaking.sol#L22) - - ```solidity - error InvalidOperation(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGoldStaking.sol [Line: 23](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGoldStaking.sol#L23) - - ```solidity - error InvalidBlockNumber(); - ``` - -- Found in contracts/interfaces/templegold/ITempleGoldStaking.sol [Line: 24](../tests/2024-07-templegold/protocol/contracts/interfaces/templegold/ITempleGoldStaking.sol#L24) - - ```solidity - error NoStaker(); - ``` - - Found in contracts/interfaces/v2/safeGuards/IThresholdSafeGuard.sol [Line: 16](../tests/2024-07-templegold/protocol/contracts/interfaces/v2/safeGuards/IThresholdSafeGuard.sol#L16) ```solidity @@ -9080,3 +8943,104 @@ State variable changes in this function but no event is emitted. +## L-27: State variable could be declared immutable + +State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor + +
15 Found Instances + + +- Found in contracts/core/Exposure.sol [Line: 18](../tests/2024-07-templegold/protocol/contracts/core/Exposure.sol#L18) + + ```solidity + IERC20 public revalToken; + ``` + +- Found in contracts/core/OpsManager.sol [Line: 25](../tests/2024-07-templegold/protocol/contracts/core/OpsManager.sol#L25) + + ```solidity + Exposure public templeExposure; + ``` + +- Found in contracts/core/OpsManager.sol [Line: 26](../tests/2024-07-templegold/protocol/contracts/core/OpsManager.sol#L26) + + ```solidity + VaultedTemple public vaultedTemple; + ``` + +- Found in contracts/deprecated/InstantExitQueue.sol [Line: 14](../tests/2024-07-templegold/protocol/contracts/deprecated/InstantExitQueue.sol#L14) + + ```solidity + TempleStaking templeStaking; + ``` + +- Found in contracts/deprecated/InstantExitQueue.sol [Line: 15](../tests/2024-07-templegold/protocol/contracts/deprecated/InstantExitQueue.sol#L15) + + ```solidity + IERC20 templeToken; + ``` + +- Found in contracts/deprecated/LockedOGTemple.sol [Line: 21](../tests/2024-07-templegold/protocol/contracts/deprecated/LockedOGTemple.sol#L21) + + ```solidity + OGTemple public OG_TEMPLE; // The token being staked, for which TEMPLE rewards are generated + ``` + +- Found in contracts/deprecated/TempleStaking.sol [Line: 29](../tests/2024-07-templegold/protocol/contracts/deprecated/TempleStaking.sol#L29) + + ```solidity + uint256 public epochSizeSeconds; + ``` + +- Found in contracts/deprecated/TempleStaking.sol [Line: 32](../tests/2024-07-templegold/protocol/contracts/deprecated/TempleStaking.sol#L32) + + ```solidity + uint256 public startTimestamp; + ``` + +- Found in contracts/fakes/NoopLiquidator.sol [Line: 13](../tests/2024-07-templegold/protocol/contracts/fakes/NoopLiquidator.sol#L13) + + ```solidity + TempleERC20Token templeToken; + ``` + +- Found in contracts/fakes/NoopVaultedTempleLiquidator.sol [Line: 14](../tests/2024-07-templegold/protocol/contracts/fakes/NoopVaultedTempleLiquidator.sol#L14) + + ```solidity + TempleERC20Token templeToken; + ``` + +- Found in contracts/fakes/NoopVaultedTempleLiquidator.sol [Line: 15](../tests/2024-07-templegold/protocol/contracts/fakes/NoopVaultedTempleLiquidator.sol#L15) + + ```solidity + VaultedTemple vaultedTemple; + ``` + +- Found in contracts/fakes/templegold/TempleGoldStakingMock.sol [Line: 31](../tests/2024-07-templegold/protocol/contracts/fakes/templegold/TempleGoldStakingMock.sol#L31) + + ```solidity + ITempleGoldStaking public previousStaking; + ``` + +- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 24](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L24) + + ```solidity + uint256 public lastUpdatedAt; + ``` + +- Found in contracts/governance/ElderElection.sol [Line: 41](../tests/2024-07-templegold/protocol/contracts/governance/ElderElection.sol#L41) + + ```solidity + Templar public templars; + ``` + +- Found in contracts/governance/TemplarMetadata.sol [Line: 18](../tests/2024-07-templegold/protocol/contracts/governance/TemplarMetadata.sol#L18) + + ```solidity + Templar public templars; + ``` + +
+ + + diff --git a/reports/uniswap_profile.md b/reports/uniswap_profile.md index 244abd6c1..e0eff4251 100644 --- a/reports/uniswap_profile.md +++ b/reports/uniswap_profile.md @@ -12,6 +12,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Low Issues](#low-issues) - [L-1: Missing checks for `address(0)` when assigning values to address state variables](#l-1-missing-checks-for-address0-when-assigning-values-to-address-state-variables) - [L-2: PUSH0 is not supported by all chains](#l-2-push0-is-not-supported-by-all-chains) + - [L-3: State variable could be declared immutable](#l-3-state-variable-could-be-declared-immutable) # Summary @@ -38,7 +39,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 1 | -| Low | 2 | +| Low | 3 | # High Issues @@ -192,3 +193,20 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai +## L-3: State variable could be declared immutable + +State variables that are should be declared immutable to save gas. Add the `immutable` attribute to state variables that are only changed in the constructor + +
1 Found Instances + + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 8](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L8) + + ```solidity + address private s_router; + ``` + +
+ + + diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..71aecd458 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,11 @@ +reorder_imports = true +imports_granularity = "Crate" +use_small_heuristics = "Max" +comment_width = 100 +wrap_comments = true +binop_separator = "Front" +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true +format_code_in_doc_comments = true +doc_comment_code_block_width = 100 diff --git a/tests/contract-playground/src/ContractLocksEther.sol b/tests/contract-playground/src/ContractLocksEther.sol index 4e607cbdc..6442a270c 100644 --- a/tests/contract-playground/src/ContractLocksEther.sol +++ b/tests/contract-playground/src/ContractLocksEther.sol @@ -208,3 +208,39 @@ contract CanWithdrawChild is CanWithdrawParent { emit Deposited(msg.sender, msg.value); } } + +import "../lib/openzeppelin-contracts/contracts/utils/Address.sol"; + +// GOOD +contract CanWithdrawOZ { + using Address for address payable; + + // Event to log deposits + event Deposited(address indexed sender, uint256 indexed amount); + + // Event to log transfers + event Transferred(address indexed to, uint256 indexed amount); + + // Public payable function to receive Ether + receive() external payable { + emit Deposited(msg.sender, msg.value); + } + + // Public payable fallback function to handle any data sent with Ether + fallback() external payable { + emit Deposited(msg.sender, msg.value); + } + + // Internal function to send Ether to a given address + function _sendEther(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Insufficient balance"); + require(recipient != address(0), "Invalid recipient"); + recipient.sendValue(amount); + emit Transferred(recipient, amount); + } + + // This function allows for the withdrawal of eth. Hence this contract is a GOOD contract. + function takeEthBack(uint256 amount) external { + _sendEther(payable(msg.sender), amount); + } +} diff --git a/tests/contract-playground/src/OnlyLibrary.sol b/tests/contract-playground/src/OnlyLibrary.sol new file mode 100644 index 000000000..5b7257a60 --- /dev/null +++ b/tests/contract-playground/src/OnlyLibrary.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library MathLib {} diff --git a/tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol b/tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol new file mode 100644 index 000000000..9263422b8 --- /dev/null +++ b/tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +contract StateVariableCouldBeDeclaredImmutable { + // BAD (this could be marked immutable) + uint256 potentiallyImmutableUint; + + // BAD (this could be marked immutable) + address potentiallyImmutableAddr; + + // GOOD + uint256 immutable goodValue; + + uint256 notImmutable; + uint256 seeminglyImmutable; + + constructor() { + potentiallyImmutableUint = 10; // aderyn-ignore + potentiallyImmutableAddr = address(10); // aderyn-ignore + potentiallyImmutableUint *= 20; // aderyn-ignore + notImmutable = 10; // aderyn-ignore + goodValue = 10; // aderyn-ignore + callSecretFunc(); + } + + function callSecretFunc() internal { + // NOTE: Although this function is only called by the constructor, it may appear + // as if `seeminglyImmutable` can be declared immutable because no other function + // changes it's state. However solidity puts a constraint which is that for a variable + // to be immutable, it should only be changed in the constructor or inlined in + // where it's defined. So the flow would be that the user is first notified that this + // internal function can be inlined into the constructor (coz that's the only place it's called) + // Then, this immutable detector picks it up and flags it as potentially immutable. + seeminglyImmutable = 3; // aderyn-ignore + } + + function changeNotImmutableVar() external { + notImmutable *= 3; // aderyn-ignore + } +} diff --git a/tests/contract-playground/src/inheritance/ExtendedInheritance.sol b/tests/contract-playground/src/inheritance/ExtendedInheritance.sol index bbdf06649..194285364 100644 --- a/tests/contract-playground/src/inheritance/ExtendedInheritance.sol +++ b/tests/contract-playground/src/inheritance/ExtendedInheritance.sol @@ -15,6 +15,7 @@ contract ExtendedInheritance is InheritanceBase { for (uint256 i = 0; i < 3; i++) { target.delegatecall(abi.encodeWithSignature("doSomething(uint256)", i)); } + s_baseAddress = target; } function recoverThatThang(uint8 v, bytes32 r, bytes32 s, bytes32 theHash) external pure returns (address) { diff --git a/tests/contract-playground/src/inheritance/InheritanceBase.sol b/tests/contract-playground/src/inheritance/InheritanceBase.sol index 771857d67..05492b20c 100644 --- a/tests/contract-playground/src/inheritance/InheritanceBase.sol +++ b/tests/contract-playground/src/inheritance/InheritanceBase.sol @@ -6,6 +6,12 @@ import "./IContractInheritance.sol"; contract InheritanceBase is IContractInheritance { event Do(uint256 something); + address public s_baseAddress; + + constructor() { + s_baseAddress = address(123); + } + function doSomething(uint256 something) external virtual { emit Do(something); }