Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.1.9 Staging #634

Merged
merged 15 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
482 changes: 267 additions & 215 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions aderyn/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aderyn"
version = "0.1.8"
version = "0.1.9"
edition = "2021"
authors = ["Cyfrin <aderyn@cyfrin.io>"]
description = "Rust based Solidity AST analyzer"
Expand All @@ -10,7 +10,7 @@ default-run = "aderyn"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
aderyn_driver = { path = "../aderyn_driver", version = "0.1.8" }
aderyn_driver = { path = "../aderyn_driver", version = "0.1.9" }
clap = { version = "4.4.6", features = ["derive"] }
reqwest = { version = "0.12.2", default-features = false, features = ["blocking", "json", "rustls-tls"] }
semver = "1.0.22"
Expand Down
5 changes: 5 additions & 0 deletions aderyn/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@

> ⚠️ **Installing via crates is no longer fully supported. `cyfrinup` is the preferred installation method.**.
>
> For the best experience, please remove the legacy crate installation by running `cargo uninstall aderyn`, and use `cyfrinup` instead.
>
> Full install instructions are [here](#installation).

<p align="center">
<br />
Expand Down
9 changes: 3 additions & 6 deletions aderyn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,12 @@ pub fn aderyn_is_currently_running_newest_version() -> Result<bool, reqwest::Err
.build()?;

let latest_version_checker = client
.get("https://crates.io/api/v1/crates?q=aderyn&per_page=1")
.get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest")
.send()?;

let data = latest_version_checker.json::<Value>()?;

let newest_version = data["crates"][0]["newest_version"].to_string();
let newest_version = &newest_version[1..newest_version.len() - 1];

let newest = Version::parse(newest_version).unwrap();
let newest =
Version::parse(data["tag_name"].as_str().unwrap().replace("v", "").as_str()).unwrap();
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();

Ok(current >= newest)
Expand Down
4 changes: 1 addition & 3 deletions aderyn/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,7 @@ fn main() {
if let Ok(yes) = aderyn_is_currently_running_newest_version() {
if !yes {
println!();
println!(
"NEW VERSION OF ADERYN AVAILABLE! Please run `cargo install aderyn` to fully upgrade the current version"
);
println!("NEW VERSION OF ADERYN AVAILABLE! Please run `cyfrinup` to upgrade.");
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions aderyn_core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aderyn_core"
version = "0.1.8"
version = "0.1.9"
edition = "2021"
authors = ["Cyfrin <aderyn@cyfrin.io>"]
description = "Rust based Solidity AST analyzer backend"
Expand All @@ -12,7 +12,7 @@ license = "MIT"
crossbeam-channel = "0.5.9"
eyre = "0.6.12"
ignore = "0.4.21"
phf = {version = "0.11.2", features = ["macros"]}
phf = { version = "0.11.2", features = ["macros"] }
prettytable = "0.10.0"
rayon = "1.8.0"
semver = "1.0.20"
Expand All @@ -22,9 +22,14 @@ serde-sarif = "0.4.2"
serde_repr = "0.1.12"
strum = { version = "0.26", features = ["derive"] }
toml = "0.8.2"
cyfrin-foundry-compilers = { version = "0.3.20-aderyn", features = ["svm-solc"] }
cyfrin-foundry-compilers = { version = "0.3.20-aderyn", features = [
"svm-solc",
] }
num-bigint = "0.4"
num-traits = "0.2"
lazy-regex = "3.2.0"
derive_more = "0.99.18"

[dev-dependencies]
serial_test = "3.0.0"
once_cell = "1.19.0"
once_cell = "1.19.0"
46 changes: 46 additions & 0 deletions aderyn_core/src/ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,49 @@ impl From<Expression> for ASTNode {
}
}
}

impl From<Statement> for ASTNode {
fn from(value: Statement) -> Self {
match value {
Statement::Block(node) => node.into(),
Statement::Break(node) => node.into(),
Statement::Continue(node) => node.into(),
Statement::DoWhileStatement(node) => node.into(),
Statement::PlaceholderStatement(node) => node.into(),
Statement::VariableDeclarationStatement(node) => node.into(),
Statement::IfStatement(node) => node.into(),
Statement::ForStatement(node) => node.into(),
Statement::WhileStatement(node) => node.into(),
Statement::EmitStatement(node) => node.into(),
Statement::TryStatement(node) => node.into(),
Statement::UncheckedBlock(node) => node.into(),
Statement::Return(node) => node.into(),
Statement::RevertStatement(node) => node.into(),
Statement::ExpressionStatement(node) => node.into(),
Statement::InlineAssembly(node) => node.into(),
}
}
}

impl From<&Statement> for ASTNode {
fn from(value: &Statement) -> Self {
match value {
Statement::Block(node) => node.into(),
Statement::Break(node) => node.into(),
Statement::Continue(node) => node.into(),
Statement::DoWhileStatement(node) => node.into(),
Statement::PlaceholderStatement(node) => node.into(),
Statement::VariableDeclarationStatement(node) => node.into(),
Statement::IfStatement(node) => node.into(),
Statement::ForStatement(node) => node.into(),
Statement::WhileStatement(node) => node.into(),
Statement::EmitStatement(node) => node.into(),
Statement::TryStatement(node) => node.into(),
Statement::UncheckedBlock(node) => node.into(),
Statement::Return(node) => node.into(),
Statement::RevertStatement(node) => node.into(),
Statement::ExpressionStatement(node) => node.into(),
Statement::InlineAssembly(node) => node.into(),
}
}
}
7 changes: 7 additions & 0 deletions aderyn_core/src/ast/impls/node/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,16 @@ impl Node for ExpressionStatement {
fn accept(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> {
if visitor.visit_expression_statement(self)? {
self.expression.accept(visitor)?;
self.accept_metadata(visitor)?;
}
visitor.end_visit_expression_statement(self)
}
fn accept_metadata(&self, visitor: &mut impl ASTConstVisitor) -> Result<()> {
if let Some(child_id) = self.expression.get_node_id() {
visitor.visit_immediate_children(self.id, vec![child_id])?;
}
Ok(())
}
}

impl Node for VariableDeclarationStatement {
Expand Down
22 changes: 15 additions & 7 deletions aderyn_core/src/context/investigator/callgraph_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod callgraph_tests {
},
};

use serial_test::serial;
use StandardInvestigationStyle::*;

fn get_function_by_name(context: &WorkspaceContext, name: &str) -> ASTNode {
Expand All @@ -35,17 +36,19 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_callgraph_is_not_none() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);
assert!(context.forward_callgraph.is_some());
assert!(context.reverse_callgraph.is_some());
}

#[test]
#[serial]
fn test_tower1_modifier_has_no_downstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand All @@ -62,8 +65,9 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_tower1_modifier_has_upstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand All @@ -80,8 +84,9 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_tower2_modifier_has_both_upstream_and_downstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand All @@ -99,8 +104,9 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_tower3_modifier_has_both_upstream_and_downstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand All @@ -120,8 +126,9 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_tower3_functions_has_upstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand All @@ -137,8 +144,9 @@ mod callgraph_tests {
}

#[test]
#[serial]
fn test_tower4_functions_has_upstream_and_downstream() {
let context = crate::detect::test_utils::load_solidity_source_unit_with_callgraphs(
let context = crate::detect::test_utils::load_solidity_source_unit(
"../tests/contract-playground/src/CallGraphTests.sol",
);

Expand Down
49 changes: 49 additions & 0 deletions aderyn_core/src/detect/detector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,27 @@ pub fn get_all_issue_detectors() -> Vec<Box<dyn IssueDetector>> {
Box::<IncorrectUseOfCaretOperatorDetector>::default(),
Box::<YulReturnDetector>::default(),
Box::<StateVariableShadowingDetector>::default(),
Box::<UncheckedSendDetector>::default(),
Box::<MisusedBooleanDetector>::default(),
Box::<SendEtherNoChecksDetector>::default(),
Box::<DelegateCallOnUncheckedAddressDetector>::default(),
Box::<TautologicalCompareDetector>::default(),
Box::<RTLODetector>::default(),
Box::<UncheckedReturnDetector>::default(),
Box::<DangerousUnaryOperatorDetector>::default(),
Box::<TautologyOrContraditionDetector>::default(),
Box::<DangerousStrictEqualityOnBalanceDetector>::default(),
Box::<StorageSignedIntegerArrayDetector>::default(),
Box::<RedundantStatementsDetector>::default(),
Box::<PublicVariableReadInExternalContextDetector>::default(),
Box::<WeakRandomnessDetector>::default(),
Box::<PreDeclaredLocalVariableUsageDetector>::default(),
Box::<DeletionNestedMappingDetector>::default(),
Box::<ConstantFunctionContainsAssemblyDetector>::default(),
Box::<BooleanEqualityDetector>::default(),
Box::<TxOriginUsedForAuthDetector>::default(),
Box::<MsgValueUsedInLoopDetector>::default(),
Box::<ContractLocksEtherDetector>::default(),
]
}

Expand Down Expand Up @@ -121,6 +132,7 @@ pub(crate) enum IssueDetectorNamePool {
IncorrectCaretOperator,
YulReturn,
StateVariableShadowing,
UncheckedSend,
MisusedBoolean,
SendEtherNoChecks,
DelegateCallUncheckedAddress,
Expand All @@ -129,9 +141,19 @@ pub(crate) enum IssueDetectorNamePool {
RTLO,
UncheckedReturn,
DangerousUnaryOperator,
TautologyOrContradiction,
DangerousStrictEquailtyOnContractBalance,
SignedStorageArray,
RedundantStatements,
PublicVariableReadInExternalContext,
WeakRandomness,
PreDeclaredLocalVariableUsage,
DeleteNestedMapping,
ConstantFunctionsAssembly,
BooleanEquality,
TxOriginUsedForAuth,
MsgValueInLoop,
ContractLocksEther,
// NOTE: `Undecided` will be the default name (for new bots).
// If it's accepted, a new variant will be added to this enum before normalizing it in aderyn
Undecided,
Expand Down Expand Up @@ -252,6 +274,7 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option<Box<dyn Iss
IssueDetectorNamePool::StateVariableShadowing => {
Some(Box::<StateVariableShadowingDetector>::default())
}
IssueDetectorNamePool::UncheckedSend => Some(Box::<UncheckedSendDetector>::default()),
IssueDetectorNamePool::MisusedBoolean => Some(Box::<MisusedBooleanDetector>::default()),
IssueDetectorNamePool::SendEtherNoChecks => {
Some(Box::<SendEtherNoChecksDetector>::default())
Expand All @@ -267,13 +290,39 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option<Box<dyn Iss
IssueDetectorNamePool::DangerousUnaryOperator => {
Some(Box::<DangerousUnaryOperatorDetector>::default())
}
IssueDetectorNamePool::TautologyOrContradiction => {
Some(Box::<TautologyOrContraditionDetector>::default())
}
IssueDetectorNamePool::DangerousStrictEquailtyOnContractBalance => {
Some(Box::<DangerousStrictEqualityOnBalanceDetector>::default())
}
IssueDetectorNamePool::SignedStorageArray => {
Some(Box::<StorageSignedIntegerArrayDetector>::default())
}
IssueDetectorNamePool::RedundantStatements => {
Some(Box::<RedundantStatementsDetector>::default())
}
IssueDetectorNamePool::PublicVariableReadInExternalContext => {
Some(Box::<PublicVariableReadInExternalContextDetector>::default())
}
IssueDetectorNamePool::WeakRandomness => Some(Box::<WeakRandomnessDetector>::default()),
IssueDetectorNamePool::PreDeclaredLocalVariableUsage => {
Some(Box::<PreDeclaredLocalVariableUsageDetector>::default())
}
IssueDetectorNamePool::DeleteNestedMapping => {
Some(Box::<DeletionNestedMappingDetector>::default())
}
IssueDetectorNamePool::ConstantFunctionsAssembly => {
Some(Box::<ConstantFunctionContainsAssemblyDetector>::default())
}
IssueDetectorNamePool::BooleanEquality => Some(Box::<BooleanEqualityDetector>::default()),
IssueDetectorNamePool::TxOriginUsedForAuth => {
Some(Box::<TxOriginUsedForAuthDetector>::default())
}
IssueDetectorNamePool::MsgValueInLoop => Some(Box::<MsgValueUsedInLoopDetector>::default()),
IssueDetectorNamePool::ContractLocksEther => {
Some(Box::<ContractLocksEtherDetector>::default())
}
IssueDetectorNamePool::Undecided => None,
}
}
Expand Down
9 changes: 6 additions & 3 deletions aderyn_core/src/detect/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ pub fn has_calls_that_sends_native_eth(ast_node: &ASTNode) -> bool {
if let Expression::Literal(literal) = c {
return literal.value.is_some();
}
if let Expression::Identifier(_) = c {
return true;
}
false
});
if !call_carries_value {
Expand Down Expand Up @@ -189,9 +192,9 @@ pub fn has_binary_checks_on_some_address(ast_node: &ASTNode) -> bool {
binary_operations.into_iter().any(|op| {
[op.left_expression, op.right_expression].iter().any(|op| {
op.as_ref().type_descriptions().is_some_and(|desc| {
desc.type_string
.as_ref()
.is_some_and(|type_string| type_string == "address")
desc.type_string.as_ref().is_some_and(|type_string| {
type_string == "address" || type_string == "address payable"
})
})
})
})
Expand Down
Loading
Loading