From 9207635c069efed3397f52991dfec835a085ce71 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 3 Oct 2024 15:59:00 +0530 Subject: [PATCH 01/25] Allow for `sendValue` on address to be recognized as a withdraw function (#748) --- aderyn_core/src/detect/helpers.rs | 6 +++- reports/report.json | 4 +-- reports/report.md | 6 ++-- .../src/ContractLocksEther.sol | 36 +++++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/aderyn_core/src/detect/helpers.rs b/aderyn_core/src/detect/helpers.rs index 8daa10b56..de8a09498 100644 --- a/aderyn_core/src/detect/helpers.rs +++ b/aderyn_core/src/detect/helpers.rs @@ -129,12 +129,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 diff --git a/reports/report.json b/reports/report.json index 4dfcf10f3..ea3575821 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { "total_source_units": 110, - "total_sloc": 3904 + "total_sloc": 3925 }, "files_details": { "files_details": [ @@ -67,7 +67,7 @@ }, { "file_path": "src/ContractLocksEther.sol", - "n_sloc": 121 + "n_sloc": 142 }, { "file_path": "src/ContractWithTodo.sol", diff --git a/reports/report.md b/reports/report.md index 8d2530c79..a2efee84f 100644 --- a/reports/report.md +++ b/reports/report.md @@ -104,7 +104,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 110 | -| Total nSLOC | 3904 | +| Total nSLOC | 3925 | ## Files Details @@ -126,7 +126,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 | @@ -221,7 +221,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** | **3925** | ## Issue Summary 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); + } +} From ceee00e6e4bafe4cfa07db3d4f19c688a1b89c2b Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 3 Oct 2024 16:18:24 +0530 Subject: [PATCH 02/25] Relax fixed pragma constraints on library contract files (#740) Co-authored-by: Alex Roan --- .github/workflows/cargo.yml | 5 -- .../detect/low/unspecific_solidity_pragma.rs | 34 ++++++++++- reports/ccip-functions-report.md | 38 +------------ reports/prb-math-report.md | 20 +------ reports/report.json | 20 ++++--- reports/report.md | 23 ++++---- reports/report.sarif | 22 ++++---- reports/sablier-aderyn-toml-nested-root.md | 32 +---------- reports/templegold-report.md | 56 +------------------ tests/contract-playground/src/OnlyLibrary.sol | 4 ++ 10 files changed, 74 insertions(+), 180 deletions(-) create mode 100644 tests/contract-playground/src/OnlyLibrary.sol diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 276e68eb4..f5efaca3f 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: | diff --git a/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs b/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs index 72dbb42a7..632c90085 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,20 @@ 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; } @@ -88,4 +103,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/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/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 ea3575821..56711bf51 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { - "total_source_units": 110, - "total_sloc": 3925 + "total_source_units": 111, + "total_sloc": 3927 }, "files_details": { "files_details": [ @@ -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 @@ -2989,12 +2993,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, @@ -4431,6 +4429,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, diff --git a/reports/report.md b/reports/report.md index a2efee84f..8b1e3f1af 100644 --- a/reports/report.md +++ b/reports/report.md @@ -103,8 +103,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | -| .sol Files | 110 | -| Total nSLOC | 3925 | +| .sol Files | 111 | +| Total nSLOC | 3927 | ## Files Details @@ -162,6 +162,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 | @@ -221,7 +222,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** | **3925** | +| **Total** | **3927** | ## Issue Summary @@ -2830,7 +2831,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 +2966,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 @@ -4310,7 +4305,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 +4410,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 diff --git a/reports/report.sarif b/reports/report.sarif index bf5e74064..f408427f0 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -4462,17 +4462,6 @@ } } }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 23, - "byteOffset": 32 - } - } - }, { "physicalLocation": { "artifactLocation": { @@ -7075,6 +7064,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OnlyLibrary.sol" + }, + "region": { + "byteLength": 23, + "byteOffset": 32 + } + } + }, { "physicalLocation": { "artifactLocation": { 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..bbcb0dbb3 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -1168,7 +1168,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 +1213,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 +1237,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 +1465,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 +1501,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 +1543,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 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 {} From 69a4d5a8c6ee38693bc9e5bc84d1cf06b6fe9cf8 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 4 Oct 2024 23:27:07 +0530 Subject: [PATCH 03/25] Fix: Allow aderyn to exit without crashing if update-check fails (#753) --- aderyn/src/lib.rs | 20 +++++++++++--------- aderyn/src/main.rs | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/aderyn/src/lib.rs b/aderyn/src/lib.rs index 31752b5bb..b34af156f 100644 --- a/aderyn/src/lib.rs +++ b/aderyn/src/lib.rs @@ -88,21 +88,23 @@ 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()?; + .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 +113,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/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."); From c270c0811e304e36a1b2b1cd688642ac0b6a0864 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 4 Oct 2024 23:39:12 +0530 Subject: [PATCH 04/25] Rename `ConstantsInsteadOfLiterals` detector to better suit `//aderyn-ignore-(..)` (#737) --- aderyn_core/src/detect/detector.rs | 10 +++++----- ...f_literals.rs => literals_instead_of_constants.rs} | 11 +++++------ aderyn_core/src/detect/low/mod.rs | 4 ++-- reports/report.json | 4 ++-- reports/report.sarif | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) rename aderyn_core/src/detect/low/{constants_instead_of_literals.rs => literals_instead_of_constants.rs} (95%) diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 33a403ef2..5dd7e54b4 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(), @@ -139,8 +139,8 @@ pub(crate) enum IssueDetectorNamePool { UnspecificSolidityPragma, ZeroAddressCheck, UselessPublicFunction, - ConstantsInsteadOfLiterals, UnindexedEvents, + LiteralInsteadOfConstant, RequireWithString, NonReentrantBeforeOthers, BlockTimestampDeadline, @@ -226,6 +226,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()) } @@ -291,9 +294,6 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::ConstantsInsteadOfLiterals => { - Some(Box::::default()) - } IssueDetectorNamePool::UnindexedEvents => Some(Box::::default()), IssueDetectorNamePool::RequireWithString => { Some(Box::::default()) 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 95% 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..6966364b2 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 @@ -128,7 +128,7 @@ impl IssueDetector for ConstantsInsteadOfLiteralsDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::ConstantsInsteadOfLiterals) + format!("{}", IssueDetectorNamePool::LiteralInsteadOfConstant) } } @@ -136,10 +136,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,7 +146,7 @@ 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); diff --git a/aderyn_core/src/detect/low/mod.rs b/aderyn_core/src/detect/low/mod.rs index 0d0855b73..b19c3fc64 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; @@ -49,7 +49,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 +60,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; diff --git a/reports/report.json b/reports/report.json index 56711bf51..933e4d0a1 100644 --- a/reports/report.json +++ b/reports/report.json @@ -3522,7 +3522,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", @@ -7153,7 +7153,7 @@ "unspecific-solidity-pragma", "zero-address-check", "useless-public-function", - "constants-instead-of-literals", + "literal-instead-of-constant", "unindexed-events", "require-with-string", "non-reentrant-before-others", diff --git a/reports/report.sarif b/reports/report.sarif index f408427f0..85b8ebaa4 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -6286,7 +6286,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", From ae3ed55c5c9dd99c500397554a8c220b7af3327d Mon Sep 17 00:00:00 2001 From: Ritik Agarwal <76695769+Ritik-Agarwal50@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:44:32 +0530 Subject: [PATCH 05/25] Fixed typo `adress` -> `address` (#741) Co-authored-by: Alex Roan Co-authored-by: Tilak Madichetti --- aderyn_core/src/detect/high/delegate_call_no_address_check.rs | 4 ++-- reports/adhoc-sol-files-highs-only-report.json | 2 +- reports/adhoc-sol-files-report.md | 4 ++-- reports/hardhat-playground-report.md | 4 ++-- reports/report.json | 2 +- reports/report.md | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) 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..29b530117 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 @@ -47,7 +47,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 { @@ -119,7 +119,7 @@ mod delegate_call_no_address_check_tests { // 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!( diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 794c02e1b..7ff53eb7d 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -144,7 +144,7 @@ ] }, { - "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", "instances": [ diff --git a/reports/adhoc-sol-files-report.md b/reports/adhoc-sol-files-report.md index 718a34129..2ec7f1f8d 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) @@ -141,7 +141,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 diff --git a/reports/hardhat-playground-report.md b/reports/hardhat-playground-report.md index 856df9649..33d9b3470 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) @@ -138,7 +138,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 diff --git a/reports/report.json b/reports/report.json index 933e4d0a1..416261569 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1908,7 +1908,7 @@ ] }, { - "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", "instances": [ diff --git a/reports/report.md b/reports/report.md index 8b1e3f1af..ae1c408e9 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) @@ -1775,7 +1775,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 From 8d5fdc301969002b76f3ad63ad9be6d00ef389de Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 5 Oct 2024 00:03:41 +0530 Subject: [PATCH 06/25] Revise detector names to be semantically correct with the `//aderyn-ignore..` pattern (#739) --- aderyn_core/src/detect/detector.rs | 52 +++++++++++-------- .../detect/high/avoid_abi_encode_packed.rs | 5 +- .../detect/high/block_timestamp_deadline.rs | 2 +- .../high/delegate_call_no_address_check.rs | 2 +- .../src/detect/high/send_ether_no_checks.rs | 2 +- .../src/detect/low/assert_state_change.rs | 2 +- .../src/detect/low/boolean_equality.rs | 2 +- .../src/detect/low/cache_array_length.rs | 2 +- aderyn_core/src/detect/low/ecrecover.rs | 5 +- .../detect/low/non_reentrant_before_others.rs | 2 +- .../src/detect/low/require_with_string.rs | 2 +- .../src/detect/low/zero_address_check.rs | 2 +- .../adhoc-sol-files-highs-only-report.json | 10 ++-- reports/report.json | 44 ++++++++-------- reports/report.sarif | 22 ++++---- 15 files changed, 85 insertions(+), 71 deletions(-) diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 5dd7e54b4..e3f85133b 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -122,8 +122,8 @@ pub(crate) enum IssueDetectorNamePool { FunctionPointerInConstructor, DeadCode, FunctionSelectorCollision, - CacheArrayLength, - AssertStateChange, + ArrayLengthNotCached, + StateChangeInAssert, CostlyOperationsInsideLoops, ConstantFunctionChangingState, BuiltinSymbolShadow, @@ -132,18 +132,18 @@ pub(crate) enum IssueDetectorNamePool { DelegateCallInLoop, CentralizationRisk, SolmateSafeTransferLib, - AvoidAbiEncodePacked, - Ecrecover, + HashCollisionDueToAbiEncodePacked, + SignatureMalleabilityDueToRawEcrecover, DeprecatedOzFunctions, UnsafeERC20Functions, UnspecificSolidityPragma, - ZeroAddressCheck, + NoZeroAddressCheck, UselessPublicFunction, UnindexedEvents, + RequireWithoutString, + NonReentrantIsNotBeforeOthers, + BlockTimestampIsWeakDeadline, LiteralInsteadOfConstant, - RequireWithString, - NonReentrantBeforeOthers, - BlockTimestampDeadline, UnsafeOzERC721Mint, PushZeroOpcode, ArbitraryTransferFrom, @@ -173,8 +173,8 @@ pub(crate) enum IssueDetectorNamePool { StateVariableShadowing, UncheckedSend, MisusedBoolean, - SendEtherNoChecks, - DelegateCallUncheckedAddress, + SendsEtherAwayWithoutCheckingAddress, + DelegateCallOnUncheckedAddress, TautologicalCompare, #[allow(clippy::upper_case_acronyms)] RTLO, @@ -190,7 +190,7 @@ pub(crate) enum IssueDetectorNamePool { DeleteNestedMapping, UnusedStateVariable, ConstantFunctionsAssembly, - BooleanEquality, + RedundantBooleanEquality, TxOriginUsedForAuth, MsgValueInLoop, ContractLocksEther, @@ -236,8 +236,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 => { @@ -277,10 +279,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()) } @@ -290,18 +294,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::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 +384,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 +423,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/high/avoid_abi_encode_packed.rs b/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs index b7cb5ae31..e1558af3d 100644 --- a/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs +++ b/aderyn_core/src/detect/high/avoid_abi_encode_packed.rs @@ -67,7 +67,10 @@ impl IssueDetector for AvoidAbiEncodePackedDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::AvoidAbiEncodePacked) + format!( + "{}", + IssueDetectorNamePool::HashCollisionDueToAbiEncodePacked + ) } } diff --git a/aderyn_core/src/detect/high/block_timestamp_deadline.rs b/aderyn_core/src/detect/high/block_timestamp_deadline.rs index 55e8f4310..84e98c7bb 100644 --- a/aderyn_core/src/detect/high/block_timestamp_deadline.rs +++ b/aderyn_core/src/detect/high/block_timestamp_deadline.rs @@ -110,7 +110,7 @@ impl IssueDetector for BlockTimestampDeadlineDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::BlockTimestampDeadline) + format!("{}", IssueDetectorNamePool::BlockTimestampIsWeakDeadline) } } 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 29b530117..805c6f4c1 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 @@ -59,7 +59,7 @@ impl IssueDetector for DelegateCallOnUncheckedAddressDetector { } fn name(&self) -> String { - IssueDetectorNamePool::DelegateCallUncheckedAddress.to_string() + IssueDetectorNamePool::DelegateCallOnUncheckedAddress.to_string() } } 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..6573d28aa 100644 --- a/aderyn_core/src/detect/high/send_ether_no_checks.rs +++ b/aderyn_core/src/detect/high/send_ether_no_checks.rs @@ -53,7 +53,7 @@ impl IssueDetector for SendEtherNoChecksDetector { } fn name(&self) -> String { - IssueDetectorNamePool::SendEtherNoChecks.to_string() + IssueDetectorNamePool::SendsEtherAwayWithoutCheckingAddress.to_string() } } diff --git a/aderyn_core/src/detect/low/assert_state_change.rs b/aderyn_core/src/detect/low/assert_state_change.rs index c88701239..5f492267b 100644 --- a/aderyn_core/src/detect/low/assert_state_change.rs +++ b/aderyn_core/src/detect/low/assert_state_change.rs @@ -55,7 +55,7 @@ impl IssueDetector for AssertStateChangeDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::AssertStateChange) + format!("{}", IssueDetectorNamePool::StateChangeInAssert) } } diff --git a/aderyn_core/src/detect/low/boolean_equality.rs b/aderyn_core/src/detect/low/boolean_equality.rs index e967d933c..fb9cf95d9 100644 --- a/aderyn_core/src/detect/low/boolean_equality.rs +++ b/aderyn_core/src/detect/low/boolean_equality.rs @@ -8,7 +8,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/cache_array_length.rs b/aderyn_core/src/detect/low/cache_array_length.rs index 1f9913f0b..3384c5ff5 100644 --- a/aderyn_core/src/detect/low/cache_array_length.rs +++ b/aderyn_core/src/detect/low/cache_array_length.rs @@ -87,7 +87,7 @@ impl IssueDetector for CacheArrayLengthDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::CacheArrayLength) + format!("{}", IssueDetectorNamePool::ArrayLengthNotCached) } } diff --git a/aderyn_core/src/detect/low/ecrecover.rs b/aderyn_core/src/detect/low/ecrecover.rs index 0b79df03d..4b02040d4 100644 --- a/aderyn_core/src/detect/low/ecrecover.rs +++ b/aderyn_core/src/detect/low/ecrecover.rs @@ -49,7 +49,10 @@ impl IssueDetector for EcrecoverDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::Ecrecover) + format!( + "{}", + IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover + ) } } 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..a2c07da0d 100644 --- a/aderyn_core/src/detect/low/non_reentrant_before_others.rs +++ b/aderyn_core/src/detect/low/non_reentrant_before_others.rs @@ -52,7 +52,7 @@ impl IssueDetector for NonReentrantBeforeOthersDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::NonReentrantBeforeOthers) + format!("{}", IssueDetectorNamePool::NonReentrantIsNotBeforeOthers) } } diff --git a/aderyn_core/src/detect/low/require_with_string.rs b/aderyn_core/src/detect/low/require_with_string.rs index a5b231fdf..9d7037265 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) } } diff --git a/aderyn_core/src/detect/low/zero_address_check.rs b/aderyn_core/src/detect/low/zero_address_check.rs index 2e0312117..4211af516 100644 --- a/aderyn_core/src/detect/low/zero_address_check.rs +++ b/aderyn_core/src/detect/low/zero_address_check.rs @@ -184,7 +184,7 @@ impl IssueDetector for ZeroAddressCheckDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::ZeroAddressCheck) + format!("{}", IssueDetectorNamePool::NoZeroAddressCheck) } } diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 7ff53eb7d..8a953244d 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -146,7 +146,7 @@ { "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/report.json b/reports/report.json index 416261569..33674a943 100644 --- a/reports/report.json +++ b/reports/report.json @@ -473,7 +473,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", @@ -498,7 +498,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", @@ -1783,7 +1783,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", @@ -1910,7 +1910,7 @@ { "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", @@ -2706,7 +2706,7 @@ { "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", @@ -3076,7 +3076,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", @@ -4148,7 +4148,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", @@ -4293,7 +4293,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", @@ -5719,7 +5719,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", @@ -6019,7 +6019,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", @@ -6044,7 +6044,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", @@ -7146,18 +7146,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", "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", @@ -7187,8 +7187,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", @@ -7203,7 +7203,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", @@ -7215,8 +7215,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", diff --git a/reports/report.sarif b/reports/report.sarif index 85b8ebaa4..075289b40 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", @@ -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", @@ -4717,7 +4717,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", @@ -6821,7 +6821,7 @@ "message": { "text": "Use descriptive reason strings or custom errors for revert paths." }, - "ruleId": "require-with-string" + "ruleId": "require-without-string" }, { "level": "note", @@ -6852,7 +6852,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", @@ -9427,7 +9427,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 +9943,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 +9963,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", From ccc8ac10dbff33c058f1bf2493982aa85c58b440 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 5 Oct 2024 00:39:23 +0530 Subject: [PATCH 07/25] Low Detector: State variable could be marked immutable + Inter Context Merge Strategy (#734) Co-authored-by: Alex Roan --- aderyn_core/Cargo.toml | 3 + aderyn_core/src/detect/detector.rs | 5 + .../low/function_pointer_in_constructor.rs | 43 ++ aderyn_core/src/detect/low/mod.rs | 2 + .../low/state_variable_could_be_immutable.rs | 193 +++++++++ aderyn_core/src/lib.rs | 105 ++++- cli/reportgen.sh | 1 + reports/adhoc-sol-files-report.md | 32 +- reports/hardhat-playground-report.md | 26 +- reports/report.json | 238 ++++++++++- reports/report.md | 247 ++++++++++- reports/report.sarif | 396 +++++++++++++++++- reports/templegold-report.md | 192 +++++---- reports/uniswap_profile.md | 20 +- .../StateVariableCouldBeDeclaredImmutable.sol | 40 ++ .../src/inheritance/ExtendedInheritance.sol | 1 + .../src/inheritance/InheritanceBase.sol | 6 + 17 files changed, 1434 insertions(+), 116 deletions(-) create mode 100644 aderyn_core/src/detect/low/state_variable_could_be_immutable.rs create mode 100644 tests/contract-playground/src/StateVariableCouldBeDeclaredImmutable.sol diff --git a/aderyn_core/Cargo.toml b/aderyn_core/Cargo.toml index 07dd80e34..49dd8b57e 100644 --- a/aderyn_core/Cargo.toml +++ b/aderyn_core/Cargo.toml @@ -33,3 +33,6 @@ derive_more = "0.99.18" [dev-dependencies] serial_test = "3.0.0" once_cell = "1.19.0" + +[lib] +doctest = false diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index e3f85133b..9002caecf 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -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, @@ -209,6 +211,9 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { + Some(Box::::default()) + } IssueDetectorNamePool::MultiplePlaceholders => { Some(Box::::default()) } 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..d59cd5622 100644 --- a/aderyn_core/src/detect/low/function_pointer_in_constructor.rs +++ b/aderyn_core/src/detect/low/function_pointer_in_constructor.rs @@ -99,6 +99,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 +137,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)] diff --git a/aderyn_core/src/detect/low/mod.rs b/aderyn_core/src/detect/low/mod.rs index b19c3fc64..4091db088 100644 --- a/aderyn_core/src/detect/low/mod.rs +++ b/aderyn_core/src/detect/low/mod.rs @@ -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; @@ -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/state_variable_could_be_immutable.rs b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs new file mode 100644 index 000000000..27ff2b795 --- /dev/null +++ b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs @@ -0,0 +1,193 @@ +use std::collections::BTreeMap; +use std::error::Error; + +use crate::ast::{FunctionKind, Mutability, NodeID}; + +use crate::capture; +use crate::context::browser::ApproximateStorageChangeFinder; +use crate::detect::detector::IssueDetectorNamePool; +use crate::detect::helpers; +use crate::{ + context::workspace_context::WorkspaceContext, + detect::detector::{IssueDetector, IssueSeverity}, +}; + +#[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/lib.rs b/aderyn_core/src/lib.rs index b0925aecb..f78cb94c0 100644 --- a/aderyn_core/src/lib.rs +++ b/aderyn_core/src/lib.rs @@ -122,24 +122,119 @@ 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; } 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-report.md b/reports/adhoc-sol-files-report.md index 2ec7f1f8d..54f803537 100644 --- a/reports/adhoc-sol-files-report.md +++ b/reports/adhoc-sol-files-report.md @@ -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 @@ -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/hardhat-playground-report.md b/reports/hardhat-playground-report.md index 33d9b3470..044fb7da1 100644 --- a/reports/hardhat-playground-report.md +++ b/reports/hardhat-playground-report.md @@ -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 @@ -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/report.json b/reports/report.json index 33674a943..938507cae 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { - "total_source_units": 111, - "total_sloc": 3927 + "total_source_units": 112, + "total_sloc": 3954 }, "files_details": { "files_details": [ @@ -253,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 @@ -403,7 +407,7 @@ }, { "file_path": "src/inheritance/ExtendedInheritance.sol", - "n_sloc": 17 + "n_sloc": 18 }, { "file_path": "src/inheritance/IContractInheritance.sol", @@ -411,7 +415,7 @@ }, { "file_path": "src/inheritance/InheritanceBase.sol", - "n_sloc": 8 + "n_sloc": 12 }, { "file_path": "src/nested/1/Nested.sol", @@ -453,7 +457,7 @@ }, "issue_count": { "high": 42, - "low": 44 + "low": 45 }, "high_issues": { "issues": [ @@ -2710,9 +2714,9 @@ "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" } ] }, @@ -3126,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, @@ -5147,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, @@ -6831,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, @@ -7107,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, @@ -7127,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.", @@ -7228,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 ae1c408e9..c30290fb9 100644 --- a/reports/report.md +++ b/reports/report.md @@ -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 | 111 | -| Total nSLOC | 3927 | +| .sol Files | 112 | +| Total nSLOC | 3954 | ## Files Details @@ -173,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 | @@ -210,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 | @@ -222,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** | **3927** | +| **Total** | **3954** | ## Issue Summary @@ -230,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 @@ -2669,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); @@ -3052,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) @@ -3103,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 @@ -5057,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) @@ -5144,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 @@ -6632,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) @@ -6929,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 @@ -7205,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 @@ -7227,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 075289b40..824261d60 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -3958,7 +3958,7 @@ }, "region": { "byteLength": 9, - "byteOffset": 705 + "byteOffset": 737 } } } @@ -4702,6 +4702,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/inheritance/ExtendedInheritance.sol" + }, + "region": { + "byteLength": 22, + "byteOffset": 579 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -8365,6 +8376,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariableCouldBeDeclaredImmutable.sol" + }, + "region": { + "byteLength": 14, + "byteOffset": 706 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -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/templegold-report.md b/reports/templegold-report.md index bbcb0dbb3..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 @@ -8314,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) @@ -8341,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 @@ -8389,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 @@ -9026,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/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); } From 748ae7fc6da5bd63f1955cb1a7b5eb6b36e0ad61 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 5 Oct 2024 01:37:53 +0530 Subject: [PATCH 08/25] standardize rust formatting with `rustfmt.toml` (#755) --- .git-blame-ignore-revs | 12 + .github/workflows/cargo.yml | 21 +- aderyn/src/lib.rs | 6 +- aderyn/src/lsp.rs | 55 ++-- aderyn/src/panic.rs | 22 +- aderyn_core/src/ast/ast.rs | 3 +- aderyn_core/src/ast/ast_nodes.rs | 31 ++- aderyn_core/src/ast/impls/disp/expressions.rs | 16 +- aderyn_core/src/ast/impls/disp/types.rs | 5 +- .../impls/disp/user_defined_value_types.rs | 5 +- aderyn_core/src/ast/impls/node/blocks.rs | 12 +- .../src/ast/impls/node/documentation.rs | 5 +- .../src/ast/impls/node/enumerations.rs | 10 +- aderyn_core/src/ast/impls/node/errors.rs | 3 +- aderyn_core/src/ast/impls/node/events.rs | 3 +- aderyn_core/src/ast/impls/node/expressions.rs | 3 +- aderyn_core/src/ast/impls/node/functions.rs | 10 +- aderyn_core/src/ast/impls/node/identifiers.rs | 3 +- .../src/ast/impls/node/import_directives.rs | 3 +- aderyn_core/src/ast/impls/node/modifiers.rs | 3 +- .../src/ast/impls/node/pragma_directives.rs | 3 +- .../src/ast/impls/node/source_units.rs | 9 +- aderyn_core/src/ast/impls/node/statements.rs | 10 +- aderyn_core/src/ast/impls/node/structures.rs | 3 +- aderyn_core/src/ast/impls/node/types.rs | 3 +- .../impls/node/user_defined_value_types.rs | 7 +- .../ast/impls/node/using_for_directives.rs | 3 +- aderyn_core/src/ast/impls/node/variables.rs | 3 +- aderyn_core/src/ast/impls/own/contracts.rs | 17 +- aderyn_core/src/ast/impls/own/expressions.rs | 78 +++--- aderyn_core/src/ast/impls/own/functions.rs | 7 +- aderyn_core/src/ast/impls/own/identifiers.rs | 13 +- aderyn_core/src/ast/impls/own/source_units.rs | 8 +- aderyn_core/src/ast/impls/own/statements.rs | 6 +- aderyn_core/src/ast/impls/own/types.rs | 3 +- aderyn_core/src/ast/mod.rs | 30 +- aderyn_core/src/ast/yul.rs | 5 +- .../src/audit/public_functions_no_sender.rs | 15 +- aderyn_core/src/context/browser/extractor.rs | 6 +- aderyn_core/src/context/browser/siblings.rs | 3 +- aderyn_core/src/context/browser/sort_nodes.rs | 4 +- .../src/context/browser/storage_vars.rs | 261 ++++++------------ aderyn_core/src/context/graph/callgraph.rs | 76 ++--- .../src/context/graph/callgraph_tests.rs | 54 +--- aderyn_core/src/context/graph/traits.rs | 12 +- .../src/context/graph/workspace_callgraph.rs | 12 +- aderyn_core/src/context/meta_workspace.rs | 15 +- aderyn_core/src/context/workspace_context.rs | 16 +- .../src/detect/experimental/ancestral_line.rs | 5 +- .../detect/experimental/closest_ancestor.rs | 5 +- .../detect/experimental/immediate_children.rs | 5 +- .../detect/experimental/immediate_parent.rs | 5 +- aderyn_core/src/detect/helpers.rs | 49 +--- .../detect/high/arbitrary_transfer_from.rs | 41 ++- .../detect/high/avoid_abi_encode_packed.rs | 28 +- .../detect/high/block_timestamp_deadline.rs | 37 ++- .../detect/high/const_func_change_state.rs | 30 +- .../src/detect/high/contract_locks_ether.rs | 24 +- .../high/dangerous_strict_equality_balance.rs | 31 +-- .../detect/high/dangerous_unary_operator.rs | 21 +- .../src/detect/high/delegate_call_in_loop.rs | 13 +- .../high/delegate_call_no_address_check.rs | 26 +- .../detect/high/deletion_nested_mapping.rs | 32 +-- .../high/dynamic_array_length_assignment.rs | 18 +- .../detect/high/enumerable_loop_removal.rs | 32 +-- .../src/detect/high/experimental_encoder.rs | 13 +- .../high/function_selector_collision.rs | 22 +- .../detect/high/incorrect_caret_operator.rs | 27 +- .../detect/high/incorrect_erc20_interface.rs | 45 +-- .../detect/high/incorrect_erc721_interface.rs | 51 ++-- .../src/detect/high/incorrect_shift_order.rs | 18 +- .../src/detect/high/misused_boolean.rs | 3 +- .../src/detect/high/msg_value_in_loops.rs | 44 ++- .../src/detect/high/multiple_constructors.rs | 21 +- .../detect/high/nested_struct_in_mapping.rs | 32 +-- .../src/detect/high/out_of_order_retryable.rs | 36 +-- .../high/pre_declared_variable_usage.rs | 45 ++- .../src/detect/high/reused_contract_name.rs | 26 +- aderyn_core/src/detect/high/rtlo.rs | 18 +- aderyn_core/src/detect/high/selfdestruct.rs | 13 +- .../src/detect/high/send_ether_no_checks.rs | 24 +- .../detect/high/state_variable_shadowing.rs | 53 ++-- .../high/storage_array_edit_with_memory.rs | 34 +-- .../high/storage_signed_integer_array.rs | 32 +-- .../src/detect/high/tautological_compare.rs | 27 +- .../detect/high/tautology_or_contradiction.rs | 60 ++-- .../detect/high/tx_origin_used_for_auth.rs | 25 +- .../src/detect/high/unchecked_calls.rs | 33 +-- .../src/detect/high/unchecked_return.rs | 27 +- aderyn_core/src/detect/high/unchecked_send.rs | 32 +-- .../high/uninitialized_state_variable.rs | 30 +- .../detect/high/unprotected_init_function.rs | 5 +- aderyn_core/src/detect/high/unsafe_casting.rs | 56 ++-- .../src/detect/high/weak_randomness.rs | 19 +- aderyn_core/src/detect/high/yul_return.rs | 18 +- .../src/detect/low/assert_state_change.rs | 34 +-- .../src/detect/low/boolean_equality.rs | 3 +- .../detect/low/builtin_symbol_shadowing.rs | 11 +- .../src/detect/low/cache_array_length.rs | 48 ++-- .../src/detect/low/centralization_risk.rs | 10 +- .../src/detect/low/constant_funcs_assembly.rs | 39 ++- .../src/detect/low/contracts_with_todos.rs | 5 +- .../low/costly_operations_inside_loops.rs | 26 +- aderyn_core/src/detect/low/dead_code.rs | 25 +- .../src/detect/low/deprecated_oz_functions.rs | 5 +- .../low/division_before_multiplication.rs | 19 +- aderyn_core/src/detect/low/ecrecover.rs | 10 +- aderyn_core/src/detect/low/empty_blocks.rs | 10 +- .../detect/low/function_init_state_vars.rs | 37 +-- .../low/function_pointer_in_constructor.rs | 16 +- .../src/detect/low/inconsistent_type_names.rs | 10 +- .../src/detect/low/large_literal_value.rs | 11 +- .../low/literals_instead_of_constants.rs | 25 +- .../detect/low/local_variable_shadowing.rs | 35 +-- .../src/detect/low/missing_inheritance.rs | 35 +-- .../src/detect/low/multiple_placeholders.rs | 16 +- .../detect/low/non_reentrant_before_others.rs | 11 +- ...ublic_variable_read_in_external_context.rs | 31 +-- aderyn_core/src/detect/low/push_0_opcode.rs | 20 +- .../src/detect/low/redundant_statements.rs | 21 +- .../src/detect/low/require_with_string.rs | 10 +- aderyn_core/src/detect/low/return_bomb.rs | 68 +++-- .../low/reverts_and_requries_in_loops.rs | 21 +- .../detect/low/solmate_safe_transfer_lib.rs | 20 +- .../state_variable_changes_without_events.rs | 34 +-- .../low/state_variable_could_be_constant.rs | 55 ++-- .../low/state_variable_could_be_immutable.rs | 46 ++- .../src/detect/low/unindexed_events.rs | 5 +- .../low/uninitialized_local_variables.rs | 46 ++- .../src/detect/low/unsafe_erc20_functions.rs | 10 +- .../src/detect/low/unsafe_oz_erc721_mint.rs | 10 +- .../detect/low/unspecific_solidity_pragma.rs | 15 +- aderyn_core/src/detect/low/unused_imports.rs | 42 +-- .../src/detect/low/unused_state_variable.rs | 31 ++- aderyn_core/src/detect/low/useless_error.rs | 10 +- .../detect/low/useless_internal_function.rs | 15 +- .../src/detect/low/useless_modifier.rs | 5 +- .../src/detect/low/useless_public_function.rs | 18 +- .../src/detect/low/void_constructor.rs | 13 +- .../src/detect/low/zero_address_check.rs | 37 +-- aderyn_core/src/detect/mod.rs | 21 +- .../src/detect/test_utils/load_source_unit.rs | 33 +-- aderyn_core/src/detect/test_utils/mod.rs | 10 +- aderyn_core/src/fscloc/cloc.rs | 32 +-- aderyn_core/src/fscloc/insight.rs | 6 +- aderyn_core/src/fscloc/mod.rs | 4 +- aderyn_core/src/lib.rs | 37 +-- aderyn_core/src/report/json_printer.rs | 4 +- aderyn_core/src/report/markdown_printer.rs | 35 +-- aderyn_core/src/report/mod.rs | 11 +- aderyn_core/src/report/printer.rs | 3 +- aderyn_core/src/report/reporter.rs | 13 +- aderyn_core/src/report/sarif_printer.rs | 7 +- aderyn_core/src/report/util.rs | 16 +- aderyn_core/src/visitor/workspace_visitor.rs | 24 +- aderyn_driver/src/config_helpers.rs | 57 +--- aderyn_driver/src/driver.rs | 50 +--- aderyn_driver/src/foundry_compiler_helpers.rs | 30 +- aderyn_driver/src/lib.rs | 19 +- aderyn_driver/src/lsp_report.rs | 21 +- aderyn_driver/src/process_auto.rs | 28 +- aderyn_driver/src/project_compiler_tests.rs | 29 +- aderyn_py/src/lib.rs | 6 +- rustfmt.toml | 11 + 164 files changed, 1320 insertions(+), 2415 deletions(-) create mode 100644 .git-blame-ignore-revs create mode 100644 rustfmt.toml diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..4677d5eba --- /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) +9bfd0468af34a0a1d9751b1451a85a510e0cb72f diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index f5efaca3f..eccc4ada5 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -267,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 b34af156f..3abe1cbc4 100644 --- a/aderyn/src/lib.rs +++ b/aderyn/src/lib.rs @@ -94,10 +94,8 @@ pub fn aderyn_is_currently_running_newest_version() -> Option { .build() .expect("client is unable to initialize"); - let latest_version_checker = client - .get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest") - .send() - .ok()?; + let latest_version_checker = + client.get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest").send().ok()?; let data = latest_version_checker.json::().ok()?; let version_string = data["tag_name"].as_str()?; 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/panic.rs b/aderyn/src/panic.rs index 147f5c223..045644f18 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,9 +42,7 @@ 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(); 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/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 de8a09498..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 @@ -276,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 e1558af3d..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,10 +61,7 @@ impl IssueDetector for AvoidAbiEncodePackedDetector { } fn name(&self) -> String { - format!( - "{}", - IssueDetectorNamePool::HashCollisionDueToAbiEncodePacked - ) + format!("{}", IssueDetectorNamePool::HashCollisionDueToAbiEncodePacked) } } @@ -97,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 84e98c7bb..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" @@ -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 805c6f4c1..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; @@ -111,10 +113,7 @@ 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!( @@ -122,9 +121,6 @@ mod delegate_call_no_address_check_tests { 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 6573d28aa..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; @@ -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 5f492267b..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); } @@ -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 fb9cf95d9..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! { 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 3384c5ff5..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); } @@ -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 4b02040d4..47654c4de 100644 --- a/aderyn_core/src/detect/low/ecrecover.rs +++ b/aderyn_core/src/detect/low/ecrecover.rs @@ -49,10 +49,7 @@ impl IssueDetector for EcrecoverDetector { } fn name(&self) -> String { - format!( - "{}", - IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover - ) + format!("{}", IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover) } } @@ -79,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 d59cd5622..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; @@ -189,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/literals_instead_of_constants.rs b/aderyn_core/src/detect/low/literals_instead_of_constants.rs index 6966364b2..e077afca3 100644 --- a/aderyn_core/src/detect/low/literals_instead_of_constants.rs +++ b/aderyn_core/src/detect/low/literals_instead_of_constants.rs @@ -37,10 +37,7 @@ impl IssueDetector for LiteralsInsteadOfConstantsDetector { 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 LiteralsInsteadOfConstantsDetector { 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 LiteralsInsteadOfConstantsDetector { } } - 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 LiteralsInsteadOfConstantsDetector { 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]); } @@ -153,10 +141,7 @@ mod constants_instead_of_literals_tests { // 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/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 a2c07da0d..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); @@ -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 9d7037265..d6de47372 100644 --- a/aderyn_core/src/detect/low/require_with_string.rs +++ b/aderyn_core/src/detect/low/require_with_string.rs @@ -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 index 27ff2b795..563b9e80c 100644 --- a/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs +++ b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs @@ -1,15 +1,14 @@ -use std::collections::BTreeMap; -use std::error::Error; +use std::{collections::BTreeMap, error::Error}; use crate::ast::{FunctionKind, Mutability, NodeID}; -use crate::capture; -use crate::context::browser::ApproximateStorageChangeFinder; -use crate::detect::detector::IssueDetectorNamePool; -use crate::detect::helpers; use crate::{ - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueSeverity}, + capture, + context::{browser::ApproximateStorageChangeFinder, workspace_context::WorkspaceContext}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; #[derive(Default)] @@ -45,7 +44,8 @@ impl IssueDetector for StateVariableCouldBeImmutableDetector { continue; } - // Doesn't make sense to look for possible immutability if it's already declared constant + // Doesn't make sense to look for possible immutability if it's already declared + // constant if variable.mutability() == Some(&Mutability::Constant) { continue; } @@ -60,14 +60,9 @@ impl IssueDetector for StateVariableCouldBeImmutableDetector { } // 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; } @@ -120,10 +115,9 @@ impl IssueDetector for StateVariableCouldBeImmutableDetector { } // 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, - ) { + 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 { @@ -154,10 +148,7 @@ impl IssueDetector for StateVariableCouldBeImmutableDetector { } fn name(&self) -> String { - format!( - "{}", - IssueDetectorNamePool::StateVariableCouldBeDeclaredImmutable - ) + format!("{}", IssueDetectorNamePool::StateVariableCouldBeDeclaredImmutable) } } @@ -185,9 +176,6 @@ mod state_variable_could_be_immutable_tests { assert_eq!(detector.instances().len(), 2); 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/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 632c90085..014fa3c49 100644 --- a/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs +++ b/aderyn_core/src/detect/low/unspecific_solidity_pragma.rs @@ -27,10 +27,7 @@ impl IssueDetector for UnspecificSolidityPragmaDetector { continue; }; let contracts_in_source_unit = ExtractContractDefinitions::from(source_unit).extracted; - if contracts_in_source_unit - .iter() - .any(|c| c.kind == ContractKind::Library) - { + if contracts_in_source_unit.iter().any(|c| c.kind == ContractKind::Library) { continue; } for literal in &pragma_directive.literals { @@ -86,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(), 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 4211af516..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 { @@ -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 1b89695fa..b06b02801 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 f78cb94c0..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( @@ -131,11 +129,7 @@ pub fn get_report( return (instances, hints, context.src_filepaths.clone()); } } - ( - Default::default(), - Default::default(), - context.src_filepaths.clone(), - ) + (Default::default(), Default::default(), context.src_filepaths.clone()) }) .collect::>(); @@ -214,10 +208,7 @@ pub fn get_report( for instances in &value { for instance in instances { - if value - .iter() - .all(|tree| tree.contains_key(&instance.0.clone())) - { + if value.iter().all(|tree| tree.contains_key(&instance.0.clone())) { selected_instances.insert(instance.0.clone(), *instance.1); } } @@ -318,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/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 From 0279da5cf20fb1e4bd9d83b766f1f00c6c6de6e9 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 5 Oct 2024 10:57:34 +0530 Subject: [PATCH 09/25] feat: Add refactor commit to blame-ingore-revs (#756) --- .git-blame-ignore-revs | 2 +- aderyn/src/panic.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4677d5eba..2cc52c5c8 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -9,4 +9,4 @@ # $ git config blame.ignoreRevsFile .git-blame-ignore-revs # fmt: all (#3398) -9bfd0468af34a0a1d9751b1451a85a510e0cb72f +748ae7fc6da5bd63f1955cb1a7b5eb6b36e0ad61 diff --git a/aderyn/src/panic.rs b/aderyn/src/panic.rs index 045644f18..ae9792f7a 100644 --- a/aderyn/src/panic.rs +++ b/aderyn/src/panic.rs @@ -49,7 +49,7 @@ fn print_compiler_bug_message(info: &PanicInfo<'_>) { 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. From 6db64b218e2bd74444948acb06c9ddb3ae4bacd3 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 10 Oct 2024 17:31:29 +0530 Subject: [PATCH 10/25] Shorten names (Start of 0.3.2) (#759) --- aderyn_core/src/detect/detector.rs | 18 ++++++++---------- .../high/dangerous_strict_equality_balance.rs | 2 +- aderyn_core/src/detect/low/ecrecover.rs | 2 +- .../low/state_variable_could_be_constant.rs | 2 +- .../low/state_variable_could_be_immutable.rs | 2 +- reports/adhoc-sol-files-highs-only-report.json | 2 +- reports/report.json | 16 ++++++++-------- reports/report.sarif | 8 ++++---- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 9002caecf..40d79390f 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -114,7 +114,7 @@ pub fn get_all_detectors_names() -> Vec { #[derive(Debug, PartialEq, EnumString, Display)] #[strum(serialize_all = "kebab-case")] pub(crate) enum IssueDetectorNamePool { - StateVariableCouldBeDeclaredImmutable, + StateVariableCouldBeImmutable, MultiplePlaceholders, StateVariableChangesWithoutEvents, MissingInheritance, @@ -135,7 +135,7 @@ pub(crate) enum IssueDetectorNamePool { CentralizationRisk, SolmateSafeTransferLib, HashCollisionDueToAbiEncodePacked, - SignatureMalleabilityDueToRawEcrecover, + RawEcrecover, DeprecatedOzFunctions, UnsafeERC20Functions, UnspecificSolidityPragma, @@ -183,7 +183,7 @@ pub(crate) enum IssueDetectorNamePool { UncheckedReturn, DangerousUnaryOperator, TautologyOrContradiction, - DangerousStrictEquailtyOnContractBalance, + StrictEquailtyCheckOnContractBalance, SignedStorageArray, RedundantStatements, PublicVariableReadInExternalContext, @@ -201,7 +201,7 @@ pub(crate) enum IssueDetectorNamePool { UninitializedLocalVariable, ReturnBomb, OutOfOrderRetryable, - StateVariableCouldBeDeclaredConstant, + StateVariableCouldBeConstant, // 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, @@ -211,7 +211,7 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { + IssueDetectorNamePool::StateVariableCouldBeImmutable => { Some(Box::::default()) } IssueDetectorNamePool::MultiplePlaceholders => { @@ -228,7 +228,7 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option Some(Box::::default()), IssueDetectorNamePool::VoidConstructor => Some(Box::::default()), - IssueDetectorNamePool::StateVariableCouldBeDeclaredConstant => { + IssueDetectorNamePool::StateVariableCouldBeConstant => { Some(Box::::default()) } IssueDetectorNamePool::LiteralInsteadOfConstant => { @@ -287,9 +287,7 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::SignatureMalleabilityDueToRawEcrecover => { - Some(Box::::default()) - } + IssueDetectorNamePool::RawEcrecover => Some(Box::::default()), IssueDetectorNamePool::DeprecatedOzFunctions => { Some(Box::::default()) } @@ -406,7 +404,7 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option { Some(Box::::default()) } - IssueDetectorNamePool::DangerousStrictEquailtyOnContractBalance => { + IssueDetectorNamePool::StrictEquailtyCheckOnContractBalance => { Some(Box::::default()) } IssueDetectorNamePool::SignedStorageArray => { 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 30579c598..9cb1cdbed 100644 --- a/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs +++ b/aderyn_core/src/detect/high/dangerous_strict_equality_balance.rs @@ -73,7 +73,7 @@ impl IssueDetector for DangerousStrictEqualityOnBalanceDetector { } fn name(&self) -> String { - IssueDetectorNamePool::DangerousStrictEquailtyOnContractBalance.to_string() + IssueDetectorNamePool::StrictEquailtyCheckOnContractBalance.to_string() } } diff --git a/aderyn_core/src/detect/low/ecrecover.rs b/aderyn_core/src/detect/low/ecrecover.rs index 47654c4de..b0503400f 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::SignatureMalleabilityDueToRawEcrecover) + format!("{}", IssueDetectorNamePool::RawEcrecover) } } 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 c9403f2a1..dba18ccdc 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 @@ -106,7 +106,7 @@ impl IssueDetector for StateVariableCouldBeConstantDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::StateVariableCouldBeDeclaredConstant) + format!("{}", IssueDetectorNamePool::StateVariableCouldBeConstant) } } 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 index 563b9e80c..92053837a 100644 --- a/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs +++ b/aderyn_core/src/detect/low/state_variable_could_be_immutable.rs @@ -148,7 +148,7 @@ impl IssueDetector for StateVariableCouldBeImmutableDetector { } fn name(&self) -> String { - format!("{}", IssueDetectorNamePool::StateVariableCouldBeDeclaredImmutable) + format!("{}", IssueDetectorNamePool::StateVariableCouldBeImmutable) } } diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 8a953244d..b9c9e004f 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -203,7 +203,7 @@ "unchecked-return", "dangerous-unary-operator", "tautology-or-contradiction", - "dangerous-strict-equailty-on-contract-balance", + "strict-equailty-check-on-contract-balance", "signed-storage-array", "weak-randomness", "pre-declared-local-variable-usage", diff --git a/reports/report.json b/reports/report.json index 938507cae..ffb67e4f7 100644 --- a/reports/report.json +++ b/reports/report.json @@ -2100,7 +2100,7 @@ { "title": "Dangerous strict equality checks on contract balances.", "description": "A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality.", - "detector_name": "dangerous-strict-equailty-on-contract-balance", + "detector_name": "strict-equailty-check-on-contract-balance", "instances": [ { "contract_path": "src/DangerousStrictEquality1.sol", @@ -2710,7 +2710,7 @@ { "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": "signature-malleability-due-to-raw-ecrecover", + "detector_name": "raw-ecrecover", "instances": [ { "contract_path": "src/inheritance/ExtendedInheritance.sol", @@ -6274,7 +6274,7 @@ { "title": "State variable could be declared constant", "description": "State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change.", - "detector_name": "state-variable-could-be-declared-constant", + "detector_name": "state-variable-could-be-constant", "instances": [ { "contract_path": "src/CostlyOperationsInsideLoops.sol", @@ -7158,7 +7158,7 @@ { "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", + "detector_name": "state-variable-could-be-immutable", "instances": [ { "contract_path": "src/ArbitraryTransferFrom.sol", @@ -7368,7 +7368,7 @@ "centralization-risk", "solmate-safe-transfer-lib", "hash-collision-due-to-abi-encode-packed", - "signature-malleability-due-to-raw-ecrecover", + "raw-ecrecover", "deprecated-oz-functions", "unsafe-erc20-functions", "unspecific-solidity-pragma", @@ -7415,7 +7415,7 @@ "unchecked-return", "dangerous-unary-operator", "tautology-or-contradiction", - "dangerous-strict-equailty-on-contract-balance", + "strict-equailty-check-on-contract-balance", "signed-storage-array", "redundant-statements", "public-variable-read-in-external-context", @@ -7447,9 +7447,9 @@ "unused-import", "unchecked-low-level-call", "function-pointer-in-constructor", - "state-variable-could-be-declared-constant", + "state-variable-could-be-constant", "state-variable-changes-without-events", - "state-variable-could-be-declared-immutable", + "state-variable-could-be-immutable", "multiple-placeholders" ] } \ No newline at end of file diff --git a/reports/report.sarif b/reports/report.sarif index 824261d60..dba590fcb 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -2936,7 +2936,7 @@ "message": { "text": "A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality." }, - "ruleId": "dangerous-strict-equailty-on-contract-balance" + "ruleId": "strict-equailty-check-on-contract-balance" }, { "level": "warning", @@ -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": "signature-malleability-due-to-raw-ecrecover" + "ruleId": "raw-ecrecover" }, { "level": "note", @@ -10838,7 +10838,7 @@ "message": { "text": "State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change." }, - "ruleId": "state-variable-could-be-declared-constant" + "ruleId": "state-variable-could-be-constant" }, { "level": "note", @@ -12297,7 +12297,7 @@ "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" + "ruleId": "state-variable-could-be-immutable" }, { "level": "note", From 6939aa50559c744d53a2e0ebba903d9d2613cc0a Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 10 Oct 2024 17:41:03 +0530 Subject: [PATCH 11/25] Bump version to 0.3.2 (#760) --- Cargo.lock | 8 ++++---- aderyn/Cargo.toml | 4 ++-- aderyn_core/Cargo.toml | 2 +- aderyn_driver/Cargo.toml | 4 ++-- aderyn_py/Cargo.toml | 6 +++--- reports/report.sarif | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c881c5f0..f4544cc19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "aderyn" -version = "0.3.0" +version = "0.3.2" dependencies = [ "aderyn_driver", "clap", @@ -44,7 +44,7 @@ dependencies = [ [[package]] name = "aderyn_core" -version = "0.3.0" +version = "0.3.2" dependencies = [ "crossbeam-channel", "cyfrin-foundry-compilers", @@ -70,7 +70,7 @@ dependencies = [ [[package]] name = "aderyn_driver" -version = "0.3.0" +version = "0.3.2" dependencies = [ "aderyn_core", "criterion", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "aderyn_py" -version = "0.3.0" +version = "0.3.2" dependencies = [ "aderyn_driver", "field_access", diff --git a/aderyn/Cargo.toml b/aderyn/Cargo.toml index e5449a300..3b0484ff1 100644 --- a/aderyn/Cargo.toml +++ b/aderyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn" -version = "0.3.0" +version = "0.3.2" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer" @@ -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.3.0" } +aderyn_driver = { path = "../aderyn_driver", version = "0.3.2" } clap = { version = "4.4.6", features = ["derive"] } reqwest = { version = "0.12.2", default-features = false, features = ["blocking", "json", "rustls-tls"] } semver = "1.0.22" diff --git a/aderyn_core/Cargo.toml b/aderyn_core/Cargo.toml index 49dd8b57e..f3dd4915f 100644 --- a/aderyn_core/Cargo.toml +++ b/aderyn_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_core" -version = "0.3.0" +version = "0.3.2" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer backend" diff --git a/aderyn_driver/Cargo.toml b/aderyn_driver/Cargo.toml index a7e808d2c..b8ea45e59 100644 --- a/aderyn_driver/Cargo.toml +++ b/aderyn_driver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_driver" -version = "0.3.0" +version = "0.3.2" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer driver" @@ -9,7 +9,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aderyn_core = { path = "../aderyn_core", version = "0.3.0" } +aderyn_core = { path = "../aderyn_core", version = "0.3.2" } rayon = "1.8.0" cyfrin-foundry-compilers = { version = "0.3.20-aderyn", features = ["svm-solc"] } serde_json = { version = "1.0.96", features = ["preserve_order"] } diff --git a/aderyn_py/Cargo.toml b/aderyn_py/Cargo.toml index 89671d0ab..2e735a3a8 100644 --- a/aderyn_py/Cargo.toml +++ b/aderyn_py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_py" -version = "0.3.0" +version = "0.3.2" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer python bindings" @@ -14,10 +14,10 @@ name = "aderynpy" crate-type = ["cdylib"] [dependencies] -aderyn_driver = { path = "../aderyn_driver", version = "0.3.0" } +aderyn_driver = { path = "../aderyn_driver", version = "0.3.2" } field_access = "0.1.8" [dependencies.pyo3] version = "0.22.2" # "abi3-py38" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.8 -features = ["abi3-py38"] \ No newline at end of file +features = ["abi3-py38"] diff --git a/reports/report.sarif b/reports/report.sarif index dba590fcb..01d2a49a1 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -12326,8 +12326,8 @@ "informationUri": "https://github.com/Cyfrin/aderyn", "name": "Aderyn", "organization": "Cyfrin", - "semanticVersion": "0.3.0", - "version": "0.3.0" + "semanticVersion": "0.3.2", + "version": "0.3.2" } } } From f00f67869e37fe76c5c89f4b4a876f38ad8a09fc Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 10 Oct 2024 21:40:53 +0530 Subject: [PATCH 12/25] Bump up version to 0.3.3 (#761) --- Cargo.lock | 8 ++++---- aderyn/Cargo.toml | 4 ++-- aderyn_core/Cargo.toml | 2 +- aderyn_driver/Cargo.toml | 4 ++-- aderyn_py/Cargo.toml | 4 ++-- reports/report.sarif | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4544cc19..9eb38c1b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "aderyn" -version = "0.3.2" +version = "0.3.3" dependencies = [ "aderyn_driver", "clap", @@ -44,7 +44,7 @@ dependencies = [ [[package]] name = "aderyn_core" -version = "0.3.2" +version = "0.3.3" dependencies = [ "crossbeam-channel", "cyfrin-foundry-compilers", @@ -70,7 +70,7 @@ dependencies = [ [[package]] name = "aderyn_driver" -version = "0.3.2" +version = "0.3.3" dependencies = [ "aderyn_core", "criterion", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "aderyn_py" -version = "0.3.2" +version = "0.3.3" dependencies = [ "aderyn_driver", "field_access", diff --git a/aderyn/Cargo.toml b/aderyn/Cargo.toml index 3b0484ff1..340d6c10e 100644 --- a/aderyn/Cargo.toml +++ b/aderyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn" -version = "0.3.2" +version = "0.3.3" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer" @@ -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.3.2" } +aderyn_driver = { path = "../aderyn_driver", version = "0.3.3" } clap = { version = "4.4.6", features = ["derive"] } reqwest = { version = "0.12.2", default-features = false, features = ["blocking", "json", "rustls-tls"] } semver = "1.0.22" diff --git a/aderyn_core/Cargo.toml b/aderyn_core/Cargo.toml index f3dd4915f..138034914 100644 --- a/aderyn_core/Cargo.toml +++ b/aderyn_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_core" -version = "0.3.2" +version = "0.3.3" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer backend" diff --git a/aderyn_driver/Cargo.toml b/aderyn_driver/Cargo.toml index b8ea45e59..0e99b2f83 100644 --- a/aderyn_driver/Cargo.toml +++ b/aderyn_driver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_driver" -version = "0.3.2" +version = "0.3.3" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer driver" @@ -9,7 +9,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aderyn_core = { path = "../aderyn_core", version = "0.3.2" } +aderyn_core = { path = "../aderyn_core", version = "0.3.3" } rayon = "1.8.0" cyfrin-foundry-compilers = { version = "0.3.20-aderyn", features = ["svm-solc"] } serde_json = { version = "1.0.96", features = ["preserve_order"] } diff --git a/aderyn_py/Cargo.toml b/aderyn_py/Cargo.toml index 2e735a3a8..640b2194a 100644 --- a/aderyn_py/Cargo.toml +++ b/aderyn_py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aderyn_py" -version = "0.3.2" +version = "0.3.3" edition = "2021" authors = ["Cyfrin "] description = "Rust based Solidity AST analyzer python bindings" @@ -14,7 +14,7 @@ name = "aderynpy" crate-type = ["cdylib"] [dependencies] -aderyn_driver = { path = "../aderyn_driver", version = "0.3.2" } +aderyn_driver = { path = "../aderyn_driver", version = "0.3.3" } field_access = "0.1.8" [dependencies.pyo3] diff --git a/reports/report.sarif b/reports/report.sarif index 01d2a49a1..cf4385093 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -12326,8 +12326,8 @@ "informationUri": "https://github.com/Cyfrin/aderyn", "name": "Aderyn", "organization": "Cyfrin", - "semanticVersion": "0.3.2", - "version": "0.3.2" + "semanticVersion": "0.3.3", + "version": "0.3.3" } } } From 47792bf931f8c8fb96c80d7037ba4aee1d9b39bc Mon Sep 17 00:00:00 2001 From: mgiagante <5287175+mgiagante@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:19:54 +0100 Subject: [PATCH 13/25] Implements `init` subcommand with optional path for aderyn.toml (#762) Co-authored-by: mgiagante <251503-mgiagante@users.noreply.gitlab.com> Co-authored-by: TilakMaddy --- aderyn/src/lib.rs | 7 +++--- aderyn/src/main.rs | 56 +++++++++++++++++++++++++++++----------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/aderyn/src/lib.rs b/aderyn/src/lib.rs index 3abe1cbc4..2ee753a5b 100644 --- a/aderyn/src/lib.rs +++ b/aderyn/src/lib.rs @@ -4,6 +4,9 @@ use serde_json::Value; use std::{fs::File, io::Write, path::PathBuf, str::FromStr}; use strum::IntoEnumIterator; +pub mod lsp; +mod panic; + pub fn create_aderyn_toml_file_at(directory: String) { let aderyn_toml_path = PathBuf::from_str(&directory).unwrap().join("aderyn.toml"); let mut file = File::create_new(aderyn_toml_path.clone()).expect("File already exists!"); @@ -12,15 +15,11 @@ pub fn create_aderyn_toml_file_at(directory: String) { println!("Created aderyn.toml at {}", aderyn_toml_path.display()); } -mod panic; - pub fn initialize_niceties() { // Crash with a nice message on panic panic::add_handler() } -pub mod lsp; - pub fn print_detail_view(detector_name: &str) { let all_detector_names = get_all_detectors_names(); if !all_detector_names.contains(&detector_name.to_string()) { diff --git a/aderyn/src/main.rs b/aderyn/src/main.rs index 8c1a55df3..f0b857aaf 100644 --- a/aderyn/src/main.rs +++ b/aderyn/src/main.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use aderyn::{ aderyn_is_currently_running_newest_version, create_aderyn_toml_file_at, initialize_niceties, lsp::spin_up_language_server, print_all_detectors_view, print_detail_view, @@ -9,18 +11,14 @@ use clap::{ArgGroup, Parser, Subcommand}; #[command(author, version, about, long_about = None)] #[command(group(ArgGroup::new("stdout_dependent").requires("stdout")))] pub struct CommandLineArgs { - /// Print every detector available - #[clap(subcommand, name = "registry")] - registry: Option, + /// Commands to initialize a config file for aderyn [BETA] and other utils + #[clap(subcommand)] + subcommand: Option, /// Foundry or Hardhat project root directory (or path to single solidity file) #[arg(default_value = ".")] root: String, - /// Initialize aderyn.toml in [ROOT] which hosts all the configuration to override defaults - #[arg(long)] - init: bool, - /// Path to the source contracts. If not provided, the ROOT directory will be used. /// /// For example, in a foundry repo: @@ -77,7 +75,7 @@ pub struct CommandLineArgs { } #[derive(Debug, Subcommand)] -enum RegistryCommand { +enum Command { /// Browse detector registry Registry { /// all - View all available detectors @@ -86,32 +84,50 @@ enum RegistryCommand { #[arg(default_value = "all")] detector: String, }, + /// Initialize aderyn.toml in the root directory or in an optional subdirectory + Init { + /// Optional path inside root where aderyn.toml will be created + path: Option, + }, } fn main() { initialize_niceties(); let cmd_args = CommandLineArgs::parse(); - if let Some(reg) = cmd_args.registry { - match reg { - RegistryCommand::Registry { detector } => { + if let Some(subcommand) = cmd_args.subcommand { + match subcommand { + Command::Registry { detector } => { if detector == "all" { print_all_detectors_view(); } else { print_detail_view(&detector); } } + Command::Init { path } => { + let creation_path = match path { + Some(optional_path) => { + let mut target_dir = PathBuf::from(&cmd_args.root); + target_dir.push(optional_path); + + let can_initialize = target_dir.exists() + && std::fs::metadata(&target_dir).is_ok_and(|p| p.is_dir()); + + if !can_initialize { + eprintln!("Failed to initialize aderyn.toml in non-existent directory"); + std::process::exit(1); + } + + target_dir.to_string_lossy().to_string() + } + None => cmd_args.root, + }; + + // Create aderyn.toml at the target directory + create_aderyn_toml_file_at(creation_path); + } } - return; - } - - if cmd_args.root == "init" { - create_aderyn_toml_file_at(".".to_string()); - return; - } - if cmd_args.init { - create_aderyn_toml_file_at(cmd_args.root); return; } From 86100ab2ee383f790423b93b70deb37c2ac62165 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Thu, 17 Oct 2024 13:04:06 +0530 Subject: [PATCH 14/25] Create funding.json (#765) --- funding.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 funding.json diff --git a/funding.json b/funding.json new file mode 100644 index 000000000..b9f925839 --- /dev/null +++ b/funding.json @@ -0,0 +1,5 @@ +{ + "opRetro": { + "projectId": "0xa7d78d566bfa319479ec048c94c3d8c1f4d628a9344ba157fc4974dbf472dc3e" + } +} From f872928bb49b8391a0aec70b94ca5c9ee1590bbf Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 12:28:40 +0530 Subject: [PATCH 15/25] Lower the severity of unchecked return detector (#766) --- aderyn_core/src/detect/detector.rs | 2 +- aderyn_core/src/detect/high/mod.rs | 2 - aderyn_core/src/detect/low/mod.rs | 2 + .../detect/{high => low}/unchecked_return.rs | 2 +- .../adhoc-sol-files-highs-only-report.json | 1 - reports/report.json | 142 +++++------ reports/report.md | 212 ++++++++-------- reports/report.sarif | 238 +++++++++--------- reports/templegold-report.md | 196 +++++++-------- 9 files changed, 398 insertions(+), 399 deletions(-) rename aderyn_core/src/detect/{high => low}/unchecked_return.rs (99%) diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index 40d79390f..d97fabd6c 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -64,7 +64,6 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), - Box::::default(), Box::::default(), Box::::default(), Box::::default(), @@ -103,6 +102,7 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), ] } diff --git a/aderyn_core/src/detect/high/mod.rs b/aderyn_core/src/detect/high/mod.rs index a38523098..6cfc66103 100644 --- a/aderyn_core/src/detect/high/mod.rs +++ b/aderyn_core/src/detect/high/mod.rs @@ -33,7 +33,6 @@ pub(crate) mod tautological_compare; pub(crate) mod tautology_or_contradiction; pub(crate) mod tx_origin_used_for_auth; pub(crate) mod unchecked_calls; -pub(crate) mod unchecked_return; pub(crate) mod unchecked_send; pub(crate) mod uninitialized_state_variable; pub(crate) mod unprotected_init_function; @@ -76,7 +75,6 @@ pub use tautological_compare::TautologicalCompareDetector; pub use tautology_or_contradiction::TautologyOrContraditionDetector; pub use tx_origin_used_for_auth::TxOriginUsedForAuthDetector; pub use unchecked_calls::UncheckedLowLevelCallDetector; -pub use unchecked_return::UncheckedReturnDetector; pub use unchecked_send::UncheckedSendDetector; pub use uninitialized_state_variable::UninitializedStateVariableDetector; pub use unprotected_init_function::UnprotectedInitializerDetector; diff --git a/aderyn_core/src/detect/low/mod.rs b/aderyn_core/src/detect/low/mod.rs index 4091db088..3b4706329 100644 --- a/aderyn_core/src/detect/low/mod.rs +++ b/aderyn_core/src/detect/low/mod.rs @@ -30,6 +30,7 @@ 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 unchecked_return; pub(crate) mod unindexed_events; pub(crate) mod uninitialized_local_variables; pub(crate) mod unsafe_erc20_functions; @@ -76,6 +77,7 @@ 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 unchecked_return::UncheckedReturnDetector; pub use unindexed_events::UnindexedEventsDetector; pub use uninitialized_local_variables::UninitializedLocalVariableDetector; pub use unsafe_erc20_functions::UnsafeERC20FunctionsDetector; diff --git a/aderyn_core/src/detect/high/unchecked_return.rs b/aderyn_core/src/detect/low/unchecked_return.rs similarity index 99% rename from aderyn_core/src/detect/high/unchecked_return.rs rename to aderyn_core/src/detect/low/unchecked_return.rs index aa848c64f..cf8464db5 100644 --- a/aderyn_core/src/detect/high/unchecked_return.rs +++ b/aderyn_core/src/detect/low/unchecked_return.rs @@ -58,7 +58,7 @@ impl IssueDetector for UncheckedReturnDetector { } fn severity(&self) -> IssueSeverity { - IssueSeverity::High + IssueSeverity::Low } fn title(&self) -> String { diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index b9c9e004f..7664c684f 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -200,7 +200,6 @@ "delegate-call-on-unchecked-address", "tautological-compare", "rtlo", - "unchecked-return", "dangerous-unary-operator", "tautology-or-contradiction", "strict-equailty-check-on-contract-balance", diff --git a/reports/report.json b/reports/report.json index ffb67e4f7..f37b6376e 100644 --- a/reports/report.json +++ b/reports/report.json @@ -456,8 +456,8 @@ ] }, "issue_count": { - "high": 42, - "low": 45 + "high": 41, + "low": 46 }, "high_issues": { "issues": [ @@ -1992,73 +1992,6 @@ } ] }, - { - "title": "Return value of the function call is not checked.", - "description": "Function returns a value but it is ignored.", - "detector_name": "unchecked-return", - "instances": [ - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 65, - "src": "1705:390", - "src_char": "1705:390" - }, - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 77, - "src": "2129:379", - "src_char": "2129:379" - }, - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 92, - "src": "2624:390", - "src_char": "2624:390" - }, - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 107, - "src": "3107:379", - "src_char": "3107:379" - }, - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 129, - "src": "3777:208", - "src_char": "3777:208" - }, - { - "contract_path": "src/OutOfOrderRetryable.sol", - "line_no": 151, - "src": "4337:261", - "src_char": "4337:261" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 142, - "src": "4146:20", - "src_char": "4146:20" - }, - { - "contract_path": "src/Trump.sol", - "line_no": 344, - "src": "11990:71", - "src_char": "11990:71" - }, - { - "contract_path": "src/UncheckedReturn.sol", - "line_no": 14, - "src": "279:5", - "src_char": "279:5" - }, - { - "contract_path": "src/UncheckedReturn.sol", - "line_no": 27, - "src": "575:47", - "src_char": "575:47" - } - ] - }, { "title": "Dangerous unary operator found in assignment.", "description": "Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in between.", @@ -7360,6 +7293,73 @@ "src_char": "186:10" } ] + }, + { + "title": "Return value of the function call is not checked.", + "description": "Function returns a value but it is ignored.", + "detector_name": "unchecked-return", + "instances": [ + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 65, + "src": "1705:390", + "src_char": "1705:390" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 77, + "src": "2129:379", + "src_char": "2129:379" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 92, + "src": "2624:390", + "src_char": "2624:390" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 107, + "src": "3107:379", + "src_char": "3107:379" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 129, + "src": "3777:208", + "src_char": "3777:208" + }, + { + "contract_path": "src/OutOfOrderRetryable.sol", + "line_no": 151, + "src": "4337:261", + "src_char": "4337:261" + }, + { + "contract_path": "src/StateVariablesManipulation.sol", + "line_no": 142, + "src": "4146:20", + "src_char": "4146:20" + }, + { + "contract_path": "src/Trump.sol", + "line_no": 344, + "src": "11990:71", + "src_char": "11990:71" + }, + { + "contract_path": "src/UncheckedReturn.sol", + "line_no": 14, + "src": "279:5", + "src_char": "279:5" + }, + { + "contract_path": "src/UncheckedReturn.sol", + "line_no": 27, + "src": "575:47", + "src_char": "575:47" + } + ] } ] }, @@ -7412,7 +7412,6 @@ "delegate-call-on-unchecked-address", "tautological-compare", "rtlo", - "unchecked-return", "dangerous-unary-operator", "tautology-or-contradiction", "strict-equailty-check-on-contract-balance", @@ -7450,6 +7449,7 @@ "state-variable-could-be-constant", "state-variable-changes-without-events", "state-variable-could-be-immutable", - "multiple-placeholders" + "multiple-placeholders", + "unchecked-return" ] } \ No newline at end of file diff --git a/reports/report.md b/reports/report.md index c30290fb9..9159cb73f 100644 --- a/reports/report.md +++ b/reports/report.md @@ -33,23 +33,22 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [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) - - [H-27: Dangerous unary operator found in assignment.](#h-27-dangerous-unary-operator-found-in-assignment) - - [H-28: Tautology or Contradiction in comparison.](#h-28-tautology-or-contradiction-in-comparison) - - [H-29: Dangerous strict equality checks on contract balances.](#h-29-dangerous-strict-equality-checks-on-contract-balances) - - [H-30: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-30-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) - - [H-31: Weak Randomness](#h-31-weak-randomness) - - [H-32: Usage of variable before declaration.](#h-32-usage-of-variable-before-declaration) - - [H-33: Deletion from a nested mappping.](#h-33-deletion-from-a-nested-mappping) - - [H-34: Potential use of `tx.origin` for authentication.](#h-34-potential-use-of-txorigin-for-authentication) - - [H-35: Loop contains `msg.value`.](#h-35-loop-contains-msgvalue) - - [H-36: Contract locks Ether without a withdraw function.](#h-36-contract-locks-ether-without-a-withdraw-function) - - [H-37: Incorrect ERC721 interface.](#h-37-incorrect-erc721-interface) - - [H-38: Incorrect ERC20 interface.](#h-38-incorrect-erc20-interface) - - [H-39: Out of order retryable transactions.](#h-39-out-of-order-retryable-transactions) - - [H-40: Constant functions changing state](#h-40-constant-functions-changing-state) - - [H-41: Function selector collides with other functions](#h-41-function-selector-collides-with-other-functions) - - [H-42: Unchecked Low level calls](#h-42-unchecked-low-level-calls) + - [H-26: Dangerous unary operator found in assignment.](#h-26-dangerous-unary-operator-found-in-assignment) + - [H-27: Tautology or Contradiction in comparison.](#h-27-tautology-or-contradiction-in-comparison) + - [H-28: Dangerous strict equality checks on contract balances.](#h-28-dangerous-strict-equality-checks-on-contract-balances) + - [H-29: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-29-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) + - [H-30: Weak Randomness](#h-30-weak-randomness) + - [H-31: Usage of variable before declaration.](#h-31-usage-of-variable-before-declaration) + - [H-32: Deletion from a nested mappping.](#h-32-deletion-from-a-nested-mappping) + - [H-33: Potential use of `tx.origin` for authentication.](#h-33-potential-use-of-txorigin-for-authentication) + - [H-34: Loop contains `msg.value`.](#h-34-loop-contains-msgvalue) + - [H-35: Contract locks Ether without a withdraw function.](#h-35-contract-locks-ether-without-a-withdraw-function) + - [H-36: Incorrect ERC721 interface.](#h-36-incorrect-erc721-interface) + - [H-37: Incorrect ERC20 interface.](#h-37-incorrect-erc20-interface) + - [H-38: Out of order retryable transactions.](#h-38-out-of-order-retryable-transactions) + - [H-39: Constant functions changing state](#h-39-constant-functions-changing-state) + - [H-40: Function selector collides with other functions](#h-40-function-selector-collides-with-other-functions) + - [H-41: Unchecked Low level calls](#h-41-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: Solmate's SafeTransferLib does not check for token contract's existence](#l-2-solmates-safetransferlib-does-not-check-for-token-contracts-existence) @@ -96,6 +95,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-43: State variable changes but no event is emitted.](#l-43-state-variable-changes-but-no-event-is-emitted) - [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) + - [L-46: Return value of the function call is not checked.](#l-46-return-value-of-the-function-call-is-not-checked) # Summary @@ -231,8 +231,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 42 | -| Low | 45 | +| High | 41 | +| Low | 46 | # High Issues @@ -1870,78 +1870,7 @@ Right to left override character may be misledaing and cause potential attacks b -## H-26: Return value of the function call is not checked. - -Function returns a value but it is ignored. - -
10 Found Instances - - -- Found in src/OutOfOrderRetryable.sol [Line: 65](../tests/contract-playground/src/OutOfOrderRetryable.sol#L65) - - ```solidity - IInbox(inbox).createRetryableTicket({ - ``` - -- Found in src/OutOfOrderRetryable.sol [Line: 77](../tests/contract-playground/src/OutOfOrderRetryable.sol#L77) - - ```solidity - IInbox(inbox).createRetryableTicket({ - ``` - -- Found in src/OutOfOrderRetryable.sol [Line: 92](../tests/contract-playground/src/OutOfOrderRetryable.sol#L92) - - ```solidity - IInbox(inbox).createRetryableTicket({ - ``` - -- Found in src/OutOfOrderRetryable.sol [Line: 107](../tests/contract-playground/src/OutOfOrderRetryable.sol#L107) - - ```solidity - IInbox(inbox).createRetryableTicket({ - ``` - -- Found in src/OutOfOrderRetryable.sol [Line: 129](../tests/contract-playground/src/OutOfOrderRetryable.sol#L129) - - ```solidity - IInbox(inbox).outboundTransferCustomRefund( - ``` - -- Found in src/OutOfOrderRetryable.sol [Line: 151](../tests/contract-playground/src/OutOfOrderRetryable.sol#L151) - - ```solidity - IInbox(inbox).unsafeCreateRetryableTicket( - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 142](../tests/contract-playground/src/StateVariablesManipulation.sol#L142) - - ```solidity - manipulateHelper(p1); - ``` - -- Found in src/Trump.sol [Line: 344](../tests/contract-playground/src/Trump.sol#L344) - - ```solidity - IERC20(uniswapV2Pair).approve(address(uniswapV2Router), type(uint).max); - ``` - -- Found in src/UncheckedReturn.sol [Line: 14](../tests/contract-playground/src/UncheckedReturn.sol#L14) - - ```solidity - one(); - ``` - -- Found in src/UncheckedReturn.sol [Line: 27](../tests/contract-playground/src/UncheckedReturn.sol#L27) - - ```solidity - UncheckedHelperExternal(address(0x12345)).two(); - ``` - -
- - - -## H-27: Dangerous unary operator found in assignment. +## H-26: Dangerous unary operator found in assignment. Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in between. @@ -1964,7 +1893,7 @@ Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in -## H-28: Tautology or Contradiction in comparison. +## H-27: Tautology or Contradiction in comparison. The condition has been determined to be either always true or always false due to the integer range in which we're operating. @@ -1987,7 +1916,7 @@ The condition has been determined to be either always true or always false due t -## H-29: Dangerous strict equality checks on contract balances. +## H-28: Dangerous strict equality checks on contract balances. A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality. @@ -2016,7 +1945,7 @@ A contract's balance can be forcibly manipulated by another selfdestructing cont -## H-30: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` +## H-29: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` If you want to leverage signed arrays in storage by assigning a literal array with at least one negative number, then you mus use solidity version 0.5.10 or above. This is because of a bug in older compilers. @@ -2033,7 +1962,7 @@ If you want to leverage signed arrays in storage by assigning a literal array wi -## H-31: Weak Randomness +## H-30: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -2098,7 +2027,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-32: Usage of variable before declaration. +## H-31: Usage of variable before declaration. This is a bad practice that may lead to unintended consequences. Please declare the variable before using it. @@ -2115,7 +2044,7 @@ This is a bad practice that may lead to unintended consequences. Please declare -## H-33: Deletion from a nested mappping. +## H-32: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -2132,7 +2061,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-34: Potential use of `tx.origin` for authentication. +## H-33: Potential use of `tx.origin` for authentication. Using `tx.origin` may lead to problems when users are interacting via smart contract with your protocol. It is recommended to use `msg.sender` for authentication. @@ -2161,7 +2090,7 @@ Using `tx.origin` may lead to problems when users are interacting via smart cont -## H-35: Loop contains `msg.value`. +## H-34: Loop contains `msg.value`. Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. @@ -2196,7 +2125,7 @@ Provide an explicit array of amounts alongside the receivers array, and check th -## H-36: Contract locks Ether without a withdraw function. +## H-35: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. @@ -2267,7 +2196,7 @@ It appears that the contract includes a payable function to accept Ether but lac -## H-37: Incorrect ERC721 interface. +## H-36: Incorrect ERC721 interface. Incorrect return values for ERC721 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC721 functions. @@ -2326,7 +2255,7 @@ Incorrect return values for ERC721 functions. A contract compiled with Solidity -## H-38: Incorrect ERC20 interface. +## H-37: Incorrect ERC20 interface. Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions. @@ -2367,7 +2296,7 @@ Incorrect return values for ERC20 functions. A contract compiled with Solidity > -## H-39: Out of order retryable transactions. +## H-38: Out of order retryable transactions. Do not rely on the order or successful execution of retryable tickets. Functions like createRetryableTicket, outboundTransferCustomRefund, unsafeCreateRetryableTicket are free to be re-tried in any order if they fail in the first go. Since this operation happens off chain, the sequencer is in control of the @@ -2392,7 +2321,7 @@ Do not rely on the order or successful execution of retryable tickets. Functions -## H-40: Constant functions changing state +## H-39: Constant functions changing state Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct. @@ -2409,7 +2338,7 @@ Function is declared constant/view but it changes state. Ensure that the attribu -## H-41: Function selector collides with other functions +## H-40: Function selector collides with other functions Function selector collides with other functions. This may cause the solidity function dispatcher to invoke the wrong function if the functions happen to be included in the same contract through an inheritance hirearchy later down the line. It is recommended to rename this function or change its parameters. @@ -2434,7 +2363,7 @@ Function selector collides with other functions. This may cause the solidity fun -## H-42: Unchecked Low level calls +## H-41: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. @@ -7467,3 +7396,74 @@ Design the modifier to only contain 1 placeholder statement. If it's not possibl +## L-46: Return value of the function call is not checked. + +Function returns a value but it is ignored. + +
10 Found Instances + + +- Found in src/OutOfOrderRetryable.sol [Line: 65](../tests/contract-playground/src/OutOfOrderRetryable.sol#L65) + + ```solidity + IInbox(inbox).createRetryableTicket({ + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 77](../tests/contract-playground/src/OutOfOrderRetryable.sol#L77) + + ```solidity + IInbox(inbox).createRetryableTicket({ + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 92](../tests/contract-playground/src/OutOfOrderRetryable.sol#L92) + + ```solidity + IInbox(inbox).createRetryableTicket({ + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 107](../tests/contract-playground/src/OutOfOrderRetryable.sol#L107) + + ```solidity + IInbox(inbox).createRetryableTicket({ + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 129](../tests/contract-playground/src/OutOfOrderRetryable.sol#L129) + + ```solidity + IInbox(inbox).outboundTransferCustomRefund( + ``` + +- Found in src/OutOfOrderRetryable.sol [Line: 151](../tests/contract-playground/src/OutOfOrderRetryable.sol#L151) + + ```solidity + IInbox(inbox).unsafeCreateRetryableTicket( + ``` + +- Found in src/StateVariablesManipulation.sol [Line: 142](../tests/contract-playground/src/StateVariablesManipulation.sol#L142) + + ```solidity + manipulateHelper(p1); + ``` + +- Found in src/Trump.sol [Line: 344](../tests/contract-playground/src/Trump.sol#L344) + + ```solidity + IERC20(uniswapV2Pair).approve(address(uniswapV2Router), type(uint).max); + ``` + +- Found in src/UncheckedReturn.sol [Line: 14](../tests/contract-playground/src/UncheckedReturn.sol#L14) + + ```solidity + one(); + ``` + +- Found in src/UncheckedReturn.sol [Line: 27](../tests/contract-playground/src/UncheckedReturn.sol#L27) + + ```solidity + UncheckedHelperExternal(address(0x12345)).two(); + ``` + +
+ + + diff --git a/reports/report.sarif b/reports/report.sarif index cf4385093..66210539f 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -2715,125 +2715,6 @@ }, "ruleId": "rtlo" }, - { - "level": "warning", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 390, - "byteOffset": 1705 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 379, - "byteOffset": 2129 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 390, - "byteOffset": 2624 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 379, - "byteOffset": 3107 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 208, - "byteOffset": 3777 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/OutOfOrderRetryable.sol" - }, - "region": { - "byteLength": 261, - "byteOffset": 4337 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 20, - "byteOffset": 4146 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/Trump.sol" - }, - "region": { - "byteLength": 71, - "byteOffset": 11990 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UncheckedReturn.sol" - }, - "region": { - "byteLength": 5, - "byteOffset": 279 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UncheckedReturn.sol" - }, - "region": { - "byteLength": 47, - "byteOffset": 575 - } - } - } - ], - "message": { - "text": "Function returns a value but it is ignored." - }, - "ruleId": "unchecked-return" - }, { "level": "warning", "locations": [ @@ -12318,6 +12199,125 @@ "text": "Design the modifier to only contain 1 placeholder statement. If it's not possible, split the logic into multiple modifiers." }, "ruleId": "multiple-placeholders" + }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 390, + "byteOffset": 1705 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 379, + "byteOffset": 2129 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 390, + "byteOffset": 2624 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 379, + "byteOffset": 3107 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 208, + "byteOffset": 3777 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/OutOfOrderRetryable.sol" + }, + "region": { + "byteLength": 261, + "byteOffset": 4337 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/StateVariablesManipulation.sol" + }, + "region": { + "byteLength": 20, + "byteOffset": 4146 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/Trump.sol" + }, + "region": { + "byteLength": 71, + "byteOffset": 11990 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UncheckedReturn.sol" + }, + "region": { + "byteLength": 5, + "byteOffset": 279 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UncheckedReturn.sol" + }, + "region": { + "byteLength": 47, + "byteOffset": 575 + } + } + } + ], + "message": { + "text": "Function returns a value but it is ignored." + }, + "ruleId": "unchecked-return" } ], "tool": { diff --git a/reports/templegold-report.md b/reports/templegold-report.md index 0a6db60f6..dbdc68318 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -13,10 +13,9 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-3: Unsafe Casting](#h-3-unsafe-casting) - [H-4: Contract Name Reused in Different Files](#h-4-contract-name-reused-in-different-files) - [H-5: Uninitialized State Variables](#h-5-uninitialized-state-variables) - - [H-6: Return value of the function call is not checked.](#h-6-return-value-of-the-function-call-is-not-checked) - - [H-7: Weak Randomness](#h-7-weak-randomness) - - [H-8: Deletion from a nested mappping.](#h-8-deletion-from-a-nested-mappping) - - [H-9: Contract locks Ether without a withdraw function.](#h-9-contract-locks-ether-without-a-withdraw-function) + - [H-6: Weak Randomness](#h-6-weak-randomness) + - [H-7: Deletion from a nested mappping.](#h-7-deletion-from-a-nested-mappping) + - [H-8: Contract locks Ether without a withdraw function.](#h-8-contract-locks-ether-without-a-withdraw-function) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -45,6 +44,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [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) + - [L-28: Return value of the function call is not checked.](#l-28-return-value-of-the-function-call-is-not-checked) # Summary @@ -197,8 +197,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 9 | -| Low | 27 | +| High | 8 | +| Low | 28 | # High Issues @@ -390,96 +390,7 @@ Solidity does initialize variables by default when you declare them, however it' -## H-6: Return value of the function call is not checked. - -Function returns a value but it is ignored. - -
13 Found Instances - - -- Found in contracts/amo/AuraStaking.sol [Line: 83](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L83) - - ```solidity - booster.deposit(auraPoolInfo.pId, amount, true); - ``` - -- Found in contracts/amo/AuraStaking.sol [Line: 93](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L93) - - ```solidity - IAuraBaseRewardPool(auraPoolInfo.rewards).withdrawAndUnwrap(toUnstake, claim); - ``` - -- Found in contracts/amo/AuraStaking.sol [Line: 111](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L111) - - ```solidity - IAuraBaseRewardPool(auraPoolInfo.rewards).getReward(address(this), claimExtras); - ``` - -- Found in contracts/fakes/v2/TempleDebtTokenTestnetAdmin.sol [Line: 28](../tests/2024-07-templegold/protocol/contracts/fakes/v2/TempleDebtTokenTestnetAdmin.sol#L28) - - ```solidity - dUSD.burn(from, amount); - ``` - -- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 52](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L52) - - ```solidity - _checkpointDaiBalance(daiToken.balanceOf(address(this))); - ``` - -- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 131](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L131) - - ```solidity - _checkpointDaiBalance(daiToken.balanceOf(address(this))); - ``` - -- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 181](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L181) - - ```solidity - _checkpointDaiBalance(balance - amount); - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 149](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L149) - - ```solidity - _getBaseCache(); - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 160](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L160) - - ```solidity - _getDebtorCache(_getBaseCache(), debtor, true); - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 638](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L638) - - ```solidity - _initBaseCache(baseCache); - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 752](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L752) - - ```solidity - _initDebtorCache(_baseCache, _debtor, debtorCache, _roundUp); - ``` - -- Found in contracts/v2/strategies/RamosStrategy.sol [Line: 252](../tests/2024-07-templegold/protocol/contracts/v2/strategies/RamosStrategy.sol#L252) - - ```solidity - ramos.removeLiquidity(params.requestData, params.bptAmount); - ``` - -- Found in contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol [Line: 680](../tests/2024-07-templegold/protocol/contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol#L680) - - ```solidity - _initDebtTokenCache(cache); - ``` - -
- - - -## H-7: Weak Randomness +## H-6: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -496,7 +407,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-8: Deletion from a nested mappping. +## H-7: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -513,7 +424,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-9: Contract locks Ether without a withdraw function. +## H-8: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. @@ -9044,3 +8955,92 @@ State variables that are should be declared immutable to save gas. Add the `immu +## L-28: Return value of the function call is not checked. + +Function returns a value but it is ignored. + +
13 Found Instances + + +- Found in contracts/amo/AuraStaking.sol [Line: 83](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L83) + + ```solidity + booster.deposit(auraPoolInfo.pId, amount, true); + ``` + +- Found in contracts/amo/AuraStaking.sol [Line: 93](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L93) + + ```solidity + IAuraBaseRewardPool(auraPoolInfo.rewards).withdrawAndUnwrap(toUnstake, claim); + ``` + +- Found in contracts/amo/AuraStaking.sol [Line: 111](../tests/2024-07-templegold/protocol/contracts/amo/AuraStaking.sol#L111) + + ```solidity + IAuraBaseRewardPool(auraPoolInfo.rewards).getReward(address(this), claimExtras); + ``` + +- Found in contracts/fakes/v2/TempleDebtTokenTestnetAdmin.sol [Line: 28](../tests/2024-07-templegold/protocol/contracts/fakes/v2/TempleDebtTokenTestnetAdmin.sol#L28) + + ```solidity + dUSD.burn(from, amount); + ``` + +- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 52](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L52) + + ```solidity + _checkpointDaiBalance(daiToken.balanceOf(address(this))); + ``` + +- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 131](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L131) + + ```solidity + _checkpointDaiBalance(daiToken.balanceOf(address(this))); + ``` + +- Found in contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol [Line: 181](../tests/2024-07-templegold/protocol/contracts/fakes/v2/strategies/DsrBaseStrategyTestnet.sol#L181) + + ```solidity + _checkpointDaiBalance(balance - amount); + ``` + +- Found in contracts/v2/TempleDebtToken.sol [Line: 149](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L149) + + ```solidity + _getBaseCache(); + ``` + +- Found in contracts/v2/TempleDebtToken.sol [Line: 160](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L160) + + ```solidity + _getDebtorCache(_getBaseCache(), debtor, true); + ``` + +- Found in contracts/v2/TempleDebtToken.sol [Line: 638](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L638) + + ```solidity + _initBaseCache(baseCache); + ``` + +- Found in contracts/v2/TempleDebtToken.sol [Line: 752](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L752) + + ```solidity + _initDebtorCache(_baseCache, _debtor, debtorCache, _roundUp); + ``` + +- Found in contracts/v2/strategies/RamosStrategy.sol [Line: 252](../tests/2024-07-templegold/protocol/contracts/v2/strategies/RamosStrategy.sol#L252) + + ```solidity + ramos.removeLiquidity(params.requestData, params.bptAmount); + ``` + +- Found in contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol [Line: 680](../tests/2024-07-templegold/protocol/contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol#L680) + + ```solidity + _initDebtTokenCache(cache); + ``` + +
+ + + From fb9b73d28625e9987c272fb44c69ce663bf04f39 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 12:46:22 +0530 Subject: [PATCH 16/25] Fix unchecked return detector test (#768) --- aderyn_core/src/detect/low/unchecked_return.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aderyn_core/src/detect/low/unchecked_return.rs b/aderyn_core/src/detect/low/unchecked_return.rs index cf8464db5..cfdf90da8 100644 --- a/aderyn_core/src/detect/low/unchecked_return.rs +++ b/aderyn_core/src/detect/low/unchecked_return.rs @@ -82,7 +82,7 @@ impl IssueDetector for UncheckedReturnDetector { mod unchecked_return_tests { use serial_test::serial; - use crate::detect::{detector::IssueDetector, high::unchecked_return::UncheckedReturnDetector}; + use crate::detect::{detector::IssueDetector, low::unchecked_return::UncheckedReturnDetector}; #[test] #[serial] @@ -101,7 +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::Low); // assert the title is correct assert_eq!( detector.title(), From 6aae4b4ff423f1db25f3c403d9724f95cafaddcd Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 13:16:06 +0530 Subject: [PATCH 17/25] Spike: Adjust for new lint rules introduced by clippy (#769) --- aderyn/src/panic.rs | 8 +++++--- aderyn_core/src/ast/macros.rs | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/aderyn/src/panic.rs b/aderyn/src/panic.rs index ae9792f7a..6035f7279 100644 --- a/aderyn/src/panic.rs +++ b/aderyn/src/panic.rs @@ -1,5 +1,5 @@ #![allow(clippy::unwrap_used)] -use std::{io::Write, panic::PanicInfo}; +use std::{io::Write, panic::PanicHookInfo}; use termcolor::{Color, ColorSpec, WriteColor}; use std::io::IsTerminal; @@ -24,10 +24,12 @@ 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: &PanicHookInfo<'_>| { + print_compiler_bug_message(info) + })); } -fn print_compiler_bug_message(info: &PanicInfo<'_>) { +fn print_compiler_bug_message(info: &PanicHookInfo<'_>) { let message = match (info.payload().downcast_ref::<&str>(), info.payload().downcast_ref::()) { (Some(s), _) => (*s).to_string(), diff --git a/aderyn_core/src/ast/macros.rs b/aderyn_core/src/ast/macros.rs index b889b190b..ef5d7479b 100644 --- a/aderyn_core/src/ast/macros.rs +++ b/aderyn_core/src/ast/macros.rs @@ -89,6 +89,7 @@ macro_rules! node_group { ($group:ident; $( $name:ident ),* $(,)*) => { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] #[serde(tag = "nodeType")] + #[allow(clippy::large_enum_variant)] pub enum $group { $( $name($name), From 7331fe210974bb52576ac9897263d4267d711e1d Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 13:41:32 +0530 Subject: [PATCH 18/25] Lower severity of Block.Timestamp Deadline detector (#767) --- aderyn_core/src/detect/high/mod.rs | 2 - .../{high => low}/block_timestamp_deadline.rs | 8 +- aderyn_core/src/detect/low/mod.rs | 2 + .../adhoc-sol-files-highs-only-report.json | 1 - reports/report.json | 222 ++++---- reports/report.md | 520 +++++++++--------- reports/report.sarif | 392 ++++++------- reports/uniswap_profile.md | 55 +- 8 files changed, 599 insertions(+), 603 deletions(-) rename aderyn_core/src/detect/{high => low}/block_timestamp_deadline.rs (98%) diff --git a/aderyn_core/src/detect/high/mod.rs b/aderyn_core/src/detect/high/mod.rs index 6cfc66103..757cc4132 100644 --- a/aderyn_core/src/detect/high/mod.rs +++ b/aderyn_core/src/detect/high/mod.rs @@ -1,6 +1,5 @@ pub(crate) mod arbitrary_transfer_from; pub(crate) mod avoid_abi_encode_packed; -pub(crate) mod block_timestamp_deadline; pub(crate) mod const_func_change_state; pub(crate) mod contract_locks_ether; pub(crate) mod dangerous_strict_equality_balance; @@ -42,7 +41,6 @@ pub(crate) mod yul_return; pub use arbitrary_transfer_from::ArbitraryTransferFromDetector; pub use avoid_abi_encode_packed::AvoidAbiEncodePackedDetector; -pub use block_timestamp_deadline::BlockTimestampDeadlineDetector; pub use const_func_change_state::ConstantFunctionChangingStateDetector; pub use contract_locks_ether::ContractLocksEtherDetector; pub use dangerous_strict_equality_balance::DangerousStrictEqualityOnBalanceDetector; diff --git a/aderyn_core/src/detect/high/block_timestamp_deadline.rs b/aderyn_core/src/detect/low/block_timestamp_deadline.rs similarity index 98% rename from aderyn_core/src/detect/high/block_timestamp_deadline.rs rename to aderyn_core/src/detect/low/block_timestamp_deadline.rs index f824ae660..6fafbaa39 100644 --- a/aderyn_core/src/detect/high/block_timestamp_deadline.rs +++ b/aderyn_core/src/detect/low/block_timestamp_deadline.rs @@ -96,7 +96,7 @@ impl IssueDetector for BlockTimestampDeadlineDetector { } fn severity(&self) -> IssueSeverity { - IssueSeverity::High + IssueSeverity::Low } fn title(&self) -> String { @@ -121,7 +121,7 @@ impl IssueDetector for BlockTimestampDeadlineDetector { mod block_timestamp_deadline_detector_tests { use serial_test::serial; - use crate::detect::{detector::IssueDetector, high::BlockTimestampDeadlineDetector}; + use crate::detect::{detector::IssueDetector, low::BlockTimestampDeadlineDetector}; #[test] #[serial] @@ -137,7 +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::Low); // assert that the title is correct assert_eq!( detector.title(), @@ -167,7 +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::Low); // assert that the title is correct assert_eq!( detector.title(), diff --git a/aderyn_core/src/detect/low/mod.rs b/aderyn_core/src/detect/low/mod.rs index 3b4706329..0304305a7 100644 --- a/aderyn_core/src/detect/low/mod.rs +++ b/aderyn_core/src/detect/low/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod assert_state_change; +pub(crate) mod block_timestamp_deadline; pub(crate) mod boolean_equality; pub(crate) mod builtin_symbol_shadowing; pub(crate) mod cache_array_length; @@ -46,6 +47,7 @@ pub(crate) mod void_constructor; pub(crate) mod zero_address_check; pub use assert_state_change::AssertStateChangeDetector; +pub use block_timestamp_deadline::BlockTimestampDeadlineDetector; pub use boolean_equality::BooleanEqualityDetector; pub use builtin_symbol_shadowing::BuiltinSymbolShadowDetector; pub use cache_array_length::CacheArrayLengthDetector; diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 7664c684f..c38712b23 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -177,7 +177,6 @@ "detectors_used": [ "delegate-call-in-loop", "hash-collision-due-to-abi-encode-packed", - "block-timestamp-is-weak-deadline", "arbitrary-transfer-from", "unprotected-initializer", "unsafe-casting-detector", diff --git a/reports/report.json b/reports/report.json index f37b6376e..cdc663f8d 100644 --- a/reports/report.json +++ b/reports/report.json @@ -456,8 +456,8 @@ ] }, "issue_count": { - "high": 41, - "low": 46 + "high": 40, + "low": 47 }, "high_issues": { "issues": [ @@ -499,115 +499,6 @@ } ] }, - { - "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-is-weak-deadline", - "instances": [ - { - "contract_path": "src/Trump.sol", - "line_no": 290, - "src": "9949:190", - "src_char": "9949:190" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 23, - "src": "670:83", - "src_char": "670:83" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 24, - "src": "763:83", - "src_char": "763:83" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 25, - "src": "856:70", - "src_char": "856:70" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 26, - "src": "936:80", - "src_char": "936:80" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 27, - "src": "1026:80", - "src_char": "1026:80" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 31, - "src": "1278:112", - "src_char": "1278:112" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 32, - "src": "1400:99", - "src_char": "1400:99" - }, - { - "contract_path": "src/uniswap/UniswapV2Swapper.sol", - "line_no": 33, - "src": "1509:109", - "src_char": "1509:109" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 52, - "src": "1115:143", - "src_char": "1115:143" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 55, - "src": "1293:321", - "src_char": "1293:321" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 66, - "src": "1668:131", - "src_char": "1668:131" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 69, - "src": "1828:236", - "src_char": "1828:236" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 77, - "src": "2132:144", - "src_char": "2132:144" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 80, - "src": "2312:322", - "src_char": "2312:322" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 91, - "src": "2690:132", - "src_char": "2690:132" - }, - { - "contract_path": "src/uniswap/UniswapV3Swapper.sol", - "line_no": 94, - "src": "2852:237", - "src_char": "2852:237" - } - ] - }, { "title": "Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)", "description": "Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. ", @@ -4252,6 +4143,115 @@ } ] }, + { + "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-is-weak-deadline", + "instances": [ + { + "contract_path": "src/Trump.sol", + "line_no": 290, + "src": "9949:190", + "src_char": "9949:190" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 23, + "src": "670:83", + "src_char": "670:83" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 24, + "src": "763:83", + "src_char": "763:83" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 25, + "src": "856:70", + "src_char": "856:70" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 26, + "src": "936:80", + "src_char": "936:80" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 27, + "src": "1026:80", + "src_char": "1026:80" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 31, + "src": "1278:112", + "src_char": "1278:112" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 32, + "src": "1400:99", + "src_char": "1400:99" + }, + { + "contract_path": "src/uniswap/UniswapV2Swapper.sol", + "line_no": 33, + "src": "1509:109", + "src_char": "1509:109" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 52, + "src": "1115:143", + "src_char": "1115:143" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 55, + "src": "1293:321", + "src_char": "1293:321" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 66, + "src": "1668:131", + "src_char": "1668:131" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 69, + "src": "1828:236", + "src_char": "1828:236" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 77, + "src": "2132:144", + "src_char": "2132:144" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 80, + "src": "2312:322", + "src_char": "2312:322" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 91, + "src": "2690:132", + "src_char": "2690:132" + }, + { + "contract_path": "src/uniswap/UniswapV3Swapper.sol", + "line_no": 94, + "src": "2852:237", + "src_char": "2852:237" + } + ] + }, { "title": "Using `ERC721::_mint()` can be dangerous", "description": "Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support ERC721 tokens. Use `_safeMint()` instead of `_mint()` for ERC721.", diff --git a/reports/report.md b/reports/report.md index 9159cb73f..f44c2a82a 100644 --- a/reports/report.md +++ b/reports/report.md @@ -10,45 +10,44 @@ 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: `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: Using `block.timestamp` for swap deadline offers no protection](#h-3-using-blocktimestamp-for-swap-deadline-offers-no-protection) - - [H-4: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-4-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) - - [H-5: Unprotected initializer](#h-5-unprotected-initializer) - - [H-6: Unsafe Casting](#h-6-unsafe-casting) - - [H-7: EnumerableSet.remove in loop corrupts the set order.](#h-7-enumerablesetremove-in-loop-corrupts-the-set-order) - - [H-8: Experimental ABI Encoder](#h-8-experimental-abi-encoder) - - [H-9: Incorrect Assembly Shift Parameter Order](#h-9-incorrect-assembly-shift-parameter-order) - - [H-10: Storage Array Edited with Memory](#h-10-storage-array-edited-with-memory) - - [H-11: Contract Has Multiple Constructors](#h-11-contract-has-multiple-constructors) - - [H-12: Contract Name Reused in Different Files](#h-12-contract-name-reused-in-different-files) - - [H-13: Nested Structs in Mappings pre-0.5.0](#h-13-nested-structs-in-mappings-pre-050) - - [H-14: Depracated EVM Instruction for `selfdestruct` should not be used.](#h-14-depracated-evm-instruction-for-selfdestruct-should-not-be-used) - - [H-15: Array length value has a direct assignment.](#h-15-array-length-value-has-a-direct-assignment) - - [H-16: Uninitialized State Variables](#h-16-uninitialized-state-variables) - - [H-17: Incorrect use of caret operator on a non hexadcimal constant](#h-17-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) - - [H-18: Yul block contains `return` function call.](#h-18-yul-block-contains-return-function-call) - - [H-19: Shadowed State Variables in Inheritance Hierarchy](#h-19-shadowed-state-variables-in-inheritance-hierarchy) - - [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 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: Dangerous unary operator found in assignment.](#h-26-dangerous-unary-operator-found-in-assignment) - - [H-27: Tautology or Contradiction in comparison.](#h-27-tautology-or-contradiction-in-comparison) - - [H-28: Dangerous strict equality checks on contract balances.](#h-28-dangerous-strict-equality-checks-on-contract-balances) - - [H-29: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-29-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) - - [H-30: Weak Randomness](#h-30-weak-randomness) - - [H-31: Usage of variable before declaration.](#h-31-usage-of-variable-before-declaration) - - [H-32: Deletion from a nested mappping.](#h-32-deletion-from-a-nested-mappping) - - [H-33: Potential use of `tx.origin` for authentication.](#h-33-potential-use-of-txorigin-for-authentication) - - [H-34: Loop contains `msg.value`.](#h-34-loop-contains-msgvalue) - - [H-35: Contract locks Ether without a withdraw function.](#h-35-contract-locks-ether-without-a-withdraw-function) - - [H-36: Incorrect ERC721 interface.](#h-36-incorrect-erc721-interface) - - [H-37: Incorrect ERC20 interface.](#h-37-incorrect-erc20-interface) - - [H-38: Out of order retryable transactions.](#h-38-out-of-order-retryable-transactions) - - [H-39: Constant functions changing state](#h-39-constant-functions-changing-state) - - [H-40: Function selector collides with other functions](#h-40-function-selector-collides-with-other-functions) - - [H-41: Unchecked Low level calls](#h-41-unchecked-low-level-calls) + - [H-3: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-3-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) + - [H-4: Unprotected initializer](#h-4-unprotected-initializer) + - [H-5: Unsafe Casting](#h-5-unsafe-casting) + - [H-6: EnumerableSet.remove in loop corrupts the set order.](#h-6-enumerablesetremove-in-loop-corrupts-the-set-order) + - [H-7: Experimental ABI Encoder](#h-7-experimental-abi-encoder) + - [H-8: Incorrect Assembly Shift Parameter Order](#h-8-incorrect-assembly-shift-parameter-order) + - [H-9: Storage Array Edited with Memory](#h-9-storage-array-edited-with-memory) + - [H-10: Contract Has Multiple Constructors](#h-10-contract-has-multiple-constructors) + - [H-11: Contract Name Reused in Different Files](#h-11-contract-name-reused-in-different-files) + - [H-12: Nested Structs in Mappings pre-0.5.0](#h-12-nested-structs-in-mappings-pre-050) + - [H-13: Depracated EVM Instruction for `selfdestruct` should not be used.](#h-13-depracated-evm-instruction-for-selfdestruct-should-not-be-used) + - [H-14: Array length value has a direct assignment.](#h-14-array-length-value-has-a-direct-assignment) + - [H-15: Uninitialized State Variables](#h-15-uninitialized-state-variables) + - [H-16: Incorrect use of caret operator on a non hexadcimal constant](#h-16-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) + - [H-17: Yul block contains `return` function call.](#h-17-yul-block-contains-return-function-call) + - [H-18: Shadowed State Variables in Inheritance Hierarchy](#h-18-shadowed-state-variables-in-inheritance-hierarchy) + - [H-19: Unchecked `bool success` value for send call.](#h-19-unchecked-bool-success-value-for-send-call) + - [H-20: Misused boolean with logical operators](#h-20-misused-boolean-with-logical-operators) + - [H-21: Functions send eth away from contract but performs no checks on any address.](#h-21-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) + - [H-22: Delegatecall made by the function without checks on any address.](#h-22-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-23: Tautological comparison.](#h-23-tautological-comparison) + - [H-24: RTLO character detected in file. \u{202e}](#h-24-rtlo-character-detected-in-file-u202e) + - [H-25: Dangerous unary operator found in assignment.](#h-25-dangerous-unary-operator-found-in-assignment) + - [H-26: Tautology or Contradiction in comparison.](#h-26-tautology-or-contradiction-in-comparison) + - [H-27: Dangerous strict equality checks on contract balances.](#h-27-dangerous-strict-equality-checks-on-contract-balances) + - [H-28: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-28-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) + - [H-29: Weak Randomness](#h-29-weak-randomness) + - [H-30: Usage of variable before declaration.](#h-30-usage-of-variable-before-declaration) + - [H-31: Deletion from a nested mappping.](#h-31-deletion-from-a-nested-mappping) + - [H-32: Potential use of `tx.origin` for authentication.](#h-32-potential-use-of-txorigin-for-authentication) + - [H-33: Loop contains `msg.value`.](#h-33-loop-contains-msgvalue) + - [H-34: Contract locks Ether without a withdraw function.](#h-34-contract-locks-ether-without-a-withdraw-function) + - [H-35: Incorrect ERC721 interface.](#h-35-incorrect-erc721-interface) + - [H-36: Incorrect ERC20 interface.](#h-36-incorrect-erc20-interface) + - [H-37: Out of order retryable transactions.](#h-37-out-of-order-retryable-transactions) + - [H-38: Constant functions changing state](#h-38-constant-functions-changing-state) + - [H-39: Function selector collides with other functions](#h-39-function-selector-collides-with-other-functions) + - [H-40: Unchecked Low level calls](#h-40-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: Solmate's SafeTransferLib does not check for token contract's existence](#l-2-solmates-safetransferlib-does-not-check-for-token-contracts-existence) @@ -62,40 +61,41 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-10: Event is missing `indexed` fields](#l-10-event-is-missing-indexed-fields) - [L-11: Empty `require()` / `revert()` statements](#l-11-empty-require--revert-statements) - [L-12: The `nonReentrant` `modifier` should occur before all other modifiers](#l-12-the-nonreentrant-modifier-should-occur-before-all-other-modifiers) - - [L-13: Using `ERC721::_mint()` can be dangerous](#l-13-using-erc721mint-can-be-dangerous) - - [L-14: PUSH0 is not supported by all chains](#l-14-push0-is-not-supported-by-all-chains) - - [L-15: Modifiers invoked only once can be shoe-horned into the function](#l-15-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) - - [L-16: Empty Block](#l-16-empty-block) - - [L-17: Large literal values multiples of 10000 can be replaced with scientific notation](#l-17-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) - - [L-18: Internal functions called only once can be inlined](#l-18-internal-functions-called-only-once-can-be-inlined) - - [L-19: Contract still has TODOs](#l-19-contract-still-has-todos) - - [L-20: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-20-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) - - [L-21: Unused Custom Error](#l-21-unused-custom-error) - - [L-22: Loop contains `require`/`revert` statements](#l-22-loop-contains-requirerevert-statements) - - [L-23: Incorrect Order of Division and Multiplication](#l-23-incorrect-order-of-division-and-multiplication) - - [L-24: Redundant statements have no effect.](#l-24-redundant-statements-have-no-effect) - - [L-25: Public variables of a contract read in an external context (using `this`).](#l-25-public-variables-of-a-contract-read-in-an-external-context-using-this) - - [L-26: Potentially unused `private` / `internal` state variables found.](#l-26-potentially-unused-private--internal-state-variables-found) - - [L-27: Functions declared `pure` / `view` but contains assembly](#l-27-functions-declared-pure--view-but-contains-assembly) - - [L-28: Boolean equality is not required.](#l-28-boolean-equality-is-not-required) - - [L-29: Local variable shadows state variables in the contract hirearchy](#l-29-local-variable-shadows-state-variables-in-the-contract-hirearchy) - - [L-30: Uninitialized local variables.](#l-30-uninitialized-local-variables) - - [L-31: Return Bomb](#l-31-return-bomb) - - [L-32: Function initializing state.](#l-32-function-initializing-state) - - [L-33: Dead Code](#l-33-dead-code) - - [L-34: Loop condition contains `state_variable.length` that could be cached outside.](#l-34-loop-condition-contains-statevariablelength-that-could-be-cached-outside) - - [L-35: Incorrect use of `assert()`](#l-35-incorrect-use-of-assert) - - [L-36: Costly operations inside loops.](#l-36-costly-operations-inside-loops) - - [L-37: Builtin Symbol Shadowing](#l-37-builtin-symbol-shadowing) - - [L-38: Void constructor](#l-38-void-constructor) - - [L-39: Potentially missing inheritance for contract.](#l-39-potentially-missing-inheritance-for-contract) - - [L-40: Unused Imports](#l-40-unused-imports) - - [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: 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) - - [L-46: Return value of the function call is not checked.](#l-46-return-value-of-the-function-call-is-not-checked) + - [L-13: Using `block.timestamp` for swap deadline offers no protection](#l-13-using-blocktimestamp-for-swap-deadline-offers-no-protection) + - [L-14: Using `ERC721::_mint()` can be dangerous](#l-14-using-erc721mint-can-be-dangerous) + - [L-15: PUSH0 is not supported by all chains](#l-15-push0-is-not-supported-by-all-chains) + - [L-16: Modifiers invoked only once can be shoe-horned into the function](#l-16-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) + - [L-17: Empty Block](#l-17-empty-block) + - [L-18: Large literal values multiples of 10000 can be replaced with scientific notation](#l-18-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) + - [L-19: Internal functions called only once can be inlined](#l-19-internal-functions-called-only-once-can-be-inlined) + - [L-20: Contract still has TODOs](#l-20-contract-still-has-todos) + - [L-21: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-21-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) + - [L-22: Unused Custom Error](#l-22-unused-custom-error) + - [L-23: Loop contains `require`/`revert` statements](#l-23-loop-contains-requirerevert-statements) + - [L-24: Incorrect Order of Division and Multiplication](#l-24-incorrect-order-of-division-and-multiplication) + - [L-25: Redundant statements have no effect.](#l-25-redundant-statements-have-no-effect) + - [L-26: Public variables of a contract read in an external context (using `this`).](#l-26-public-variables-of-a-contract-read-in-an-external-context-using-this) + - [L-27: Potentially unused `private` / `internal` state variables found.](#l-27-potentially-unused-private--internal-state-variables-found) + - [L-28: Functions declared `pure` / `view` but contains assembly](#l-28-functions-declared-pure--view-but-contains-assembly) + - [L-29: Boolean equality is not required.](#l-29-boolean-equality-is-not-required) + - [L-30: Local variable shadows state variables in the contract hirearchy](#l-30-local-variable-shadows-state-variables-in-the-contract-hirearchy) + - [L-31: Uninitialized local variables.](#l-31-uninitialized-local-variables) + - [L-32: Return Bomb](#l-32-return-bomb) + - [L-33: Function initializing state.](#l-33-function-initializing-state) + - [L-34: Dead Code](#l-34-dead-code) + - [L-35: Loop condition contains `state_variable.length` that could be cached outside.](#l-35-loop-condition-contains-statevariablelength-that-could-be-cached-outside) + - [L-36: Incorrect use of `assert()`](#l-36-incorrect-use-of-assert) + - [L-37: Costly operations inside loops.](#l-37-costly-operations-inside-loops) + - [L-38: Builtin Symbol Shadowing](#l-38-builtin-symbol-shadowing) + - [L-39: Void constructor](#l-39-void-constructor) + - [L-40: Potentially missing inheritance for contract.](#l-40-potentially-missing-inheritance-for-contract) + - [L-41: Unused Imports](#l-41-unused-imports) + - [L-42: Function pointers used in constructors.](#l-42-function-pointers-used-in-constructors) + - [L-43: State variable could be declared constant](#l-43-state-variable-could-be-declared-constant) + - [L-44: State variable changes but no event is emitted.](#l-44-state-variable-changes-but-no-event-is-emitted) + - [L-45: State variable could be declared immutable](#l-45-state-variable-could-be-declared-immutable) + - [L-46: Modifier has multiple placeholders.](#l-46-modifier-has-multiple-placeholders) + - [L-47: Return value of the function call is not checked.](#l-47-return-value-of-the-function-call-is-not-checked) # Summary @@ -231,8 +231,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 41 | -| Low | 46 | +| High | 40 | +| Low | 47 | # High Issues @@ -284,120 +284,7 @@ If all arguments are strings and or bytes, `bytes.concat()` should be used inste -## H-3: Using `block.timestamp` for swap deadline offers no protection - -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. - -
17 Found Instances - - -- Found in src/Trump.sol [Line: 290](../tests/contract-playground/src/Trump.sol#L290) - - ```solidity - uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens( - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 23](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L23) - - ```solidity - router1.swapExactTokensForTokens(amountIn, amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 24](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L24) - - ```solidity - router1.swapTokensForExactTokens(amountOut, amountInMax, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 25](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L25) - - ```solidity - router1.swapExactETHForTokens(amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 26](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L26) - - ```solidity - router1.swapTokensForExactETH(amountOut, amountInMax, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 27](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L27) - - ```solidity - router1.swapExactTokensForETH(amountIn, amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 31](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L31) - - ```solidity - router2.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 32](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L32) - - ```solidity - router2.swapExactETHForTokensSupportingFeeOnTransferTokens(amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 33](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L33) - - ```solidity - router2.swapExactTokensForETHSupportingFeeOnTransferTokens(amountIn, amountOutMin, path, to, block.timestamp); - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 52](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L52) - - ```solidity - ExactInputSingleParams memory exactInputSingleParams = ExactInputSingleParams( - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 55](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L55) - - ```solidity - exactInputSingleParams = ExactInputSingleParams({ - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 66](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L66) - - ```solidity - ExactInputParams memory exactInputParams = ExactInputParams( - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 69](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L69) - - ```solidity - exactInputParams = ExactInputParams({ - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 77](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L77) - - ```solidity - ExactOutputSingleParams memory exactOutputSingleParams = ExactOutputSingleParams( - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 80](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L80) - - ```solidity - exactOutputSingleParams = ExactOutputSingleParams({ - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 91](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L91) - - ```solidity - ExactOutputParams memory exactOutputParams = ExactOutputParams( - ``` - -- Found in src/uniswap/UniswapV3Swapper.sol [Line: 94](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L94) - - ```solidity - exactOutputParams = ExactOutputParams({ - ``` - -
- - - -## H-4: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`) +## H-3: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`) Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. @@ -444,7 +331,7 @@ Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) ca -## H-5: Unprotected initializer +## H-4: Unprotected initializer Consider protecting the initializer functions with modifiers. @@ -461,7 +348,7 @@ Consider protecting the initializer functions with modifiers. -## H-6: Unsafe Casting +## H-5: Unsafe Casting Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior.When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type,leading to truncation and loss of significant digits. Use OpenZeppelin's SafeCast library to safely downcast integers. @@ -1036,7 +923,7 @@ Downcasting int/uints in Solidity can be unsafe due to the potential for data lo -## H-7: EnumerableSet.remove in loop corrupts the set order. +## H-6: EnumerableSet.remove in loop corrupts the set order. If the order of an EnumerableSet is required, removing items in a loop using `at` and `remove` corrupts this order. Consider using a different data structure or removing items by collecting them during the loop, then removing after the loop. @@ -1078,7 +965,7 @@ Consider using a different data structure or removing items by collecting them d -## H-8: Experimental ABI Encoder +## H-7: Experimental ABI Encoder Experimental encoders should not be used in production. There are multiple known compiler bugs that are caused by the experimental encoder. Upgrade your solidity version to remove the need for experimental features. @@ -1095,7 +982,7 @@ Experimental encoders should not be used in production. There are multiple known -## H-9: Incorrect Assembly Shift Parameter Order +## H-8: Incorrect Assembly Shift Parameter Order Example: `shl(shifted, 4)` will shift the right constant `4` by `a` bits. The correct order is `shl(4, shifted)`. @@ -1118,7 +1005,7 @@ Example: `shl(shifted, 4)` will shift the right constant `4` by `a` bits. The co -## H-10: Storage Array Edited with Memory +## H-9: Storage Array Edited with Memory Storage reference is passed to a function with a memory parameter. This will not update the storage variable as expected. Consider using storage parameters instead. @@ -1135,7 +1022,7 @@ Storage reference is passed to a function with a memory parameter. This will not -## H-11: Contract Has Multiple Constructors +## H-10: Contract Has Multiple Constructors In some versions of Solidity, contracts compile with multiple constructors. The first constructor takes precedence. This can lead to unexpected behavior. @@ -1152,7 +1039,7 @@ In some versions of Solidity, contracts compile with multiple constructors. The -## H-12: Contract Name Reused in Different Files +## H-11: Contract Name Reused in Different Files When compiling contracts with certain development frameworks (for example: Truffle), having contracts with the same name across different files can lead to one being overwritten. @@ -1187,7 +1074,7 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-13: Nested Structs in Mappings pre-0.5.0 +## H-12: Nested Structs in Mappings pre-0.5.0 Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled, but produced incorrect values. Refrain from using these, or update to a more recent version of Solidity. @@ -1204,7 +1091,7 @@ Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled -## H-14: Depracated EVM Instruction for `selfdestruct` should not be used. +## H-13: Depracated EVM Instruction for `selfdestruct` should not be used. @@ -1221,7 +1108,7 @@ Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled -## H-15: Array length value has a direct assignment. +## H-14: Array length value has a direct assignment. If the length of a dynamic array (storage variable) directly assigned to, it may allow access to other storage slots by tweaking it's value. This practice has been depracated in newer Solidity versions @@ -1262,7 +1149,7 @@ If the length of a dynamic array (storage variable) directly assigned to, it may -## H-16: Uninitialized State Variables +## H-15: Uninitialized State Variables 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. @@ -1483,7 +1370,7 @@ Solidity does initialize variables by default when you declare them, however it' -## H-17: Incorrect use of caret operator on a non hexadcimal constant +## H-16: Incorrect use of caret operator on a non hexadcimal constant The caret operator is usually mistakenly thought of as an exponentiation operator but actually, it's a bitwise xor operator. @@ -1524,7 +1411,7 @@ The caret operator is usually mistakenly thought of as an exponentiation operato -## H-18: Yul block contains `return` function call. +## H-17: Yul block contains `return` function call. Remove this, as this causes execution to halt. Nothing after that call will execute, including code following the assembly block. @@ -1541,7 +1428,7 @@ Remove this, as this causes execution to halt. Nothing after that call will exec -## H-19: Shadowed State Variables in Inheritance Hierarchy +## H-18: Shadowed State Variables in Inheritance Hierarchy This vulnerability arises when a derived contract unintentionally shadows a state variable from a parent contract by declaring a variable with the same name. This can be misleading. To prevent this, ensure variable names are unique across the inheritance hierarchy or use proper visibility and scope controls. @@ -1558,7 +1445,7 @@ This vulnerability arises when a derived contract unintentionally shadows a stat -## H-20: Unchecked `bool success` value for send call. +## H-19: Unchecked `bool success` value for send call. The transaction `address(payable?).send(address)` may fail because of reasons like out-of-gas, invalid receipient address or revert from the recipient. Therefore, the boolean returned by this function call must be checked to be `true` in order to verify that the transaction was successful @@ -1575,7 +1462,7 @@ The transaction `address(payable?).send(address)` may fail because of reasons li -## H-21: Misused boolean with logical operators +## H-20: Misused boolean with logical operators The patterns `if (… || true)` and `if (.. && false)` will always evaluate to true and false respectively. @@ -1646,7 +1533,7 @@ The patterns `if (… || true)` and `if (.. && false)` will always evaluate to t -## H-22: Functions send eth away from contract but performs no checks on any address. +## H-21: Functions send eth away from contract but performs no checks on any address. Consider introducing checks for `msg.sender` to ensure the recipient of the money is as intended. @@ -1777,7 +1664,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 address. +## H-22: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -1818,7 +1705,7 @@ Introduce checks on the address -## H-24: Tautological comparison. +## H-23: Tautological comparison. The left hand side and the right hand side of the binary operation has the same value. This makes the condition always true or always false. @@ -1853,7 +1740,7 @@ The left hand side and the right hand side of the binary operation has the same -## H-25: RTLO character detected in file. \u{202e} +## H-24: RTLO character detected in file. \u{202e} Right to left override character may be misledaing and cause potential attacks by visually misordering method arguments! @@ -1870,7 +1757,7 @@ Right to left override character may be misledaing and cause potential attacks b -## H-26: Dangerous unary operator found in assignment. +## H-25: Dangerous unary operator found in assignment. Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in between. @@ -1893,7 +1780,7 @@ Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in -## H-27: Tautology or Contradiction in comparison. +## H-26: Tautology or Contradiction in comparison. The condition has been determined to be either always true or always false due to the integer range in which we're operating. @@ -1916,7 +1803,7 @@ The condition has been determined to be either always true or always false due t -## H-28: Dangerous strict equality checks on contract balances. +## H-27: Dangerous strict equality checks on contract balances. A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality. @@ -1945,7 +1832,7 @@ A contract's balance can be forcibly manipulated by another selfdestructing cont -## H-29: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` +## H-28: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` If you want to leverage signed arrays in storage by assigning a literal array with at least one negative number, then you mus use solidity version 0.5.10 or above. This is because of a bug in older compilers. @@ -1962,7 +1849,7 @@ If you want to leverage signed arrays in storage by assigning a literal array wi -## H-30: Weak Randomness +## H-29: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -2027,7 +1914,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-31: Usage of variable before declaration. +## H-30: Usage of variable before declaration. This is a bad practice that may lead to unintended consequences. Please declare the variable before using it. @@ -2044,7 +1931,7 @@ This is a bad practice that may lead to unintended consequences. Please declare -## H-32: Deletion from a nested mappping. +## H-31: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -2061,7 +1948,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-33: Potential use of `tx.origin` for authentication. +## H-32: Potential use of `tx.origin` for authentication. Using `tx.origin` may lead to problems when users are interacting via smart contract with your protocol. It is recommended to use `msg.sender` for authentication. @@ -2090,7 +1977,7 @@ Using `tx.origin` may lead to problems when users are interacting via smart cont -## H-34: Loop contains `msg.value`. +## H-33: Loop contains `msg.value`. Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. @@ -2125,7 +2012,7 @@ Provide an explicit array of amounts alongside the receivers array, and check th -## H-35: Contract locks Ether without a withdraw function. +## H-34: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. @@ -2196,7 +2083,7 @@ It appears that the contract includes a payable function to accept Ether but lac -## H-36: Incorrect ERC721 interface. +## H-35: Incorrect ERC721 interface. Incorrect return values for ERC721 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC721 functions. @@ -2255,7 +2142,7 @@ Incorrect return values for ERC721 functions. A contract compiled with Solidity -## H-37: Incorrect ERC20 interface. +## H-36: Incorrect ERC20 interface. Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions. @@ -2296,7 +2183,7 @@ Incorrect return values for ERC20 functions. A contract compiled with Solidity > -## H-38: Out of order retryable transactions. +## H-37: Out of order retryable transactions. Do not rely on the order or successful execution of retryable tickets. Functions like createRetryableTicket, outboundTransferCustomRefund, unsafeCreateRetryableTicket are free to be re-tried in any order if they fail in the first go. Since this operation happens off chain, the sequencer is in control of the @@ -2321,7 +2208,7 @@ Do not rely on the order or successful execution of retryable tickets. Functions -## H-39: Constant functions changing state +## H-38: Constant functions changing state Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct. @@ -2338,7 +2225,7 @@ Function is declared constant/view but it changes state. Ensure that the attribu -## H-40: Function selector collides with other functions +## H-39: Function selector collides with other functions Function selector collides with other functions. This may cause the solidity function dispatcher to invoke the wrong function if the functions happen to be included in the same contract through an inheritance hirearchy later down the line. It is recommended to rename this function or change its parameters. @@ -2363,7 +2250,7 @@ Function selector collides with other functions. This may cause the solidity fun -## H-41: Unchecked Low level calls +## H-40: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. @@ -4221,7 +4108,120 @@ This is a best-practice to protect against reentrancy in other modifiers. -## L-13: Using `ERC721::_mint()` can be dangerous +## L-13: Using `block.timestamp` for swap deadline offers no protection + +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. + +
17 Found Instances + + +- Found in src/Trump.sol [Line: 290](../tests/contract-playground/src/Trump.sol#L290) + + ```solidity + uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens( + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 23](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L23) + + ```solidity + router1.swapExactTokensForTokens(amountIn, amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 24](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L24) + + ```solidity + router1.swapTokensForExactTokens(amountOut, amountInMax, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 25](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L25) + + ```solidity + router1.swapExactETHForTokens(amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 26](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L26) + + ```solidity + router1.swapTokensForExactETH(amountOut, amountInMax, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 27](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L27) + + ```solidity + router1.swapExactTokensForETH(amountIn, amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 31](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L31) + + ```solidity + router2.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 32](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L32) + + ```solidity + router2.swapExactETHForTokensSupportingFeeOnTransferTokens(amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 33](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L33) + + ```solidity + router2.swapExactTokensForETHSupportingFeeOnTransferTokens(amountIn, amountOutMin, path, to, block.timestamp); + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 52](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L52) + + ```solidity + ExactInputSingleParams memory exactInputSingleParams = ExactInputSingleParams( + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 55](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L55) + + ```solidity + exactInputSingleParams = ExactInputSingleParams({ + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 66](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L66) + + ```solidity + ExactInputParams memory exactInputParams = ExactInputParams( + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 69](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L69) + + ```solidity + exactInputParams = ExactInputParams({ + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 77](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L77) + + ```solidity + ExactOutputSingleParams memory exactOutputSingleParams = ExactOutputSingleParams( + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 80](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L80) + + ```solidity + exactOutputSingleParams = ExactOutputSingleParams({ + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 91](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L91) + + ```solidity + ExactOutputParams memory exactOutputParams = ExactOutputParams( + ``` + +- Found in src/uniswap/UniswapV3Swapper.sol [Line: 94](../tests/contract-playground/src/uniswap/UniswapV3Swapper.sol#L94) + + ```solidity + exactOutputParams = ExactOutputParams({ + ``` + +
+ + + +## L-14: Using `ERC721::_mint()` can be dangerous Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support ERC721 tokens. Use `_safeMint()` instead of `_mint()` for ERC721. @@ -4238,7 +4238,7 @@ Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support -## L-14: PUSH0 is not supported by all chains +## L-15: PUSH0 is not supported by all chains 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. @@ -4507,7 +4507,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-15: Modifiers invoked only once can be shoe-horned into the function +## L-16: Modifiers invoked only once can be shoe-horned into the function @@ -4602,7 +4602,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-16: Empty Block +## L-17: Empty Block Consider removing empty blocks. @@ -4817,7 +4817,7 @@ Consider removing empty blocks. -## L-17: Large literal values multiples of 10000 can be replaced with scientific notation +## L-18: Large literal values multiples of 10000 can be replaced with scientific notation Use `e` notation, for example: `1e18`, instead of its full numeric value. @@ -4990,7 +4990,7 @@ Use `e` notation, for example: `1e18`, instead of its full numeric value. -## L-18: Internal functions called only once can be inlined +## L-19: Internal functions called only once can be inlined 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. @@ -5115,7 +5115,7 @@ Instead of separating the logic into a separate function, consider inlining the -## L-19: Contract still has TODOs +## L-20: Contract still has TODOs Contract contains comments with TODOS @@ -5138,7 +5138,7 @@ Contract contains comments with TODOS -## L-20: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). +## L-21: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). Consider keeping the naming convention consistent in a given contract. Explicit size declarations are preferred (uint256, int256) over implicit ones (uint, int) to avoid confusion. @@ -5323,7 +5323,7 @@ Consider keeping the naming convention consistent in a given contract. Explicit -## L-21: Unused Custom Error +## L-22: Unused Custom Error it is recommended that the definition be removed when custom error is unused @@ -5352,7 +5352,7 @@ it is recommended that the definition be removed when custom error is unused -## L-22: Loop contains `require`/`revert` statements +## L-23: Loop contains `require`/`revert` statements Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop @@ -5375,7 +5375,7 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca -## L-23: Incorrect Order of Division and Multiplication +## L-24: Incorrect Order of Division and Multiplication Division operations followed directly by multiplication operations can lead to precision loss due to the way integer arithmetic is handled in Solidity. @@ -5410,7 +5410,7 @@ Division operations followed directly by multiplication operations can lead to p -## L-24: Redundant statements have no effect. +## L-25: Redundant statements have no effect. Remove the redundant statements because no code will be generated and it just congests the codebase. @@ -5457,7 +5457,7 @@ Remove the redundant statements because no code will be generated and it just co -## L-25: Public variables of a contract read in an external context (using `this`). +## L-26: Public variables of a contract read in an external context (using `this`). The contract reads it's own variable using `this` which adds an unnecessary STATICCALL. Remove `this` and access the variable like storage. @@ -5492,7 +5492,7 @@ The contract reads it's own variable using `this` which adds an unnecessary STAT -## L-26: Potentially unused `private` / `internal` state variables found. +## L-27: Potentially unused `private` / `internal` state variables found. State variable appears to be unused. No analysis has been performed to see if any inilne assembly references it. So if that's not the case, consider removing this unused variable. @@ -5665,7 +5665,7 @@ State variable appears to be unused. No analysis has been performed to see if an -## L-27: Functions declared `pure` / `view` but contains assembly +## L-28: Functions declared `pure` / `view` but contains assembly If the assembly code contains bugs or unintended side effects, it can lead to incorrect results or vulnerabilities, which are hard to debug and resolve, especially when the function is meant to be simple and predictable. @@ -5694,7 +5694,7 @@ If the assembly code contains bugs or unintended side effects, it can lead to in -## L-28: Boolean equality is not required. +## L-29: Boolean equality is not required. 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. @@ -5729,7 +5729,7 @@ If `x` is a boolean, there is no need to do `if(x == true)` or `if(x == false)`. -## L-29: Local variable shadows state variables in the contract hirearchy +## L-30: Local variable shadows state variables in the contract hirearchy Rename the local variables that shadow another component. @@ -5788,7 +5788,7 @@ Rename the local variables that shadow another component. -## L-30: Uninitialized local variables. +## L-31: Uninitialized local variables. Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. @@ -5889,7 +5889,7 @@ Initialize all the variables. If a variable is meant to be initialized to zero, -## L-31: Return Bomb +## L-32: Return Bomb A low level callee may consume all callers gas unexpectedly. Avoid unlimited implicit decoding of returndata on calls to unchecked addresses. You can limit the gas by passing a gas limit as an option to the call. For example, `unknownAdress.call{gas: gasLimitHere}("calldata")` That would act as a safety net from OOG errors. @@ -5907,7 +5907,7 @@ A low level callee may consume all callers gas unexpectedly. Avoid unlimited imp -## L-32: Function initializing state. +## L-33: Function initializing state. Detects the immediate initialization of state variables through function calls that are not pure/constant, or that use non-constant state variable. Remove any initialization of state variables via non-constant state variables or function calls. If variables must be set upon contract deployment, locate initialization in the constructor instead. @@ -5936,7 +5936,7 @@ Detects the immediate initialization of state variables through function calls t -## L-33: Dead Code +## L-34: Dead Code Functions that are not used. Consider removing them. @@ -6019,7 +6019,7 @@ Functions that are not used. Consider removing them. -## L-34: Loop condition contains `state_variable.length` that could be cached outside. +## L-35: Loop condition contains `state_variable.length` that could be cached outside. Cache the lengths of storage arrays if they are used and not modified in for loops. @@ -6048,7 +6048,7 @@ Cache the lengths of storage arrays if they are used and not modified in for loo -## L-35: Incorrect use of `assert()` +## L-36: Incorrect use of `assert()` Argument to `assert()` modifies the state. Use `require` for invariants modifying state. @@ -6065,7 +6065,7 @@ Argument to `assert()` modifies the state. Use `require` for invariants modifyin -## L-36: Costly operations inside loops. +## L-37: Costly operations inside loops. Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local variable to hold the loop computation result. @@ -6160,7 +6160,7 @@ Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local -## L-37: Builtin Symbol Shadowing +## L-38: Builtin Symbol Shadowing Name clashes with a built-in-symbol. Consider renaming it. @@ -6195,7 +6195,7 @@ Name clashes with a built-in-symbol. Consider renaming it. -## L-38: Void constructor +## L-39: Void constructor Call to a constructor that is not implemented. @@ -6212,7 +6212,7 @@ Call to a constructor that is not implemented. -## L-39: Potentially missing inheritance for contract. +## L-40: Potentially missing inheritance for contract. There is an interface / abstract contract that is potentially missing (not included in) the inheritance of this contract. @@ -6244,7 +6244,7 @@ There is an interface / abstract contract that is potentially missing (not inclu -## L-40: Unused Imports +## L-41: Unused Imports Redundant import statement. Consider removing it. @@ -6273,7 +6273,7 @@ Redundant import statement. Consider removing it. -## L-41: Function pointers used in constructors. +## L-42: Function pointers used in constructors. solc versions below 0.5.9 contain a compiler bug leading to unexpected behavior when calling uninitialized function pointers in constructors. It is recommended to not use function pointers in constructors. @@ -6290,7 +6290,7 @@ solc versions below 0.5.9 contain a compiler bug leading to unexpected behavior -## L-42: State variable could be declared constant +## L-43: State variable could be declared constant State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change. @@ -6571,7 +6571,7 @@ State variables that are not updated following deployment should be declared con -## L-43: State variable changes but no event is emitted. +## L-44: State variable changes but no event is emitted. State variable changes in this function but no event is emitted. @@ -7182,7 +7182,7 @@ State variable changes in this function but no event is emitted. -## L-44: State variable could be declared immutable +## L-45: 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 @@ -7379,7 +7379,7 @@ State variables that are should be declared immutable to save gas. Add the `immu -## L-45: Modifier has multiple placeholders. +## L-46: Modifier has multiple placeholders. Design the modifier to only contain 1 placeholder statement. If it's not possible, split the logic into multiple modifiers. @@ -7396,7 +7396,7 @@ Design the modifier to only contain 1 placeholder statement. If it's not possibl -## L-46: Return value of the function call is not checked. +## L-47: Return value of the function call is not checked. Function returns a value but it is ignored. diff --git a/reports/report.sarif b/reports/report.sarif index 66210539f..9498df3e9 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -66,202 +66,6 @@ }, "ruleId": "hash-collision-due-to-abi-encode-packed" }, - { - "level": "warning", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/Trump.sol" - }, - "region": { - "byteLength": 190, - "byteOffset": 9949 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 83, - "byteOffset": 670 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 83, - "byteOffset": 763 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 70, - "byteOffset": 856 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 80, - "byteOffset": 936 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 80, - "byteOffset": 1026 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 112, - "byteOffset": 1278 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 99, - "byteOffset": 1400 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV2Swapper.sol" - }, - "region": { - "byteLength": 109, - "byteOffset": 1509 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 143, - "byteOffset": 1115 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 321, - "byteOffset": 1293 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 131, - "byteOffset": 1668 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 236, - "byteOffset": 1828 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 144, - "byteOffset": 2132 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 322, - "byteOffset": 2312 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 132, - "byteOffset": 2690 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/uniswap/UniswapV3Swapper.sol" - }, - "region": { - "byteLength": 237, - "byteOffset": 2852 - } - } - } - ], - "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-is-weak-deadline" - }, { "level": "warning", "locations": [ @@ -6746,6 +6550,202 @@ }, "ruleId": "non-reentrant-is-not-before-others" }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/Trump.sol" + }, + "region": { + "byteLength": 190, + "byteOffset": 9949 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 83, + "byteOffset": 670 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 83, + "byteOffset": 763 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 70, + "byteOffset": 856 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 80, + "byteOffset": 936 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 80, + "byteOffset": 1026 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 112, + "byteOffset": 1278 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 99, + "byteOffset": 1400 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV2Swapper.sol" + }, + "region": { + "byteLength": 109, + "byteOffset": 1509 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 143, + "byteOffset": 1115 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 321, + "byteOffset": 1293 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 131, + "byteOffset": 1668 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 236, + "byteOffset": 1828 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 144, + "byteOffset": 2132 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 322, + "byteOffset": 2312 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 132, + "byteOffset": 2690 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/uniswap/UniswapV3Swapper.sol" + }, + "region": { + "byteLength": 237, + "byteOffset": 2852 + } + } + } + ], + "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-is-weak-deadline" + }, { "level": "note", "locations": [ diff --git a/reports/uniswap_profile.md b/reports/uniswap_profile.md index e0eff4251..b1d889988 100644 --- a/reports/uniswap_profile.md +++ b/reports/uniswap_profile.md @@ -7,12 +7,11 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Summary](#files-summary) - [Files Details](#files-details) - [Issue Summary](#issue-summary) -- [High Issues](#high-issues) - - [H-1: Using `block.timestamp` for swap deadline offers no protection](#h-1-using-blocktimestamp-for-swap-deadline-offers-no-protection) - [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) + - [L-2: Using `block.timestamp` for swap deadline offers no protection](#l-2-using-blocktimestamp-for-swap-deadline-offers-no-protection) + - [L-3: PUSH0 is not supported by all chains](#l-3-push0-is-not-supported-by-all-chains) + - [L-4: State variable could be declared immutable](#l-4-state-variable-could-be-declared-immutable) # Summary @@ -38,13 +37,30 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 1 | -| Low | 3 | +| High | 0 | +| Low | 4 | -# High Issues +# Low Issues + +## L-1: Missing checks for `address(0)` when assigning values to address state variables + +Check for `address(0)` when assigning values to address state variables. + +
1 Found Instances -## H-1: Using `block.timestamp` for swap deadline offers no protection + +- Found in src/uniswap/UniswapV2Swapper.sol [Line: 11](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L11) + + ```solidity + s_router = router; + ``` + +
+ + + +## L-2: Using `block.timestamp` for swap deadline offers no protection 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. @@ -151,26 +167,7 @@ In the PoS model, proposers know well in advance if they will propose one or con -# Low Issues - -## L-1: Missing checks for `address(0)` when assigning values to address state variables - -Check for `address(0)` when assigning values to address state variables. - -
1 Found Instances - - -- Found in src/uniswap/UniswapV2Swapper.sol [Line: 11](../tests/contract-playground/src/uniswap/UniswapV2Swapper.sol#L11) - - ```solidity - s_router = router; - ``` - -
- - - -## L-2: PUSH0 is not supported by all chains +## L-3: PUSH0 is not supported by all chains 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. @@ -193,7 +190,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-3: State variable could be declared immutable +## L-4: 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 From a19aa12eab91e7970f6ed8062b230c536217ef31 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 13:56:07 +0530 Subject: [PATCH 19/25] Kill detector: uninitialized state variable (#770) --- aderyn_core/src/detect/detector.rs | 5 - aderyn_core/src/detect/high/mod.rs | 2 - .../high/uninitialized_state_variable.rs | 145 ------- .../adhoc-sol-files-highs-only-report.json | 40 +- reports/adhoc-sol-files-report.md | 52 +-- reports/ccip-functions-report.md | 26 +- reports/hardhat-playground-report.md | 40 +- reports/report.json | 220 +--------- reports/report.md | 324 +++----------- reports/report.sarif | 394 ------------------ reports/templegold-report.md | 68 +-- 11 files changed, 71 insertions(+), 1245 deletions(-) delete mode 100644 aderyn_core/src/detect/high/uninitialized_state_variable.rs diff --git a/aderyn_core/src/detect/detector.rs b/aderyn_core/src/detect/detector.rs index d97fabd6c..7693cc98c 100644 --- a/aderyn_core/src/detect/detector.rs +++ b/aderyn_core/src/detect/detector.rs @@ -54,7 +54,6 @@ pub fn get_all_issue_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), - Box::::default(), Box::::default(), Box::::default(), Box::::default(), @@ -169,7 +168,6 @@ pub(crate) enum IssueDetectorNamePool { NestedStructInMapping, SelfdestructIdentifier, DynamicArrayLengthAssignment, - UninitializedStateVariable, IncorrectCaretOperator, YulReturn, StateVariableShadowing, @@ -375,9 +373,6 @@ pub fn request_issue_detector_by_name(detector_name: &str) -> Option::default()) } - IssueDetectorNamePool::UninitializedStateVariable => { - Some(Box::::default()) - } IssueDetectorNamePool::IncorrectCaretOperator => { Some(Box::::default()) } diff --git a/aderyn_core/src/detect/high/mod.rs b/aderyn_core/src/detect/high/mod.rs index 757cc4132..e47edf407 100644 --- a/aderyn_core/src/detect/high/mod.rs +++ b/aderyn_core/src/detect/high/mod.rs @@ -33,7 +33,6 @@ pub(crate) mod tautology_or_contradiction; pub(crate) mod tx_origin_used_for_auth; pub(crate) mod unchecked_calls; pub(crate) mod unchecked_send; -pub(crate) mod uninitialized_state_variable; pub(crate) mod unprotected_init_function; pub(crate) mod unsafe_casting; pub(crate) mod weak_randomness; @@ -74,7 +73,6 @@ pub use tautology_or_contradiction::TautologyOrContraditionDetector; pub use tx_origin_used_for_auth::TxOriginUsedForAuthDetector; pub use unchecked_calls::UncheckedLowLevelCallDetector; pub use unchecked_send::UncheckedSendDetector; -pub use uninitialized_state_variable::UninitializedStateVariableDetector; pub use unprotected_init_function::UnprotectedInitializerDetector; pub use unsafe_casting::UnsafeCastingDetector; pub use weak_randomness::WeakRandomnessDetector; diff --git a/aderyn_core/src/detect/high/uninitialized_state_variable.rs b/aderyn_core/src/detect/high/uninitialized_state_variable.rs deleted file mode 100644 index 067a5c0de..000000000 --- a/aderyn_core/src/detect/high/uninitialized_state_variable.rs +++ /dev/null @@ -1,145 +0,0 @@ -use std::{ - collections::{BTreeMap, HashSet}, - error::Error, -}; - -use crate::ast::{Expression, NodeID}; - -use crate::{ - capture, - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, -}; -use eyre::Result; - -#[derive(Default)] -pub struct UninitializedStateVariableDetector { - // 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 UninitializedStateVariableDetector { - fn detect(&mut self, context: &WorkspaceContext) -> Result> { - /* - * Plan (Maybe it can be improved) - * - 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. - * - 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 - * - Now we're left with storage variables that have never been initialized - */ - - let mut state_variable_ids = HashSet::new(); - - for var_decl in context - .variable_declarations() - .into_iter() - .filter(|s| s.state_variable && s.value.is_none()) - .filter(|s| { - s.type_descriptions - .type_string - .as_ref() - .is_some_and(|type_string| !type_string.starts_with("mapping")) - }) - .filter(|s| { - s.type_descriptions - .type_string - .as_ref() - .is_some_and(|type_string| !type_string.ends_with(']')) - }) - .filter(|s| { - s.type_descriptions - .type_string - .as_ref() - .is_some_and(|type_string| !type_string.starts_with("struct")) - }) - { - state_variable_ids.insert(var_decl.id); - } - - for assignment in context.assignments() { - if let Expression::Identifier(identifier) = assignment.left_hand_side.as_ref() { - if let Some(refers_to) = identifier.referenced_declaration { - let _ = state_variable_ids.remove(&refers_to); - } - } - } - - for id in state_variable_ids { - context.nodes.get(&id).inspect(|&x| capture!(self, context, x)); - } - - Ok(!self.found_instances.is_empty()) - } - - fn severity(&self) -> IssueSeverity { - IssueSeverity::High - } - - fn title(&self) -> String { - String::from("Uninitialized State Variables") - } - - fn description(&self) -> String { - String::from( - "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." - ) - } - - fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> { - self.found_instances.clone() - } - - fn name(&self) -> String { - IssueDetectorNamePool::UninitializedStateVariable.to_string() - } -} - -#[cfg(test)] -mod uninitialized_state_variable_tests { - use serial_test::serial; - - use crate::detect::{ - detector::IssueDetector, - high::uninitialized_state_variable::UninitializedStateVariableDetector, - }; - - #[test] - #[serial] - fn test_uninitialized_state_variables() { - let context: crate::context::workspace_context::WorkspaceContext = - crate::detect::test_utils::load_solidity_source_unit( - "../tests/contract-playground/src/UninitializedStateVariable.sol", - ); - - let mut detector = UninitializedStateVariableDetector::default(); - let found = detector.detect(&context).unwrap(); - - println!("{:?}", detector.instances()); - - // 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); - // assert the severity is 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 the description is correct - assert_eq!( - detector.description(), - String::from( - "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." - ) - ); - } -} diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index c38712b23..6bc3b4bde 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -88,7 +88,7 @@ ] }, "issue_count": { - "high": 4, + "high": 3, "low": 0 }, "high_issues": { @@ -106,43 +106,6 @@ } ] }, - { - "title": "Uninitialized State Variables", - "description": "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.", - "detector_name": "uninitialized-state-variable", - "instances": [ - { - "contract_path": "InconsistentUints.sol", - "line_no": 7, - "src": "197:11", - "src_char": "197:11" - }, - { - "contract_path": "InconsistentUints.sol", - "line_no": 8, - "src": "233:14", - "src_char": "233:14" - }, - { - "contract_path": "StateVariables.sol", - "line_no": 8, - "src": "199:19", - "src_char": "199:19" - }, - { - "contract_path": "StateVariables.sol", - "line_no": 9, - "src": "241:20", - "src_char": "241:20" - }, - { - "contract_path": "StateVariables.sol", - "line_no": 10, - "src": "282:18", - "src_char": "282:18" - } - ] - }, { "title": "Delegatecall made by the function without checks on any address.", "description": "Introduce checks on the address", @@ -189,7 +152,6 @@ "nested-struct-in-mapping", "selfdestruct-identifier", "dynamic-array-length-assignment", - "uninitialized-state-variable", "incorrect-caret-operator", "yul-return", "state-variable-shadowing", diff --git a/reports/adhoc-sol-files-report.md b/reports/adhoc-sol-files-report.md index 54f803537..db45e4445 100644 --- a/reports/adhoc-sol-files-report.md +++ b/reports/adhoc-sol-files-report.md @@ -9,9 +9,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Issue Summary](#issue-summary) - [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 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) + - [H-2: Delegatecall made by the function without checks on any address.](#h-2-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-3: Unchecked Low level calls](#h-3-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -78,7 +77,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 4 | +| High | 3 | | Low | 22 | @@ -101,48 +100,7 @@ When calling `delegatecall` the same `msg.value` amount will be accredited multi -## H-2: Uninitialized State Variables - -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. - -
5 Found Instances - - -- Found in InconsistentUints.sol [Line: 7](../tests/adhoc-sol-files/InconsistentUints.sol#L7) - - ```solidity - int public intVariable; // 1 - ``` - -- Found in InconsistentUints.sol [Line: 8](../tests/adhoc-sol-files/InconsistentUints.sol#L8) - - ```solidity - int256 public int256Variable; // 1 - ``` - -- Found in StateVariables.sol [Line: 8](../tests/adhoc-sol-files/StateVariables.sol#L8) - - ```solidity - uint256 private staticPrivateNumber; - ``` - -- Found in StateVariables.sol [Line: 9](../tests/adhoc-sol-files/StateVariables.sol#L9) - - ```solidity - uint256 internal staticInternalNumber; - ``` - -- Found in StateVariables.sol [Line: 10](../tests/adhoc-sol-files/StateVariables.sol#L10) - - ```solidity - uint256 public staticPublicNumber; - ``` - -
- - - -## H-3: Delegatecall made by the function without checks on any address. +## H-2: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -159,7 +117,7 @@ Introduce checks on the address -## H-4: Unchecked Low level calls +## H-3: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. diff --git a/reports/ccip-functions-report.md b/reports/ccip-functions-report.md index c6a976fd2..41637b56f 100644 --- a/reports/ccip-functions-report.md +++ b/reports/ccip-functions-report.md @@ -10,7 +10,6 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [High Issues](#high-issues) - [H-1: Unprotected initializer](#h-1-unprotected-initializer) - [H-2: Contract Name Reused in Different Files](#h-2-contract-name-reused-in-different-files) - - [H-3: Uninitialized State Variables](#h-3-uninitialized-state-variables) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -105,7 +104,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 3 | +| High | 2 | | Low | 18 | @@ -469,29 +468,6 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-3: Uninitialized State Variables - -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. - -
2 Found Instances - - -- Found in src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol [Line: 39](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol#L39) - - ```solidity - uint64 private s_currentSubscriptionId; - ``` - -- Found in src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol [Line: 39](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol#L39) - - ```solidity - uint64 private s_currentSubscriptionId; - ``` - -
- - - # Low Issues ## L-1: Centralization Risk for trusted owners diff --git a/reports/hardhat-playground-report.md b/reports/hardhat-playground-report.md index 044fb7da1..1d37df154 100644 --- a/reports/hardhat-playground-report.md +++ b/reports/hardhat-playground-report.md @@ -10,9 +10,8 @@ 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: `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 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) + - [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: `ecrecover` is susceptible to signature malleability](#l-1-ecrecover-is-susceptible-to-signature-malleability) - [L-2: Unsafe ERC20 Operations should not be used](#l-2-unsafe-erc20-operations-should-not-be-used) @@ -57,7 +56,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 5 | +| High | 4 | | Low | 13 | @@ -110,36 +109,7 @@ If all arguments are strings and or bytes, `bytes.concat()` should be used inste -## H-3: Uninitialized State Variables - -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. - -
3 Found Instances - - -- Found in contracts/StateVariables.sol [Line: 8](../tests/hardhat-js-playground/contracts/StateVariables.sol#L8) - - ```solidity - uint256 private staticPrivateNumber; - ``` - -- Found in contracts/StateVariables.sol [Line: 9](../tests/hardhat-js-playground/contracts/StateVariables.sol#L9) - - ```solidity - uint256 internal staticInternalNumber; - ``` - -- Found in contracts/StateVariables.sol [Line: 10](../tests/hardhat-js-playground/contracts/StateVariables.sol#L10) - - ```solidity - uint256 public staticPublicNumber; - ``` - -
- - - -## H-4: Delegatecall made by the function without checks on any address. +## H-3: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -156,7 +126,7 @@ Introduce checks on the address -## H-5: Unchecked Low level calls +## H-4: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. diff --git a/reports/report.json b/reports/report.json index cdc663f8d..df85b688d 100644 --- a/reports/report.json +++ b/reports/report.json @@ -456,7 +456,7 @@ ] }, "issue_count": { - "high": 40, + "high": 39, "low": 47 }, "high_issues": { @@ -1315,223 +1315,6 @@ } ] }, - { - "title": "Uninitialized State Variables", - "description": "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.", - "detector_name": "uninitialized-state-variable", - "instances": [ - { - "contract_path": "src/AssemblyExample.sol", - "line_no": 5, - "src": "97:1", - "src_char": "97:1" - }, - { - "contract_path": "src/BuiltinSymbolShadow.sol", - "line_no": 5, - "src": "92:8", - "src_char": "92:8" - }, - { - "contract_path": "src/ConstantFuncsAssembly.sol", - "line_no": 6, - "src": "110:20", - "src_char": "110:20" - }, - { - "contract_path": "src/DelegateCallWithoutAddressCheck.sol", - "line_no": 9, - "src": "337:7", - "src_char": "337:7" - }, - { - "contract_path": "src/InconsistentUints.sol", - "line_no": 7, - "src": "197:11", - "src_char": "197:11" - }, - { - "contract_path": "src/InconsistentUints.sol", - "line_no": 8, - "src": "233:14", - "src_char": "233:14" - }, - { - "contract_path": "src/IncorrectCaretOperator.sol", - "line_no": 10, - "src": "355:7", - "src_char": "355:7" - }, - { - "contract_path": "src/IncorrectERC721.sol", - "line_no": 147, - "src": "4076:11", - "src_char": "4076:11" - }, - { - "contract_path": "src/LocalVariableShadow.sol", - "line_no": 7, - "src": "129:5", - "src_char": "129:5" - }, - { - "contract_path": "src/LocalVariableShadow.sol", - "line_no": 25, - "src": "532:4", - "src_char": "532:4" - }, - { - "contract_path": "src/PublicVariableReadInExternalContext.sol", - "line_no": 6, - "src": "130:26", - "src_char": "130:26" - }, - { - "contract_path": "src/ReturnBomb.sol", - "line_no": 61, - "src": "1623:7", - "src_char": "1623:7" - }, - { - "contract_path": "src/StateShadowing.sol", - "line_no": 5, - "src": "87:13", - "src_char": "87:13" - }, - { - "contract_path": "src/StateVariables.sol", - "line_no": 8, - "src": "199:19", - "src_char": "199:19" - }, - { - "contract_path": "src/StateVariables.sol", - "line_no": 9, - "src": "241:20", - "src_char": "241:20" - }, - { - "contract_path": "src/StateVariables.sol", - "line_no": 10, - "src": "282:18", - "src_char": "282:18" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 8, - "src": "184:10", - "src_char": "184:10" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 9, - "src": "214:9", - "src_char": "214:9" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 10, - "src": "241:10", - "src_char": "241:10" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 11, - "src": "272:13", - "src_char": "272:13" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 12, - "src": "314:20", - "src_char": "314:20" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 13, - "src": "354:12", - "src_char": "354:12" - }, - { - "contract_path": "src/StateVariablesManipulation.sol", - "line_no": 14, - "src": "385:11", - "src_char": "385:11" - }, - { - "contract_path": "src/TautologyOrContradiction.sol", - "line_no": 6, - "src": "133:6", - "src_char": "133:6" - }, - { - "contract_path": "src/TautologyOrContradiction.sol", - "line_no": 7, - "src": "145:9", - "src_char": "145:9" - }, - { - "contract_path": "src/UninitializedLocalVariables.sol", - "line_no": 5, - "src": "93:12", - "src_char": "93:12" - }, - { - "contract_path": "src/UninitializedStateVariable.sol", - "line_no": 7, - "src": "122:8", - "src_char": "122:8" - }, - { - "contract_path": "src/UninitializedStateVariable.sol", - "line_no": 15, - "src": "529:11", - "src_char": "529:11" - }, - { - "contract_path": "src/UnusedStateVariables.sol", - "line_no": 6, - "src": "147:13", - "src_char": "147:13" - }, - { - "contract_path": "src/UnusedStateVariables.sol", - "line_no": 7, - "src": "183:13", - "src_char": "183:13" - }, - { - "contract_path": "src/UnusedStateVariables.sol", - "line_no": 8, - "src": "215:10", - "src_char": "215:10" - }, - { - "contract_path": "src/UnusedStateVariables.sol", - "line_no": 9, - "src": "246:12", - "src_char": "246:12" - }, - { - "contract_path": "src/UnusedStateVariables.sol", - "line_no": 12, - "src": "314:11", - "src_char": "314:11" - }, - { - "contract_path": "src/WrongOrderOfLayout.sol", - "line_no": 11, - "src": "257:10", - "src_char": "257:10" - }, - { - "contract_path": "src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol", - "line_no": 68, - "src": "1971:5", - "src_char": "1971:5" - } - ] - }, { "title": "Incorrect use of caret operator on a non hexadcimal constant", "description": "The caret operator is usually mistakenly thought of as an exponentiation operator but actually, it's a bitwise xor operator.", @@ -7402,7 +7185,6 @@ "nested-struct-in-mapping", "selfdestruct-identifier", "dynamic-array-length-assignment", - "uninitialized-state-variable", "incorrect-caret-operator", "yul-return", "state-variable-shadowing", diff --git a/reports/report.md b/reports/report.md index f44c2a82a..cc8d293a9 100644 --- a/reports/report.md +++ b/reports/report.md @@ -22,32 +22,31 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-12: Nested Structs in Mappings pre-0.5.0](#h-12-nested-structs-in-mappings-pre-050) - [H-13: Depracated EVM Instruction for `selfdestruct` should not be used.](#h-13-depracated-evm-instruction-for-selfdestruct-should-not-be-used) - [H-14: Array length value has a direct assignment.](#h-14-array-length-value-has-a-direct-assignment) - - [H-15: Uninitialized State Variables](#h-15-uninitialized-state-variables) - - [H-16: Incorrect use of caret operator on a non hexadcimal constant](#h-16-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) - - [H-17: Yul block contains `return` function call.](#h-17-yul-block-contains-return-function-call) - - [H-18: Shadowed State Variables in Inheritance Hierarchy](#h-18-shadowed-state-variables-in-inheritance-hierarchy) - - [H-19: Unchecked `bool success` value for send call.](#h-19-unchecked-bool-success-value-for-send-call) - - [H-20: Misused boolean with logical operators](#h-20-misused-boolean-with-logical-operators) - - [H-21: Functions send eth away from contract but performs no checks on any address.](#h-21-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) - - [H-22: Delegatecall made by the function without checks on any address.](#h-22-delegatecall-made-by-the-function-without-checks-on-any-address) - - [H-23: Tautological comparison.](#h-23-tautological-comparison) - - [H-24: RTLO character detected in file. \u{202e}](#h-24-rtlo-character-detected-in-file-u202e) - - [H-25: Dangerous unary operator found in assignment.](#h-25-dangerous-unary-operator-found-in-assignment) - - [H-26: Tautology or Contradiction in comparison.](#h-26-tautology-or-contradiction-in-comparison) - - [H-27: Dangerous strict equality checks on contract balances.](#h-27-dangerous-strict-equality-checks-on-contract-balances) - - [H-28: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-28-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) - - [H-29: Weak Randomness](#h-29-weak-randomness) - - [H-30: Usage of variable before declaration.](#h-30-usage-of-variable-before-declaration) - - [H-31: Deletion from a nested mappping.](#h-31-deletion-from-a-nested-mappping) - - [H-32: Potential use of `tx.origin` for authentication.](#h-32-potential-use-of-txorigin-for-authentication) - - [H-33: Loop contains `msg.value`.](#h-33-loop-contains-msgvalue) - - [H-34: Contract locks Ether without a withdraw function.](#h-34-contract-locks-ether-without-a-withdraw-function) - - [H-35: Incorrect ERC721 interface.](#h-35-incorrect-erc721-interface) - - [H-36: Incorrect ERC20 interface.](#h-36-incorrect-erc20-interface) - - [H-37: Out of order retryable transactions.](#h-37-out-of-order-retryable-transactions) - - [H-38: Constant functions changing state](#h-38-constant-functions-changing-state) - - [H-39: Function selector collides with other functions](#h-39-function-selector-collides-with-other-functions) - - [H-40: Unchecked Low level calls](#h-40-unchecked-low-level-calls) + - [H-15: Incorrect use of caret operator on a non hexadcimal constant](#h-15-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) + - [H-16: Yul block contains `return` function call.](#h-16-yul-block-contains-return-function-call) + - [H-17: Shadowed State Variables in Inheritance Hierarchy](#h-17-shadowed-state-variables-in-inheritance-hierarchy) + - [H-18: Unchecked `bool success` value for send call.](#h-18-unchecked-bool-success-value-for-send-call) + - [H-19: Misused boolean with logical operators](#h-19-misused-boolean-with-logical-operators) + - [H-20: Functions send eth away from contract but performs no checks on any address.](#h-20-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) + - [H-21: Delegatecall made by the function without checks on any address.](#h-21-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-22: Tautological comparison.](#h-22-tautological-comparison) + - [H-23: RTLO character detected in file. \u{202e}](#h-23-rtlo-character-detected-in-file-u202e) + - [H-24: Dangerous unary operator found in assignment.](#h-24-dangerous-unary-operator-found-in-assignment) + - [H-25: Tautology or Contradiction in comparison.](#h-25-tautology-or-contradiction-in-comparison) + - [H-26: Dangerous strict equality checks on contract balances.](#h-26-dangerous-strict-equality-checks-on-contract-balances) + - [H-27: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-27-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) + - [H-28: Weak Randomness](#h-28-weak-randomness) + - [H-29: Usage of variable before declaration.](#h-29-usage-of-variable-before-declaration) + - [H-30: Deletion from a nested mappping.](#h-30-deletion-from-a-nested-mappping) + - [H-31: Potential use of `tx.origin` for authentication.](#h-31-potential-use-of-txorigin-for-authentication) + - [H-32: Loop contains `msg.value`.](#h-32-loop-contains-msgvalue) + - [H-33: Contract locks Ether without a withdraw function.](#h-33-contract-locks-ether-without-a-withdraw-function) + - [H-34: Incorrect ERC721 interface.](#h-34-incorrect-erc721-interface) + - [H-35: Incorrect ERC20 interface.](#h-35-incorrect-erc20-interface) + - [H-36: Out of order retryable transactions.](#h-36-out-of-order-retryable-transactions) + - [H-37: Constant functions changing state](#h-37-constant-functions-changing-state) + - [H-38: Function selector collides with other functions](#h-38-function-selector-collides-with-other-functions) + - [H-39: Unchecked Low level calls](#h-39-unchecked-low-level-calls) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: Solmate's SafeTransferLib does not check for token contract's existence](#l-2-solmates-safetransferlib-does-not-check-for-token-contracts-existence) @@ -231,7 +230,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 40 | +| High | 39 | | Low | 47 | @@ -1149,228 +1148,7 @@ If the length of a dynamic array (storage variable) directly assigned to, it may -## H-15: Uninitialized State Variables - -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. - -
35 Found Instances - - -- Found in src/AssemblyExample.sol [Line: 5](../tests/contract-playground/src/AssemblyExample.sol#L5) - - ```solidity - uint b; - ``` - -- Found in src/BuiltinSymbolShadow.sol [Line: 5](../tests/contract-playground/src/BuiltinSymbolShadow.sol#L5) - - ```solidity - uint now; // BAD - ``` - -- Found in src/ConstantFuncsAssembly.sol [Line: 6](../tests/contract-playground/src/ConstantFuncsAssembly.sol#L6) - - ```solidity - uint256 public value; - ``` - -- Found in src/DelegateCallWithoutAddressCheck.sol [Line: 9](../tests/contract-playground/src/DelegateCallWithoutAddressCheck.sol#L9) - - ```solidity - address public manager; - ``` - -- Found in src/InconsistentUints.sol [Line: 7](../tests/contract-playground/src/InconsistentUints.sol#L7) - - ```solidity - int public intVariable; // 1 - ``` - -- Found in src/InconsistentUints.sol [Line: 8](../tests/contract-playground/src/InconsistentUints.sol#L8) - - ```solidity - int256 public int256Variable; // 1 - ``` - -- Found in src/IncorrectCaretOperator.sol [Line: 10](../tests/contract-playground/src/IncorrectCaretOperator.sol#L10) - - ```solidity - uint256 private s_first; - ``` - -- Found in src/IncorrectERC721.sol [Line: 147](../tests/contract-playground/src/IncorrectERC721.sol#L147) - - ```solidity - uint256 public totalSupply; - ``` - -- Found in src/LocalVariableShadow.sol [Line: 7](../tests/contract-playground/src/LocalVariableShadow.sol#L7) - - ```solidity - uint owner; - ``` - -- Found in src/LocalVariableShadow.sol [Line: 25](../tests/contract-playground/src/LocalVariableShadow.sol#L25) - - ```solidity - uint internal roll; - ``` - -- Found in src/PublicVariableReadInExternalContext.sol [Line: 6](../tests/contract-playground/src/PublicVariableReadInExternalContext.sol#L6) - - ```solidity - uint256 public testUint256; - ``` - -- Found in src/ReturnBomb.sol [Line: 61](../tests/contract-playground/src/ReturnBomb.sol#L61) - - ```solidity - address goodGuy; - ``` - -- Found in src/StateShadowing.sol [Line: 5](../tests/contract-playground/src/StateShadowing.sol#L5) - - ```solidity - address owner; - ``` - -- Found in src/StateVariables.sol [Line: 8](../tests/contract-playground/src/StateVariables.sol#L8) - - ```solidity - uint256 private staticPrivateNumber; - ``` - -- Found in src/StateVariables.sol [Line: 9](../tests/contract-playground/src/StateVariables.sol#L9) - - ```solidity - uint256 internal staticInternalNumber; - ``` - -- Found in src/StateVariables.sol [Line: 10](../tests/contract-playground/src/StateVariables.sol#L10) - - ```solidity - uint256 public staticPublicNumber; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 8](../tests/contract-playground/src/StateVariablesManipulation.sol#L8) - - ```solidity - uint256 public simpleUint; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 9](../tests/contract-playground/src/StateVariablesManipulation.sol#L9) - - ```solidity - int256 public simpleInt; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 10](../tests/contract-playground/src/StateVariablesManipulation.sol#L10) - - ```solidity - bool public simpleBool; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 11](../tests/contract-playground/src/StateVariablesManipulation.sol#L11) - - ```solidity - address public simpleAddress; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 12](../tests/contract-playground/src/StateVariablesManipulation.sol#L12) - - ```solidity - address payable public simplePayableAddress; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 13](../tests/contract-playground/src/StateVariablesManipulation.sol#L13) - - ```solidity - string public simpleString; - ``` - -- Found in src/StateVariablesManipulation.sol [Line: 14](../tests/contract-playground/src/StateVariablesManipulation.sol#L14) - - ```solidity - bytes public simpleBytes; - ``` - -- Found in src/TautologyOrContradiction.sol [Line: 6](../tests/contract-playground/src/TautologyOrContradiction.sol#L6) - - ```solidity - uint x; - ``` - -- Found in src/TautologyOrContradiction.sol [Line: 7](../tests/contract-playground/src/TautologyOrContradiction.sol#L7) - - ```solidity - uint256 y; - ``` - -- Found in src/UninitializedLocalVariables.sol [Line: 5](../tests/contract-playground/src/UninitializedLocalVariables.sol#L5) - - ```solidity - uint256 stateVarUint; - ``` - -- Found in src/UninitializedStateVariable.sol [Line: 7](../tests/contract-playground/src/UninitializedStateVariable.sol#L7) - - ```solidity - string public s_author; // BAD (because it's used without initializing) - ``` - -- Found in src/UninitializedStateVariable.sol [Line: 15](../tests/contract-playground/src/UninitializedStateVariable.sol#L15) - - ```solidity - address destination; // BAD - ``` - -- Found in src/UnusedStateVariables.sol [Line: 6](../tests/contract-playground/src/UnusedStateVariables.sol#L6) - - ```solidity - uint256 internal unusedUint256; - ``` - -- Found in src/UnusedStateVariables.sol [Line: 7](../tests/contract-playground/src/UnusedStateVariables.sol#L7) - - ```solidity - address internal unusedAddress; - ``` - -- Found in src/UnusedStateVariables.sol [Line: 8](../tests/contract-playground/src/UnusedStateVariables.sol#L8) - - ```solidity - bool private unusedBool; - ``` - -- Found in src/UnusedStateVariables.sol [Line: 9](../tests/contract-playground/src/UnusedStateVariables.sol#L9) - - ```solidity - string private unusedString; - ``` - -- Found in src/UnusedStateVariables.sol [Line: 12](../tests/contract-playground/src/UnusedStateVariables.sol#L12) - - ```solidity - bytes32 public usedBytes32; // External contracts may want to interact with it by calling it as a function - ``` - -- Found in src/WrongOrderOfLayout.sol [Line: 11](../tests/contract-playground/src/WrongOrderOfLayout.sol#L11) - - ```solidity - uint256 public multiplier; - ``` - -- Found in src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol [Line: 68](../tests/contract-playground/src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol#L68) - - ```solidity - address public owner; - ``` - -
- - - -## H-16: Incorrect use of caret operator on a non hexadcimal constant +## H-15: Incorrect use of caret operator on a non hexadcimal constant The caret operator is usually mistakenly thought of as an exponentiation operator but actually, it's a bitwise xor operator. @@ -1411,7 +1189,7 @@ The caret operator is usually mistakenly thought of as an exponentiation operato -## H-17: Yul block contains `return` function call. +## H-16: Yul block contains `return` function call. Remove this, as this causes execution to halt. Nothing after that call will execute, including code following the assembly block. @@ -1428,7 +1206,7 @@ Remove this, as this causes execution to halt. Nothing after that call will exec -## H-18: Shadowed State Variables in Inheritance Hierarchy +## H-17: Shadowed State Variables in Inheritance Hierarchy This vulnerability arises when a derived contract unintentionally shadows a state variable from a parent contract by declaring a variable with the same name. This can be misleading. To prevent this, ensure variable names are unique across the inheritance hierarchy or use proper visibility and scope controls. @@ -1445,7 +1223,7 @@ This vulnerability arises when a derived contract unintentionally shadows a stat -## H-19: Unchecked `bool success` value for send call. +## H-18: Unchecked `bool success` value for send call. The transaction `address(payable?).send(address)` may fail because of reasons like out-of-gas, invalid receipient address or revert from the recipient. Therefore, the boolean returned by this function call must be checked to be `true` in order to verify that the transaction was successful @@ -1462,7 +1240,7 @@ The transaction `address(payable?).send(address)` may fail because of reasons li -## H-20: Misused boolean with logical operators +## H-19: Misused boolean with logical operators The patterns `if (… || true)` and `if (.. && false)` will always evaluate to true and false respectively. @@ -1533,7 +1311,7 @@ The patterns `if (… || true)` and `if (.. && false)` will always evaluate to t -## H-21: Functions send eth away from contract but performs no checks on any address. +## H-20: Functions send eth away from contract but performs no checks on any address. Consider introducing checks for `msg.sender` to ensure the recipient of the money is as intended. @@ -1664,7 +1442,7 @@ Consider introducing checks for `msg.sender` to ensure the recipient of the mone -## H-22: Delegatecall made by the function without checks on any address. +## H-21: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -1705,7 +1483,7 @@ Introduce checks on the address -## H-23: Tautological comparison. +## H-22: Tautological comparison. The left hand side and the right hand side of the binary operation has the same value. This makes the condition always true or always false. @@ -1740,7 +1518,7 @@ The left hand side and the right hand side of the binary operation has the same -## H-24: RTLO character detected in file. \u{202e} +## H-23: RTLO character detected in file. \u{202e} Right to left override character may be misledaing and cause potential attacks by visually misordering method arguments! @@ -1757,7 +1535,7 @@ Right to left override character may be misledaing and cause potential attacks b -## H-25: Dangerous unary operator found in assignment. +## H-24: Dangerous unary operator found in assignment. Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in between. @@ -1780,7 +1558,7 @@ Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in -## H-26: Tautology or Contradiction in comparison. +## H-25: Tautology or Contradiction in comparison. The condition has been determined to be either always true or always false due to the integer range in which we're operating. @@ -1803,7 +1581,7 @@ The condition has been determined to be either always true or always false due t -## H-27: Dangerous strict equality checks on contract balances. +## H-26: Dangerous strict equality checks on contract balances. A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality. @@ -1832,7 +1610,7 @@ A contract's balance can be forcibly manipulated by another selfdestructing cont -## H-28: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` +## H-27: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` If you want to leverage signed arrays in storage by assigning a literal array with at least one negative number, then you mus use solidity version 0.5.10 or above. This is because of a bug in older compilers. @@ -1849,7 +1627,7 @@ If you want to leverage signed arrays in storage by assigning a literal array wi -## H-29: Weak Randomness +## H-28: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -1914,7 +1692,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-30: Usage of variable before declaration. +## H-29: Usage of variable before declaration. This is a bad practice that may lead to unintended consequences. Please declare the variable before using it. @@ -1931,7 +1709,7 @@ This is a bad practice that may lead to unintended consequences. Please declare -## H-31: Deletion from a nested mappping. +## H-30: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -1948,7 +1726,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-32: Potential use of `tx.origin` for authentication. +## H-31: Potential use of `tx.origin` for authentication. Using `tx.origin` may lead to problems when users are interacting via smart contract with your protocol. It is recommended to use `msg.sender` for authentication. @@ -1977,7 +1755,7 @@ Using `tx.origin` may lead to problems when users are interacting via smart cont -## H-33: Loop contains `msg.value`. +## H-32: Loop contains `msg.value`. Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. @@ -2012,7 +1790,7 @@ Provide an explicit array of amounts alongside the receivers array, and check th -## H-34: Contract locks Ether without a withdraw function. +## H-33: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. @@ -2083,7 +1861,7 @@ It appears that the contract includes a payable function to accept Ether but lac -## H-35: Incorrect ERC721 interface. +## H-34: Incorrect ERC721 interface. Incorrect return values for ERC721 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC721 functions. @@ -2142,7 +1920,7 @@ Incorrect return values for ERC721 functions. A contract compiled with Solidity -## H-36: Incorrect ERC20 interface. +## H-35: Incorrect ERC20 interface. Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions. @@ -2183,7 +1961,7 @@ Incorrect return values for ERC20 functions. A contract compiled with Solidity > -## H-37: Out of order retryable transactions. +## H-36: Out of order retryable transactions. Do not rely on the order or successful execution of retryable tickets. Functions like createRetryableTicket, outboundTransferCustomRefund, unsafeCreateRetryableTicket are free to be re-tried in any order if they fail in the first go. Since this operation happens off chain, the sequencer is in control of the @@ -2208,7 +1986,7 @@ Do not rely on the order or successful execution of retryable tickets. Functions -## H-38: Constant functions changing state +## H-37: Constant functions changing state Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct. @@ -2225,7 +2003,7 @@ Function is declared constant/view but it changes state. Ensure that the attribu -## H-39: Function selector collides with other functions +## H-38: Function selector collides with other functions Function selector collides with other functions. This may cause the solidity function dispatcher to invoke the wrong function if the functions happen to be included in the same contract through an inheritance hirearchy later down the line. It is recommended to rename this function or change its parameters. @@ -2250,7 +2028,7 @@ Function selector collides with other functions. This may cause the solidity fun -## H-40: Unchecked Low level calls +## H-39: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. diff --git a/reports/report.sarif b/reports/report.sarif index 9498df3e9..b8af79386 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -1516,400 +1516,6 @@ }, "ruleId": "dynamic-array-length-assignment" }, - { - "level": "warning", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/AssemblyExample.sol" - }, - "region": { - "byteLength": 1, - "byteOffset": 97 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/BuiltinSymbolShadow.sol" - }, - "region": { - "byteLength": 8, - "byteOffset": 92 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/ConstantFuncsAssembly.sol" - }, - "region": { - "byteLength": 20, - "byteOffset": 110 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/DelegateCallWithoutAddressCheck.sol" - }, - "region": { - "byteLength": 7, - "byteOffset": 337 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/InconsistentUints.sol" - }, - "region": { - "byteLength": 11, - "byteOffset": 197 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/InconsistentUints.sol" - }, - "region": { - "byteLength": 14, - "byteOffset": 233 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/IncorrectCaretOperator.sol" - }, - "region": { - "byteLength": 7, - "byteOffset": 355 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/IncorrectERC721.sol" - }, - "region": { - "byteLength": 11, - "byteOffset": 4076 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/LocalVariableShadow.sol" - }, - "region": { - "byteLength": 5, - "byteOffset": 129 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/LocalVariableShadow.sol" - }, - "region": { - "byteLength": 4, - "byteOffset": 532 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/PublicVariableReadInExternalContext.sol" - }, - "region": { - "byteLength": 26, - "byteOffset": 130 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/ReturnBomb.sol" - }, - "region": { - "byteLength": 7, - "byteOffset": 1623 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateShadowing.sol" - }, - "region": { - "byteLength": 13, - "byteOffset": 87 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariables.sol" - }, - "region": { - "byteLength": 19, - "byteOffset": 199 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariables.sol" - }, - "region": { - "byteLength": 20, - "byteOffset": 241 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariables.sol" - }, - "region": { - "byteLength": 18, - "byteOffset": 282 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 10, - "byteOffset": 184 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 9, - "byteOffset": 214 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 10, - "byteOffset": 241 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 13, - "byteOffset": 272 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 20, - "byteOffset": 314 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 12, - "byteOffset": 354 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/StateVariablesManipulation.sol" - }, - "region": { - "byteLength": 11, - "byteOffset": 385 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/TautologyOrContradiction.sol" - }, - "region": { - "byteLength": 6, - "byteOffset": 133 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/TautologyOrContradiction.sol" - }, - "region": { - "byteLength": 9, - "byteOffset": 145 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UninitializedLocalVariables.sol" - }, - "region": { - "byteLength": 12, - "byteOffset": 93 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UninitializedStateVariable.sol" - }, - "region": { - "byteLength": 8, - "byteOffset": 122 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UninitializedStateVariable.sol" - }, - "region": { - "byteLength": 11, - "byteOffset": 529 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UnusedStateVariables.sol" - }, - "region": { - "byteLength": 13, - "byteOffset": 147 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UnusedStateVariables.sol" - }, - "region": { - "byteLength": 13, - "byteOffset": 183 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UnusedStateVariables.sol" - }, - "region": { - "byteLength": 10, - "byteOffset": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UnusedStateVariables.sol" - }, - "region": { - "byteLength": 12, - "byteOffset": 246 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/UnusedStateVariables.sol" - }, - "region": { - "byteLength": 11, - "byteOffset": 314 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/WrongOrderOfLayout.sol" - }, - "region": { - "byteLength": 10, - "byteOffset": 257 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol" - }, - "region": { - "byteLength": 5, - "byteOffset": 1971 - } - } - } - ], - "message": { - "text": "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." - }, - "ruleId": "uninitialized-state-variable" - }, { "level": "warning", "locations": [ diff --git a/reports/templegold-report.md b/reports/templegold-report.md index dbdc68318..54eeebd0b 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -12,10 +12,9 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [H-2: Unprotected initializer](#h-2-unprotected-initializer) - [H-3: Unsafe Casting](#h-3-unsafe-casting) - [H-4: Contract Name Reused in Different Files](#h-4-contract-name-reused-in-different-files) - - [H-5: Uninitialized State Variables](#h-5-uninitialized-state-variables) - - [H-6: Weak Randomness](#h-6-weak-randomness) - - [H-7: Deletion from a nested mappping.](#h-7-deletion-from-a-nested-mappping) - - [H-8: Contract locks Ether without a withdraw function.](#h-8-contract-locks-ether-without-a-withdraw-function) + - [H-5: Weak Randomness](#h-5-weak-randomness) + - [H-6: Deletion from a nested mappping.](#h-6-deletion-from-a-nested-mappping) + - [H-7: Contract locks Ether without a withdraw function.](#h-7-contract-locks-ether-without-a-withdraw-function) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -197,7 +196,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 8 | +| High | 7 | | Low | 28 | @@ -337,60 +336,7 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-5: Uninitialized State Variables - -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. - -
7 Found Instances - - -- Found in contracts/amm/TempleUniswapV2Pair.sol [Line: 29](../tests/2024-07-templegold/protocol/contracts/amm/TempleUniswapV2Pair.sol#L29) - - ```solidity - uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event - ``` - -- Found in contracts/fakes/templegold/TempleGoldStakingMock.sol [Line: 39](../tests/2024-07-templegold/protocol/contracts/fakes/templegold/TempleGoldStakingMock.sol#L39) - - ```solidity - uint256 public rewardPerTokenStored; - ``` - -- Found in contracts/fakes/templegold/TempleGoldStakingMock.sol [Line: 47](../tests/2024-07-templegold/protocol/contracts/fakes/templegold/TempleGoldStakingMock.sol#L47) - - ```solidity - uint256 public periodFinish; - ``` - -- Found in contracts/fakes/templegold/TempleGoldStakingMock.sol [Line: 48](../tests/2024-07-templegold/protocol/contracts/fakes/templegold/TempleGoldStakingMock.sol#L48) - - ```solidity - uint256 public lastUpdateTime; - ``` - -- Found in contracts/templegold/TempleGoldStaking.sol [Line: 40](../tests/2024-07-templegold/protocol/contracts/templegold/TempleGoldStaking.sol#L40) - - ```solidity - uint256 public override rewardPerTokenStored; - ``` - -- Found in contracts/templegold/TempleGoldStaking.sol [Line: 45](../tests/2024-07-templegold/protocol/contracts/templegold/TempleGoldStaking.sol#L45) - - ```solidity - uint256 public override periodFinish; - ``` - -- Found in contracts/templegold/TempleGoldStaking.sol [Line: 47](../tests/2024-07-templegold/protocol/contracts/templegold/TempleGoldStaking.sol#L47) - - ```solidity - uint256 public override lastUpdateTime; - ``` - -
- - - -## H-6: Weak Randomness +## H-5: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -407,7 +353,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-7: Deletion from a nested mappping. +## H-6: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -424,7 +370,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-8: Contract locks Ether without a withdraw function. +## H-7: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. From f0c14b5fd059837e05b668b6884345dbfd174109 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 15:20:14 +0530 Subject: [PATCH 20/25] Don't report uninitialized state variable when there is `initializer` modifier (#771) --- .../detect/high/unprotected_init_function.rs | 76 ++++++++++++++++--- reports/ccip-functions-report.md | 42 +--------- reports/report.json | 46 ++++++----- reports/report.md | 28 ++++--- reports/report.sarif | 23 ++++-- reports/templegold-report.md | 64 +++------------- .../src/UnprotectedInitialize.sol | 11 ++- 7 files changed, 147 insertions(+), 143 deletions(-) diff --git a/aderyn_core/src/detect/high/unprotected_init_function.rs b/aderyn_core/src/detect/high/unprotected_init_function.rs index c67babc81..d52a2f204 100644 --- a/aderyn_core/src/detect/high/unprotected_init_function.rs +++ b/aderyn_core/src/detect/high/unprotected_init_function.rs @@ -3,8 +3,15 @@ use std::{collections::BTreeMap, error::Error}; use crate::{ ast::NodeID, capture, - context::{browser::ExtractIdentifiers, workspace_context::WorkspaceContext}, - detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + context::{ + browser::{ExtractIdentifiers, ExtractModifierInvocations, ExtractRevertStatements}, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers, + }, }; use eyre::Result; @@ -17,15 +24,16 @@ pub struct UnprotectedInitializerDetector { impl IssueDetector for UnprotectedInitializerDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - for function in context.function_definitions() { - if function.name.to_lowercase().contains("init") { - 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") { - capture!(self, context, function); - } + for func in helpers::get_implemented_external_and_public_functions(context) { + let callgraph = CallGraph::new(context, &[&func.into()], CallGraphDirection::Inward)?; + let mut tracker = UnprotectedInitializationTracker::default(); + callgraph.accept(context, &mut tracker)?; + + if func.name.starts_with("_init") || func.name.starts_with("init") { + if tracker.has_initializer_modifier || tracker.has_require_or_revert { + continue; } + capture!(self, context, func); } } @@ -53,6 +61,51 @@ impl IssueDetector for UnprotectedInitializerDetector { } } +#[derive(Default, Debug)] +struct UnprotectedInitializationTracker { + has_require_or_revert: bool, + has_initializer_modifier: bool, // devtooligan's suggestion +} + +impl CallGraphVisitor for UnprotectedInitializationTracker { + fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> { + // Check for revert(), require(), revert SomeError() + let has_req_or_revert_calls = ExtractIdentifiers::from(node) + .extracted + .into_iter() + .any(|x| x.name == "require" || x.name == "revert"); + + let has_revert_stmnts = !ExtractRevertStatements::from(node).extracted.is_empty(); + + if has_req_or_revert_calls || has_revert_stmnts { + self.has_require_or_revert = true; + } + + // Check if modifier name is "initialized" and assume it works + // This is done becauase often times initialized comes from openzeppelin and it is out of + // scope when running aderyn due to it being a library + + let modifier_invocations = ExtractModifierInvocations::from(node).extracted; + + for inv in modifier_invocations { + match inv.modifier_name { + crate::ast::IdentifierOrIdentifierPath::Identifier(n) => { + if n.name == "initializer" { + self.has_initializer_modifier = true; + } + } + crate::ast::IdentifierOrIdentifierPath::IdentifierPath(n) => { + if n.name == "initializer" { + self.has_initializer_modifier = true; + } + } + } + } + + Ok(()) + } +} + #[cfg(test)] mod unprotected_initializer { use serial_test::serial; @@ -69,8 +122,7 @@ mod unprotected_initializer { ); let mut detector = UnprotectedInitializerDetector::default(); - let found = detector.detect(&context).unwrap(); - // assert that the detector found an abi encode packed + let found = detector.detect(&context).unwrap(); // assert that the detector found an abi encode packed assert!(found); // println!("{:?}", detector.instances()); assert_eq!(detector.instances().len(), 1); diff --git a/reports/ccip-functions-report.md b/reports/ccip-functions-report.md index 41637b56f..063975477 100644 --- a/reports/ccip-functions-report.md +++ b/reports/ccip-functions-report.md @@ -8,8 +8,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Details](#files-details) - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - - [H-1: Unprotected initializer](#h-1-unprotected-initializer) - - [H-2: Contract Name Reused in Different Files](#h-2-contract-name-reused-in-different-files) + - [H-1: Contract Name Reused in Different Files](#h-1-contract-name-reused-in-different-files) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -104,48 +103,13 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 2 | +| High | 1 | | Low | 18 | # High Issues -## H-1: Unprotected initializer - -Consider protecting the initializer functions with modifiers. - -
4 Found Instances - - -- Found in src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol [Line: 91](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol#L91) - - ```solidity - function _initializeRequest( - ``` - -- Found in src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol [Line: 108](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol#L108) - - ```solidity - function _initializeRequestForInlineJavaScript(Request memory self, string memory javaScriptSource) internal pure { - ``` - -- Found in src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol [Line: 91](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol#L91) - - ```solidity - function initializeRequest( - ``` - -- Found in src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol [Line: 108](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol#L108) - - ```solidity - function initializeRequestForInlineJavaScript(Request memory self, string memory javaScriptSource) internal pure { - ``` - -
- - - -## H-2: Contract Name Reused in Different Files +## H-1: Contract Name Reused in Different Files When compiling contracts with certain development frameworks (for example: Truffle), having contracts with the same name across different files can lead to one being overwritten. diff --git a/reports/report.json b/reports/report.json index df85b688d..6dba3edff 100644 --- a/reports/report.json +++ b/reports/report.json @@ -1,7 +1,7 @@ { "files_summary": { "total_source_units": 112, - "total_sloc": 3954 + "total_sloc": 3957 }, "files_details": { "files_details": [ @@ -339,7 +339,7 @@ }, { "file_path": "src/UnprotectedInitialize.sol", - "n_sloc": 25 + "n_sloc": 28 }, { "file_path": "src/UnsafeERC721Mint.sol", @@ -549,9 +549,9 @@ "instances": [ { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 35, - "src": "820:33", - "src_char": "820:33" + "line_no": 37, + "src": "916:33", + "src_char": "916:33" } ] }, @@ -4398,9 +4398,9 @@ }, { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 13, - "src": "222:21", - "src_char": "222:21" + "line_no": 15, + "src": "320:21", + "src_char": "320:21" } ] }, @@ -4559,6 +4559,12 @@ "src": "915:65", "src_char": "915:65" }, + { + "contract_path": "src/UnprotectedInitialize.sol", + "line_no": 43, + "src": "1071:38", + "src_char": "1071:38" + }, { "contract_path": "src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol", "line_no": 11, @@ -6763,21 +6769,21 @@ }, { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 19, - "src": "354:22", - "src_char": "354:22" + "line_no": 21, + "src": "452:22", + "src_char": "452:22" }, { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 25, - "src": "522:20", - "src_char": "522:20" + "line_no": 27, + "src": "618:20", + "src_char": "618:20" }, { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 35, - "src": "820:33", - "src_char": "820:33" + "line_no": 37, + "src": "916:33", + "src_char": "916:33" }, { "contract_path": "src/UnusedError.sol", @@ -7022,9 +7028,9 @@ }, { "contract_path": "src/UnprotectedInitialize.sol", - "line_no": 7, - "src": "146:5", - "src_char": "146:5" + "line_no": 9, + "src": "244:5", + "src_char": "244:5" }, { "contract_path": "src/auditor_mode/ExternalCalls.sol", diff --git a/reports/report.md b/reports/report.md index cc8d293a9..65b0ca10a 100644 --- a/reports/report.md +++ b/reports/report.md @@ -104,7 +104,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 112 | -| Total nSLOC | 3954 | +| Total nSLOC | 3957 | ## Files Details @@ -194,7 +194,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | src/UncheckedSend.sol | 18 | | src/UninitializedLocalVariables.sol | 62 | | src/UninitializedStateVariable.sol | 29 | -| src/UnprotectedInitialize.sol | 25 | +| src/UnprotectedInitialize.sol | 28 | | src/UnsafeERC721Mint.sol | 18 | | src/UnusedError.sol | 19 | | src/UnusedImport.sol | 10 | @@ -223,7 +223,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** | **3954** | +| **Total** | **3957** | ## Issue Summary @@ -337,7 +337,7 @@ Consider protecting the initializer functions with modifiers.
1 Found Instances -- Found in src/UnprotectedInitialize.sol [Line: 35](../tests/contract-playground/src/UnprotectedInitialize.sol#L35) +- Found in src/UnprotectedInitialize.sol [Line: 37](../tests/contract-playground/src/UnprotectedInitialize.sol#L37) ```solidity function initializeWithoutModifierOrRevert() external { @@ -4370,7 +4370,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai modifier lockTheSwap { ``` -- Found in src/UnprotectedInitialize.sol [Line: 13](../tests/contract-playground/src/UnprotectedInitialize.sol#L13) +- Found in src/UnprotectedInitialize.sol [Line: 15](../tests/contract-playground/src/UnprotectedInitialize.sol#L15) ```solidity modifier firstTimeInitializing() { @@ -4384,7 +4384,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai Consider removing empty blocks. -
34 Found Instances +
35 Found Instances - Found in src/AderynIgnoreCustomDetectors.sol [Line: 7](../tests/contract-playground/src/AderynIgnoreCustomDetectors.sol#L7) @@ -4537,6 +4537,12 @@ Consider removing empty blocks. function doSomething(bool success) internal pure { ``` +- Found in src/UnprotectedInitialize.sol [Line: 43](../tests/contract-playground/src/UnprotectedInitialize.sol#L43) + + ```solidity + function initializeWithModifierNamedInitiliazer() external initializer { + ``` + - Found in src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol [Line: 11](../tests/contract-playground/src/auditor_mode/PublicFunctionsWithoutSenderCheck.sol#L11) ```solidity @@ -6848,19 +6854,19 @@ State variable changes in this function but no event is emitted. function revokeUser(address _user) external { ``` -- Found in src/UnprotectedInitialize.sol [Line: 19](../tests/contract-playground/src/UnprotectedInitialize.sol#L19) +- Found in src/UnprotectedInitialize.sol [Line: 21](../tests/contract-playground/src/UnprotectedInitialize.sol#L21) ```solidity - function initializeWithModifier() external firstTimeInitializing() { + function initializeWithModifier() external firstTimeInitializing { ``` -- Found in src/UnprotectedInitialize.sol [Line: 25](../tests/contract-playground/src/UnprotectedInitialize.sol#L25) +- Found in src/UnprotectedInitialize.sol [Line: 27](../tests/contract-playground/src/UnprotectedInitialize.sol#L27) ```solidity function initializeWithRevert() external { ``` -- Found in src/UnprotectedInitialize.sol [Line: 35](../tests/contract-playground/src/UnprotectedInitialize.sol#L35) +- Found in src/UnprotectedInitialize.sol [Line: 37](../tests/contract-playground/src/UnprotectedInitialize.sol#L37) ```solidity function initializeWithoutModifierOrRevert() external { @@ -7111,7 +7117,7 @@ State variables that are should be declared immutable to save gas. Add the `immu uint256 public myVar; // initialized in extension, hence not captured ``` -- Found in src/UnprotectedInitialize.sol [Line: 7](../tests/contract-playground/src/UnprotectedInitialize.sol#L7) +- Found in src/UnprotectedInitialize.sol [Line: 9](../tests/contract-playground/src/UnprotectedInitialize.sol#L9) ```solidity address private owner; diff --git a/reports/report.sarif b/reports/report.sarif index b8af79386..1ae344c2b 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -151,7 +151,7 @@ }, "region": { "byteLength": 33, - "byteOffset": 820 + "byteOffset": 916 } } } @@ -7007,7 +7007,7 @@ }, "region": { "byteLength": 21, - "byteOffset": 222 + "byteOffset": 320 } } } @@ -7295,6 +7295,17 @@ } } }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/UnprotectedInitialize.sol" + }, + "region": { + "byteLength": 38, + "byteOffset": 1071 + } + } + }, { "physicalLocation": { "artifactLocation": { @@ -11239,7 +11250,7 @@ }, "region": { "byteLength": 22, - "byteOffset": 354 + "byteOffset": 452 } } }, @@ -11250,7 +11261,7 @@ }, "region": { "byteLength": 20, - "byteOffset": 522 + "byteOffset": 618 } } }, @@ -11261,7 +11272,7 @@ }, "region": { "byteLength": 33, - "byteOffset": 820 + "byteOffset": 916 } } }, @@ -11710,7 +11721,7 @@ }, "region": { "byteLength": 5, - "byteOffset": 146 + "byteOffset": 244 } } }, diff --git a/reports/templegold-report.md b/reports/templegold-report.md index 54eeebd0b..8309c215b 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -9,12 +9,11 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - [H-1: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-1-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) - - [H-2: Unprotected initializer](#h-2-unprotected-initializer) - - [H-3: Unsafe Casting](#h-3-unsafe-casting) - - [H-4: Contract Name Reused in Different Files](#h-4-contract-name-reused-in-different-files) - - [H-5: Weak Randomness](#h-5-weak-randomness) - - [H-6: Deletion from a nested mappping.](#h-6-deletion-from-a-nested-mappping) - - [H-7: Contract locks Ether without a withdraw function.](#h-7-contract-locks-ether-without-a-withdraw-function) + - [H-2: Unsafe Casting](#h-2-unsafe-casting) + - [H-3: Contract Name Reused in Different Files](#h-3-contract-name-reused-in-different-files) + - [H-4: Weak Randomness](#h-4-weak-randomness) + - [H-5: Deletion from a nested mappping.](#h-5-deletion-from-a-nested-mappping) + - [H-6: Contract locks Ether without a withdraw function.](#h-6-contract-locks-ether-without-a-withdraw-function) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -196,7 +195,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 7 | +| High | 6 | | Low | 28 | @@ -237,48 +236,7 @@ Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) ca -## H-2: Unprotected initializer - -Consider protecting the initializer functions with modifiers. - -
5 Found Instances - - -- Found in contracts/amo/test/external/IStashRewards.sol [Line: 9](../tests/2024-07-templegold/protocol/contracts/amo/test/external/IStashRewards.sol#L9) - - ```solidity - function initialize(uint256 _pid, address _operator, address _staker, address _gauge, address _rewardFactory) external; - ``` - -- Found in contracts/util/ABDKMathQuad.sol [Line: 422](../tests/2024-07-templegold/protocol/contracts/util/ABDKMathQuad.sol#L422) - - ```solidity - function isInfinity (bytes16 x) internal pure returns (bool) { - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 597](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L597) - - ```solidity - function _initBaseCache(BaseCache memory _baseCache) private view returns (bool dirty) { - ``` - -- Found in contracts/v2/TempleDebtToken.sol [Line: 672](../tests/2024-07-templegold/protocol/contracts/v2/TempleDebtToken.sol#L672) - - ```solidity - function _initDebtorCache( - ``` - -- Found in contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol [Line: 618](../tests/2024-07-templegold/protocol/contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol#L618) - - ```solidity - function _initDebtTokenCache(DebtTokenCache memory _cache) private view returns (bool dirty) { - ``` - -
- - - -## H-3: Unsafe Casting +## H-2: Unsafe Casting Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior.When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type,leading to truncation and loss of significant digits. Use OpenZeppelin's SafeCast library to safely downcast integers. @@ -301,7 +259,7 @@ Downcasting int/uints in Solidity can be unsafe due to the potential for data lo -## H-4: Contract Name Reused in Different Files +## H-3: Contract Name Reused in Different Files When compiling contracts with certain development frameworks (for example: Truffle), having contracts with the same name across different files can lead to one being overwritten. @@ -336,7 +294,7 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-5: Weak Randomness +## H-4: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -353,7 +311,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-6: Deletion from a nested mappping. +## H-5: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -370,7 +328,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-7: Contract locks Ether without a withdraw function. +## H-6: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. diff --git a/tests/contract-playground/src/UnprotectedInitialize.sol b/tests/contract-playground/src/UnprotectedInitialize.sol index 7f72a33fd..8bbebc8b6 100644 --- a/tests/contract-playground/src/UnprotectedInitialize.sol +++ b/tests/contract-playground/src/UnprotectedInitialize.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: No License +import "../lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"; + pragma solidity 0.8.19; -contract InitializedContract { +contract InitializedContract is Initializable { bool private initialized; address private owner; @@ -16,7 +18,7 @@ contract InitializedContract { } // GOOD - function initializeWithModifier() external firstTimeInitializing() { + function initializeWithModifier() external firstTimeInitializing { initialized = true; // Additional initialization logic here } @@ -36,4 +38,9 @@ contract InitializedContract { initialized = true; // Additional initialization logic here } + + // GOOD + function initializeWithModifierNamedInitiliazer() external initializer { + // Additional initialization logic here + } } From ab2eecc1e42d6bc814296ae072efe24b7892f139 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 22:54:25 +0530 Subject: [PATCH 21/25] Fix: Lower severity for Delegate Call Detector (#772) --- aderyn_core/src/detect/high/mod.rs | 2 - .../{high => low}/delegate_call_in_loop.rs | 20 +- aderyn_core/src/detect/low/mod.rs | 2 + .../adhoc-sol-files-highs-only-report.json | 16 +- reports/adhoc-sol-files-report.md | 126 +++--- reports/hardhat-playground-report.md | 104 ++--- reports/report.json | 30 +- reports/report.md | 380 +++++++++--------- reports/report.sarif | 40 +- 9 files changed, 348 insertions(+), 372 deletions(-) rename aderyn_core/src/detect/{high => low}/delegate_call_in_loop.rs (85%) diff --git a/aderyn_core/src/detect/high/mod.rs b/aderyn_core/src/detect/high/mod.rs index e47edf407..5ea1b424e 100644 --- a/aderyn_core/src/detect/high/mod.rs +++ b/aderyn_core/src/detect/high/mod.rs @@ -4,7 +4,6 @@ pub(crate) mod const_func_change_state; pub(crate) mod contract_locks_ether; pub(crate) mod dangerous_strict_equality_balance; pub(crate) mod dangerous_unary_operator; -pub(crate) mod delegate_call_in_loop; pub(crate) mod delegate_call_no_address_check; pub(crate) mod deletion_nested_mapping; pub(crate) mod dynamic_array_length_assignment; @@ -44,7 +43,6 @@ pub use const_func_change_state::ConstantFunctionChangingStateDetector; pub use contract_locks_ether::ContractLocksEtherDetector; pub use dangerous_strict_equality_balance::DangerousStrictEqualityOnBalanceDetector; pub use dangerous_unary_operator::DangerousUnaryOperatorDetector; -pub use delegate_call_in_loop::DelegateCallInLoopDetector; pub use delegate_call_no_address_check::DelegateCallOnUncheckedAddressDetector; pub use deletion_nested_mapping::DeletionNestedMappingDetector; pub use dynamic_array_length_assignment::DynamicArrayLengthAssignmentDetector; diff --git a/aderyn_core/src/detect/high/delegate_call_in_loop.rs b/aderyn_core/src/detect/low/delegate_call_in_loop.rs similarity index 85% rename from aderyn_core/src/detect/high/delegate_call_in_loop.rs rename to aderyn_core/src/detect/low/delegate_call_in_loop.rs index 808a85edc..7915e673a 100644 --- a/aderyn_core/src/detect/high/delegate_call_in_loop.rs +++ b/aderyn_core/src/detect/low/delegate_call_in_loop.rs @@ -50,15 +50,15 @@ impl IssueDetector for DelegateCallInLoopDetector { } fn severity(&self) -> IssueSeverity { - IssueSeverity::High + IssueSeverity::Low } fn title(&self) -> String { - String::from("Using `delegatecall` in loop") + String::from("Using `delegatecall` in loop may consume excessive gas") } fn description(&self) -> String { - String::from("When calling `delegatecall` the same `msg.value` amount will be accredited multiple times.") + String::from("Using `delegatecall` in loop may consume excessive gas") } fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> { @@ -97,9 +97,8 @@ impl CallGraphVisitor for DelegateCallTracker { mod delegate_call_in_loop_detector_tests { use serial_test::serial; - use crate::detect::detector::IssueDetector; - use super::DelegateCallInLoopDetector; + use crate::detect::detector::IssueDetector; #[test] #[serial] @@ -115,15 +114,6 @@ 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 the title is correct - assert_eq!(detector.title(), String::from("Using `delegatecall` in loop")); - // assert the description is correct - assert_eq!( - detector.description(), - String::from( - "When calling `delegatecall` the same `msg.value` amount will be accredited multiple times." - ) - ); + 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 0304305a7..73b9e2d3c 100644 --- a/aderyn_core/src/detect/low/mod.rs +++ b/aderyn_core/src/detect/low/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod constant_funcs_assembly; pub(crate) mod contracts_with_todos; pub(crate) mod costly_operations_inside_loops; pub(crate) mod dead_code; +pub(crate) mod delegate_call_in_loop; pub(crate) mod deprecated_oz_functions; pub(crate) mod division_before_multiplication; pub(crate) mod ecrecover; @@ -56,6 +57,7 @@ pub use constant_funcs_assembly::ConstantFunctionContainsAssemblyDetector; pub use contracts_with_todos::ContractsWithTodosDetector; pub use costly_operations_inside_loops::CostlyOperationsInsideLoopsDetector; pub use dead_code::DeadCodeDetector; +pub use delegate_call_in_loop::DelegateCallInLoopDetector; pub use deprecated_oz_functions::DeprecatedOZFunctionsDetector; pub use division_before_multiplication::DivisionBeforeMultiplicationDetector; pub use ecrecover::EcrecoverDetector; diff --git a/reports/adhoc-sol-files-highs-only-report.json b/reports/adhoc-sol-files-highs-only-report.json index 6bc3b4bde..20dff8d63 100644 --- a/reports/adhoc-sol-files-highs-only-report.json +++ b/reports/adhoc-sol-files-highs-only-report.json @@ -88,24 +88,11 @@ ] }, "issue_count": { - "high": 3, + "high": 2, "low": 0 }, "high_issues": { "issues": [ - { - "title": "Using `delegatecall` in loop", - "description": "When calling `delegatecall` the same `msg.value` amount will be accredited multiple times.", - "detector_name": "delegate-call-in-loop", - "instances": [ - { - "contract_path": "inheritance/ExtendedInheritance.sol", - "line_no": 15, - "src": "474:96", - "src_char": "474:96" - } - ] - }, { "title": "Delegatecall made by the function without checks on any address.", "description": "Introduce checks on the address", @@ -138,7 +125,6 @@ "issues": [] }, "detectors_used": [ - "delegate-call-in-loop", "hash-collision-due-to-abi-encode-packed", "arbitrary-transfer-from", "unprotected-initializer", diff --git a/reports/adhoc-sol-files-report.md b/reports/adhoc-sol-files-report.md index db45e4445..43a521cab 100644 --- a/reports/adhoc-sol-files-report.md +++ b/reports/adhoc-sol-files-report.md @@ -8,32 +8,32 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Details](#files-details) - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - - [H-1: Using `delegatecall` in loop](#h-1-using-delegatecall-in-loop) - - [H-2: Delegatecall made by the function without checks on any address.](#h-2-delegatecall-made-by-the-function-without-checks-on-any-address) - - [H-3: Unchecked Low level calls](#h-3-unchecked-low-level-calls) + - [H-1: Delegatecall made by the function without checks on any address.](#h-1-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-2: Unchecked Low level calls](#h-2-unchecked-low-level-calls) - [Low Issues](#low-issues) - - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) - - [L-3: Solidity pragma should be specific, not wide](#l-3-solidity-pragma-should-be-specific-not-wide) - - [L-4: Missing checks for `address(0)` when assigning values to address state variables](#l-4-missing-checks-for-address0-when-assigning-values-to-address-state-variables) - - [L-5: `public` functions not used internally could be marked `external`](#l-5-public-functions-not-used-internally-could-be-marked-external) - - [L-6: Define and use `constant` variables instead of using literals](#l-6-define-and-use-constant-variables-instead-of-using-literals) - - [L-7: Event is missing `indexed` fields](#l-7-event-is-missing-indexed-fields) - - [L-8: Empty `require()` / `revert()` statements](#l-8-empty-require--revert-statements) - - [L-9: PUSH0 is not supported by all chains](#l-9-push0-is-not-supported-by-all-chains) - - [L-10: Modifiers invoked only once can be shoe-horned into the function](#l-10-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) - - [L-11: Empty Block](#l-11-empty-block) - - [L-12: Large literal values multiples of 10000 can be replaced with scientific notation](#l-12-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) - - [L-13: Internal functions called only once can be inlined](#l-13-internal-functions-called-only-once-can-be-inlined) - - [L-14: Contract still has TODOs](#l-14-contract-still-has-todos) - - [L-15: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-15-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) - - [L-16: Unused Custom Error](#l-16-unused-custom-error) - - [L-17: Potentially unused `private` / `internal` state variables found.](#l-17-potentially-unused-private--internal-state-variables-found) - - [L-18: Dead Code](#l-18-dead-code) - - [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) + - [L-1: Using `delegatecall` in loop may consume excessive gas](#l-1-using-delegatecall-in-loop-may-consume-excessive-gas) + - [L-2: Centralization Risk for trusted owners](#l-2-centralization-risk-for-trusted-owners) + - [L-3: `ecrecover` is susceptible to signature malleability](#l-3-ecrecover-is-susceptible-to-signature-malleability) + - [L-4: Solidity pragma should be specific, not wide](#l-4-solidity-pragma-should-be-specific-not-wide) + - [L-5: Missing checks for `address(0)` when assigning values to address state variables](#l-5-missing-checks-for-address0-when-assigning-values-to-address-state-variables) + - [L-6: `public` functions not used internally could be marked `external`](#l-6-public-functions-not-used-internally-could-be-marked-external) + - [L-7: Define and use `constant` variables instead of using literals](#l-7-define-and-use-constant-variables-instead-of-using-literals) + - [L-8: Event is missing `indexed` fields](#l-8-event-is-missing-indexed-fields) + - [L-9: Empty `require()` / `revert()` statements](#l-9-empty-require--revert-statements) + - [L-10: PUSH0 is not supported by all chains](#l-10-push0-is-not-supported-by-all-chains) + - [L-11: Modifiers invoked only once can be shoe-horned into the function](#l-11-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) + - [L-12: Empty Block](#l-12-empty-block) + - [L-13: Large literal values multiples of 10000 can be replaced with scientific notation](#l-13-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) + - [L-14: Internal functions called only once can be inlined](#l-14-internal-functions-called-only-once-can-be-inlined) + - [L-15: Contract still has TODOs](#l-15-contract-still-has-todos) + - [L-16: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-16-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) + - [L-17: Unused Custom Error](#l-17-unused-custom-error) + - [L-18: Potentially unused `private` / `internal` state variables found.](#l-18-potentially-unused-private--internal-state-variables-found) + - [L-19: Dead Code](#l-19-dead-code) + - [L-20: Unused Imports](#l-20-unused-imports) + - [L-21: State variable could be declared constant](#l-21-state-variable-could-be-declared-constant) + - [L-22: State variable changes but no event is emitted.](#l-22-state-variable-changes-but-no-event-is-emitted) + - [L-23: State variable could be declared immutable](#l-23-state-variable-could-be-declared-immutable) # Summary @@ -77,66 +77,66 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 3 | -| Low | 22 | +| High | 2 | +| Low | 23 | # High Issues -## H-1: Using `delegatecall` in loop +## H-1: Delegatecall made by the function without checks on any address. -When calling `delegatecall` the same `msg.value` amount will be accredited multiple times. +Introduce checks on the address
1 Found Instances -- Found in inheritance/ExtendedInheritance.sol [Line: 15](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L15) +- Found in inheritance/ExtendedInheritance.sol [Line: 14](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L14) ```solidity - for (uint256 i = 0; i < 3; i++) { + function doSomethingElse(address target) external { ```
-## H-2: Delegatecall made by the function without checks on any address. +## H-2: Unchecked Low level calls -Introduce checks on the address +The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged.
1 Found Instances -- Found in inheritance/ExtendedInheritance.sol [Line: 14](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L14) +- Found in inheritance/ExtendedInheritance.sol [Line: 16](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L16) ```solidity - function doSomethingElse(address target) external { + target.delegatecall(abi.encodeWithSignature("doSomething(uint256)", i)); ```
-## H-3: Unchecked Low level calls +# Low Issues -The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. +## L-1: Using `delegatecall` in loop may consume excessive gas + +Using `delegatecall` in loop may consume excessive gas
1 Found Instances -- Found in inheritance/ExtendedInheritance.sol [Line: 16](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L16) +- Found in inheritance/ExtendedInheritance.sol [Line: 15](../tests/adhoc-sol-files/inheritance/ExtendedInheritance.sol#L15) ```solidity - target.delegatecall(abi.encodeWithSignature("doSomething(uint256)", i)); + for (uint256 i = 0; i < 3; i++) { ```
-# Low Issues - -## L-1: Centralization Risk for trusted owners +## L-2: Centralization Risk for trusted owners Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds. @@ -153,7 +153,7 @@ Contracts have owners with privileged rights to perform admin tasks and need to -## L-2: `ecrecover` is susceptible to signature malleability +## L-3: `ecrecover` is susceptible to signature malleability 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. @@ -170,7 +170,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th -## L-3: Solidity pragma should be specific, not wide +## L-4: Solidity pragma should be specific, not wide 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;` @@ -277,7 +277,7 @@ Consider using a specific version of Solidity in your contracts instead of a wid -## L-4: Missing checks for `address(0)` when assigning values to address state variables +## L-5: Missing checks for `address(0)` when assigning values to address state variables Check for `address(0)` when assigning values to address state variables. @@ -294,7 +294,7 @@ Check for `address(0)` when assigning values to address state variables. -## L-5: `public` functions not used internally could be marked `external` +## L-6: `public` functions not used internally could be marked `external` Instead of marking a function as `public`, consider marking it as `external` if it is not used internally. @@ -341,7 +341,7 @@ Instead of marking a function as `public`, consider marking it as `external` if -## L-6: Define and use `constant` variables instead of using literals +## L-7: Define and use `constant` variables instead of using literals If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract. @@ -388,7 +388,7 @@ If the same constant literal value is used multiple times, create a constant sta -## L-7: Event is missing `indexed` fields +## L-8: Event is missing `indexed` fields Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed. @@ -411,7 +411,7 @@ Index event fields make the field more quickly accessible to off-chain tools tha -## L-8: Empty `require()` / `revert()` statements +## L-9: Empty `require()` / `revert()` statements Use descriptive reason strings or custom errors for revert paths. @@ -428,7 +428,7 @@ Use descriptive reason strings or custom errors for revert paths. -## L-9: PUSH0 is not supported by all chains +## L-10: PUSH0 is not supported by all chains 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. @@ -499,7 +499,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-10: Modifiers invoked only once can be shoe-horned into the function +## L-11: Modifiers invoked only once can be shoe-horned into the function @@ -528,7 +528,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-11: Empty Block +## L-12: Empty Block Consider removing empty blocks. @@ -551,7 +551,7 @@ Consider removing empty blocks. -## L-12: Large literal values multiples of 10000 can be replaced with scientific notation +## L-13: Large literal values multiples of 10000 can be replaced with scientific notation Use `e` notation, for example: `1e18`, instead of its full numeric value. @@ -568,7 +568,7 @@ Use `e` notation, for example: `1e18`, instead of its full numeric value. -## L-13: Internal functions called only once can be inlined +## L-14: Internal functions called only once can be inlined 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. @@ -585,7 +585,7 @@ Instead of separating the logic into a separate function, consider inlining the -## L-14: Contract still has TODOs +## L-15: Contract still has TODOs Contract contains comments with TODOS @@ -602,7 +602,7 @@ Contract contains comments with TODOS -## L-15: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). +## L-16: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). Consider keeping the naming convention consistent in a given contract. Explicit size declarations are preferred (uint256, int256) over implicit ones (uint, int) to avoid confusion. @@ -655,7 +655,7 @@ Consider keeping the naming convention consistent in a given contract. Explicit -## L-16: Unused Custom Error +## L-17: Unused Custom Error it is recommended that the definition be removed when custom error is unused @@ -672,7 +672,7 @@ it is recommended that the definition be removed when custom error is unused -## L-17: Potentially unused `private` / `internal` state variables found. +## L-18: Potentially unused `private` / `internal` state variables found. State variable appears to be unused. No analysis has been performed to see if any inilne assembly references it. So if that's not the case, consider removing this unused variable. @@ -725,7 +725,7 @@ State variable appears to be unused. No analysis has been performed to see if an -## L-18: Dead Code +## L-19: Dead Code Functions that are not used. Consider removing them. @@ -748,7 +748,7 @@ Functions that are not used. Consider removing them. -## L-19: Unused Imports +## L-20: Unused Imports Redundant import statement. Consider removing it. @@ -765,7 +765,7 @@ Redundant import statement. Consider removing it. -## L-20: State variable could be declared constant +## L-21: State variable could be declared constant State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change. @@ -818,7 +818,7 @@ State variables that are not updated following deployment should be declared con -## L-21: State variable changes but no event is emitted. +## L-22: State variable changes but no event is emitted. State variable changes in this function but no event is emitted. @@ -895,7 +895,7 @@ State variable changes in this function but no event is emitted. -## L-22: State variable could be declared immutable +## L-23: 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 diff --git a/reports/hardhat-playground-report.md b/reports/hardhat-playground-report.md index 1d37df154..edb360e28 100644 --- a/reports/hardhat-playground-report.md +++ b/reports/hardhat-playground-report.md @@ -8,24 +8,24 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Details](#files-details) - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - - [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: 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) + - [H-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`](#h-1-abiencodepacked-should-not-be-used-with-dynamic-types-when-passing-the-result-to-a-hash-function-such-as-keccak256) + - [H-2: Delegatecall made by the function without checks on any address.](#h-2-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-3: Unchecked Low level calls](#h-3-unchecked-low-level-calls) - [Low Issues](#low-issues) - - [L-1: `ecrecover` is susceptible to signature malleability](#l-1-ecrecover-is-susceptible-to-signature-malleability) - - [L-2: Unsafe ERC20 Operations should not be used](#l-2-unsafe-erc20-operations-should-not-be-used) - - [L-3: Solidity pragma should be specific, not wide](#l-3-solidity-pragma-should-be-specific-not-wide) - - [L-4: Missing checks for `address(0)` when assigning values to address state variables](#l-4-missing-checks-for-address0-when-assigning-values-to-address-state-variables) - - [L-5: `public` functions not used internally could be marked `external`](#l-5-public-functions-not-used-internally-could-be-marked-external) - - [L-6: Event is missing `indexed` fields](#l-6-event-is-missing-indexed-fields) - - [L-7: PUSH0 is not supported by all chains](#l-7-push0-is-not-supported-by-all-chains) - - [L-8: Contract still has TODOs](#l-8-contract-still-has-todos) - - [L-9: Potentially unused `private` / `internal` state variables found.](#l-9-potentially-unused-private--internal-state-variables-found) - - [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) + - [L-1: Using `delegatecall` in loop may consume excessive gas](#l-1-using-delegatecall-in-loop-may-consume-excessive-gas) + - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) + - [L-3: Unsafe ERC20 Operations should not be used](#l-3-unsafe-erc20-operations-should-not-be-used) + - [L-4: Solidity pragma should be specific, not wide](#l-4-solidity-pragma-should-be-specific-not-wide) + - [L-5: Missing checks for `address(0)` when assigning values to address state variables](#l-5-missing-checks-for-address0-when-assigning-values-to-address-state-variables) + - [L-6: `public` functions not used internally could be marked `external`](#l-6-public-functions-not-used-internally-could-be-marked-external) + - [L-7: Event is missing `indexed` fields](#l-7-event-is-missing-indexed-fields) + - [L-8: PUSH0 is not supported by all chains](#l-8-push0-is-not-supported-by-all-chains) + - [L-9: Contract still has TODOs](#l-9-contract-still-has-todos) + - [L-10: Potentially unused `private` / `internal` state variables found.](#l-10-potentially-unused-private--internal-state-variables-found) + - [L-11: Unused Imports](#l-11-unused-imports) + - [L-12: State variable could be declared constant](#l-12-state-variable-could-be-declared-constant) + - [L-13: State variable changes but no event is emitted.](#l-13-state-variable-changes-but-no-event-is-emitted) + - [L-14: State variable could be declared immutable](#l-14-state-variable-could-be-declared-immutable) # Summary @@ -56,30 +56,13 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 4 | -| Low | 13 | +| High | 3 | +| Low | 14 | # High Issues -## H-1: Using `delegatecall` in loop - -When calling `delegatecall` the same `msg.value` amount will be accredited multiple times. - -
1 Found Instances - - -- Found in contracts/ExtendedInheritance.sol [Line: 15](../tests/hardhat-js-playground/contracts/ExtendedInheritance.sol#L15) - - ```solidity - for (uint256 i = 0; i < 3; i++) { - ``` - -
- - - -## H-2: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()` +## H-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()` 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). If all arguments are strings and or bytes, `bytes.concat()` should be used instead. @@ -109,7 +92,7 @@ If all arguments are strings and or bytes, `bytes.concat()` should be used inste -## H-3: Delegatecall made by the function without checks on any address. +## H-2: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -126,7 +109,7 @@ Introduce checks on the address -## H-4: Unchecked Low level calls +## H-3: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. @@ -145,7 +128,24 @@ The return value of the low-level call is not checked, so if the call fails, the # Low Issues -## L-1: `ecrecover` is susceptible to signature malleability +## L-1: Using `delegatecall` in loop may consume excessive gas + +Using `delegatecall` in loop may consume excessive gas + +
1 Found Instances + + +- Found in contracts/ExtendedInheritance.sol [Line: 15](../tests/hardhat-js-playground/contracts/ExtendedInheritance.sol#L15) + + ```solidity + for (uint256 i = 0; i < 3; i++) { + ``` + +
+ + + +## L-2: `ecrecover` is susceptible to signature malleability 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. @@ -162,7 +162,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th -## L-2: Unsafe ERC20 Operations should not be used +## L-3: Unsafe ERC20 Operations should not be used ERC20 functions may not behave as expected. For example: return values are not always meaningful. It is recommended to use OpenZeppelin's SafeERC20 library. @@ -179,7 +179,7 @@ ERC20 functions may not behave as expected. For example: return values are not a -## L-3: Solidity pragma should be specific, not wide +## L-4: Solidity pragma should be specific, not wide 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;` @@ -214,7 +214,7 @@ Consider using a specific version of Solidity in your contracts instead of a wid -## L-4: Missing checks for `address(0)` when assigning values to address state variables +## L-5: Missing checks for `address(0)` when assigning values to address state variables Check for `address(0)` when assigning values to address state variables. @@ -231,7 +231,7 @@ Check for `address(0)` when assigning values to address state variables. -## L-5: `public` functions not used internally could be marked `external` +## L-6: `public` functions not used internally could be marked `external` Instead of marking a function as `public`, consider marking it as `external` if it is not used internally. @@ -284,7 +284,7 @@ Instead of marking a function as `public`, consider marking it as `external` if -## L-6: Event is missing `indexed` fields +## L-7: Event is missing `indexed` fields Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed. @@ -313,7 +313,7 @@ Index event fields make the field more quickly accessible to off-chain tools tha -## L-7: PUSH0 is not supported by all chains +## L-8: PUSH0 is not supported by all chains 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. @@ -366,7 +366,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-8: Contract still has TODOs +## L-9: Contract still has TODOs Contract contains comments with TODOS @@ -383,7 +383,7 @@ Contract contains comments with TODOS -## L-9: Potentially unused `private` / `internal` state variables found. +## L-10: Potentially unused `private` / `internal` state variables found. State variable appears to be unused. No analysis has been performed to see if any inilne assembly references it. So if that's not the case, consider removing this unused variable. @@ -430,7 +430,7 @@ State variable appears to be unused. No analysis has been performed to see if an -## L-10: Unused Imports +## L-11: Unused Imports Redundant import statement. Consider removing it. @@ -447,7 +447,7 @@ Redundant import statement. Consider removing it. -## L-11: State variable could be declared constant +## L-12: State variable could be declared constant State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change. @@ -476,7 +476,7 @@ State variables that are not updated following deployment should be declared con -## L-12: State variable changes but no event is emitted. +## L-13: State variable changes but no event is emitted. State variable changes in this function but no event is emitted. @@ -547,7 +547,7 @@ State variable changes in this function but no event is emitted. -## L-13: State variable could be declared immutable +## L-14: 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 diff --git a/reports/report.json b/reports/report.json index 6dba3edff..2c49f44b9 100644 --- a/reports/report.json +++ b/reports/report.json @@ -456,24 +456,11 @@ ] }, "issue_count": { - "high": 39, - "low": 47 + "high": 38, + "low": 48 }, "high_issues": { "issues": [ - { - "title": "Using `delegatecall` in loop", - "description": "When calling `delegatecall` the same `msg.value` amount will be accredited multiple times.", - "detector_name": "delegate-call-in-loop", - "instances": [ - { - "contract_path": "src/inheritance/ExtendedInheritance.sol", - "line_no": 15, - "src": "474:96", - "src_char": "474:96" - } - ] - }, { "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.", @@ -2192,6 +2179,19 @@ }, "low_issues": { "issues": [ + { + "title": "Using `delegatecall` in loop may consume excessive gas", + "description": "Using `delegatecall` in loop may consume excessive gas", + "detector_name": "delegate-call-in-loop", + "instances": [ + { + "contract_path": "src/inheritance/ExtendedInheritance.sol", + "line_no": 15, + "src": "474:96", + "src_char": "474:96" + } + ] + }, { "title": "Centralization Risk for trusted owners", "description": "Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.", diff --git a/reports/report.md b/reports/report.md index 65b0ca10a..3a1567b62 100644 --- a/reports/report.md +++ b/reports/report.md @@ -8,93 +8,93 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Details](#files-details) - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - - [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: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-3-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) - - [H-4: Unprotected initializer](#h-4-unprotected-initializer) - - [H-5: Unsafe Casting](#h-5-unsafe-casting) - - [H-6: EnumerableSet.remove in loop corrupts the set order.](#h-6-enumerablesetremove-in-loop-corrupts-the-set-order) - - [H-7: Experimental ABI Encoder](#h-7-experimental-abi-encoder) - - [H-8: Incorrect Assembly Shift Parameter Order](#h-8-incorrect-assembly-shift-parameter-order) - - [H-9: Storage Array Edited with Memory](#h-9-storage-array-edited-with-memory) - - [H-10: Contract Has Multiple Constructors](#h-10-contract-has-multiple-constructors) - - [H-11: Contract Name Reused in Different Files](#h-11-contract-name-reused-in-different-files) - - [H-12: Nested Structs in Mappings pre-0.5.0](#h-12-nested-structs-in-mappings-pre-050) - - [H-13: Depracated EVM Instruction for `selfdestruct` should not be used.](#h-13-depracated-evm-instruction-for-selfdestruct-should-not-be-used) - - [H-14: Array length value has a direct assignment.](#h-14-array-length-value-has-a-direct-assignment) - - [H-15: Incorrect use of caret operator on a non hexadcimal constant](#h-15-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) - - [H-16: Yul block contains `return` function call.](#h-16-yul-block-contains-return-function-call) - - [H-17: Shadowed State Variables in Inheritance Hierarchy](#h-17-shadowed-state-variables-in-inheritance-hierarchy) - - [H-18: Unchecked `bool success` value for send call.](#h-18-unchecked-bool-success-value-for-send-call) - - [H-19: Misused boolean with logical operators](#h-19-misused-boolean-with-logical-operators) - - [H-20: Functions send eth away from contract but performs no checks on any address.](#h-20-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) - - [H-21: Delegatecall made by the function without checks on any address.](#h-21-delegatecall-made-by-the-function-without-checks-on-any-address) - - [H-22: Tautological comparison.](#h-22-tautological-comparison) - - [H-23: RTLO character detected in file. \u{202e}](#h-23-rtlo-character-detected-in-file-u202e) - - [H-24: Dangerous unary operator found in assignment.](#h-24-dangerous-unary-operator-found-in-assignment) - - [H-25: Tautology or Contradiction in comparison.](#h-25-tautology-or-contradiction-in-comparison) - - [H-26: Dangerous strict equality checks on contract balances.](#h-26-dangerous-strict-equality-checks-on-contract-balances) - - [H-27: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-27-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) - - [H-28: Weak Randomness](#h-28-weak-randomness) - - [H-29: Usage of variable before declaration.](#h-29-usage-of-variable-before-declaration) - - [H-30: Deletion from a nested mappping.](#h-30-deletion-from-a-nested-mappping) - - [H-31: Potential use of `tx.origin` for authentication.](#h-31-potential-use-of-txorigin-for-authentication) - - [H-32: Loop contains `msg.value`.](#h-32-loop-contains-msgvalue) - - [H-33: Contract locks Ether without a withdraw function.](#h-33-contract-locks-ether-without-a-withdraw-function) - - [H-34: Incorrect ERC721 interface.](#h-34-incorrect-erc721-interface) - - [H-35: Incorrect ERC20 interface.](#h-35-incorrect-erc20-interface) - - [H-36: Out of order retryable transactions.](#h-36-out-of-order-retryable-transactions) - - [H-37: Constant functions changing state](#h-37-constant-functions-changing-state) - - [H-38: Function selector collides with other functions](#h-38-function-selector-collides-with-other-functions) - - [H-39: Unchecked Low level calls](#h-39-unchecked-low-level-calls) + - [H-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`](#h-1-abiencodepacked-should-not-be-used-with-dynamic-types-when-passing-the-result-to-a-hash-function-such-as-keccak256) + - [H-2: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-2-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) + - [H-3: Unprotected initializer](#h-3-unprotected-initializer) + - [H-4: Unsafe Casting](#h-4-unsafe-casting) + - [H-5: EnumerableSet.remove in loop corrupts the set order.](#h-5-enumerablesetremove-in-loop-corrupts-the-set-order) + - [H-6: Experimental ABI Encoder](#h-6-experimental-abi-encoder) + - [H-7: Incorrect Assembly Shift Parameter Order](#h-7-incorrect-assembly-shift-parameter-order) + - [H-8: Storage Array Edited with Memory](#h-8-storage-array-edited-with-memory) + - [H-9: Contract Has Multiple Constructors](#h-9-contract-has-multiple-constructors) + - [H-10: Contract Name Reused in Different Files](#h-10-contract-name-reused-in-different-files) + - [H-11: Nested Structs in Mappings pre-0.5.0](#h-11-nested-structs-in-mappings-pre-050) + - [H-12: Depracated EVM Instruction for `selfdestruct` should not be used.](#h-12-depracated-evm-instruction-for-selfdestruct-should-not-be-used) + - [H-13: Array length value has a direct assignment.](#h-13-array-length-value-has-a-direct-assignment) + - [H-14: Incorrect use of caret operator on a non hexadcimal constant](#h-14-incorrect-use-of-caret-operator-on-a-non-hexadcimal-constant) + - [H-15: Yul block contains `return` function call.](#h-15-yul-block-contains-return-function-call) + - [H-16: Shadowed State Variables in Inheritance Hierarchy](#h-16-shadowed-state-variables-in-inheritance-hierarchy) + - [H-17: Unchecked `bool success` value for send call.](#h-17-unchecked-bool-success-value-for-send-call) + - [H-18: Misused boolean with logical operators](#h-18-misused-boolean-with-logical-operators) + - [H-19: Functions send eth away from contract but performs no checks on any address.](#h-19-functions-send-eth-away-from-contract-but-performs-no-checks-on-any-address) + - [H-20: Delegatecall made by the function without checks on any address.](#h-20-delegatecall-made-by-the-function-without-checks-on-any-address) + - [H-21: Tautological comparison.](#h-21-tautological-comparison) + - [H-22: RTLO character detected in file. \u{202e}](#h-22-rtlo-character-detected-in-file-u202e) + - [H-23: Dangerous unary operator found in assignment.](#h-23-dangerous-unary-operator-found-in-assignment) + - [H-24: Tautology or Contradiction in comparison.](#h-24-tautology-or-contradiction-in-comparison) + - [H-25: Dangerous strict equality checks on contract balances.](#h-25-dangerous-strict-equality-checks-on-contract-balances) + - [H-26: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10`](#h-26-compiler-bug-signed-array-in-storage-detected-for-compiler-version-0510) + - [H-27: Weak Randomness](#h-27-weak-randomness) + - [H-28: Usage of variable before declaration.](#h-28-usage-of-variable-before-declaration) + - [H-29: Deletion from a nested mappping.](#h-29-deletion-from-a-nested-mappping) + - [H-30: Potential use of `tx.origin` for authentication.](#h-30-potential-use-of-txorigin-for-authentication) + - [H-31: Loop contains `msg.value`.](#h-31-loop-contains-msgvalue) + - [H-32: Contract locks Ether without a withdraw function.](#h-32-contract-locks-ether-without-a-withdraw-function) + - [H-33: Incorrect ERC721 interface.](#h-33-incorrect-erc721-interface) + - [H-34: Incorrect ERC20 interface.](#h-34-incorrect-erc20-interface) + - [H-35: Out of order retryable transactions.](#h-35-out-of-order-retryable-transactions) + - [H-36: Constant functions changing state](#h-36-constant-functions-changing-state) + - [H-37: Function selector collides with other functions](#h-37-function-selector-collides-with-other-functions) + - [H-38: Unchecked Low level calls](#h-38-unchecked-low-level-calls) - [Low Issues](#low-issues) - - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - - [L-2: Solmate's SafeTransferLib does not check for token contract's existence](#l-2-solmates-safetransferlib-does-not-check-for-token-contracts-existence) - - [L-3: `ecrecover` is susceptible to signature malleability](#l-3-ecrecover-is-susceptible-to-signature-malleability) - - [L-4: Deprecated OpenZeppelin functions should not be used](#l-4-deprecated-openzeppelin-functions-should-not-be-used) - - [L-5: Unsafe ERC20 Operations should not be used](#l-5-unsafe-erc20-operations-should-not-be-used) - - [L-6: Solidity pragma should be specific, not wide](#l-6-solidity-pragma-should-be-specific-not-wide) - - [L-7: Missing checks for `address(0)` when assigning values to address state variables](#l-7-missing-checks-for-address0-when-assigning-values-to-address-state-variables) - - [L-8: `public` functions not used internally could be marked `external`](#l-8-public-functions-not-used-internally-could-be-marked-external) - - [L-9: Define and use `constant` variables instead of using literals](#l-9-define-and-use-constant-variables-instead-of-using-literals) - - [L-10: Event is missing `indexed` fields](#l-10-event-is-missing-indexed-fields) - - [L-11: Empty `require()` / `revert()` statements](#l-11-empty-require--revert-statements) - - [L-12: The `nonReentrant` `modifier` should occur before all other modifiers](#l-12-the-nonreentrant-modifier-should-occur-before-all-other-modifiers) - - [L-13: Using `block.timestamp` for swap deadline offers no protection](#l-13-using-blocktimestamp-for-swap-deadline-offers-no-protection) - - [L-14: Using `ERC721::_mint()` can be dangerous](#l-14-using-erc721mint-can-be-dangerous) - - [L-15: PUSH0 is not supported by all chains](#l-15-push0-is-not-supported-by-all-chains) - - [L-16: Modifiers invoked only once can be shoe-horned into the function](#l-16-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) - - [L-17: Empty Block](#l-17-empty-block) - - [L-18: Large literal values multiples of 10000 can be replaced with scientific notation](#l-18-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) - - [L-19: Internal functions called only once can be inlined](#l-19-internal-functions-called-only-once-can-be-inlined) - - [L-20: Contract still has TODOs](#l-20-contract-still-has-todos) - - [L-21: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-21-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) - - [L-22: Unused Custom Error](#l-22-unused-custom-error) - - [L-23: Loop contains `require`/`revert` statements](#l-23-loop-contains-requirerevert-statements) - - [L-24: Incorrect Order of Division and Multiplication](#l-24-incorrect-order-of-division-and-multiplication) - - [L-25: Redundant statements have no effect.](#l-25-redundant-statements-have-no-effect) - - [L-26: Public variables of a contract read in an external context (using `this`).](#l-26-public-variables-of-a-contract-read-in-an-external-context-using-this) - - [L-27: Potentially unused `private` / `internal` state variables found.](#l-27-potentially-unused-private--internal-state-variables-found) - - [L-28: Functions declared `pure` / `view` but contains assembly](#l-28-functions-declared-pure--view-but-contains-assembly) - - [L-29: Boolean equality is not required.](#l-29-boolean-equality-is-not-required) - - [L-30: Local variable shadows state variables in the contract hirearchy](#l-30-local-variable-shadows-state-variables-in-the-contract-hirearchy) - - [L-31: Uninitialized local variables.](#l-31-uninitialized-local-variables) - - [L-32: Return Bomb](#l-32-return-bomb) - - [L-33: Function initializing state.](#l-33-function-initializing-state) - - [L-34: Dead Code](#l-34-dead-code) - - [L-35: Loop condition contains `state_variable.length` that could be cached outside.](#l-35-loop-condition-contains-statevariablelength-that-could-be-cached-outside) - - [L-36: Incorrect use of `assert()`](#l-36-incorrect-use-of-assert) - - [L-37: Costly operations inside loops.](#l-37-costly-operations-inside-loops) - - [L-38: Builtin Symbol Shadowing](#l-38-builtin-symbol-shadowing) - - [L-39: Void constructor](#l-39-void-constructor) - - [L-40: Potentially missing inheritance for contract.](#l-40-potentially-missing-inheritance-for-contract) - - [L-41: Unused Imports](#l-41-unused-imports) - - [L-42: Function pointers used in constructors.](#l-42-function-pointers-used-in-constructors) - - [L-43: State variable could be declared constant](#l-43-state-variable-could-be-declared-constant) - - [L-44: State variable changes but no event is emitted.](#l-44-state-variable-changes-but-no-event-is-emitted) - - [L-45: State variable could be declared immutable](#l-45-state-variable-could-be-declared-immutable) - - [L-46: Modifier has multiple placeholders.](#l-46-modifier-has-multiple-placeholders) - - [L-47: Return value of the function call is not checked.](#l-47-return-value-of-the-function-call-is-not-checked) + - [L-1: Using `delegatecall` in loop may consume excessive gas](#l-1-using-delegatecall-in-loop-may-consume-excessive-gas) + - [L-2: Centralization Risk for trusted owners](#l-2-centralization-risk-for-trusted-owners) + - [L-3: Solmate's SafeTransferLib does not check for token contract's existence](#l-3-solmates-safetransferlib-does-not-check-for-token-contracts-existence) + - [L-4: `ecrecover` is susceptible to signature malleability](#l-4-ecrecover-is-susceptible-to-signature-malleability) + - [L-5: Deprecated OpenZeppelin functions should not be used](#l-5-deprecated-openzeppelin-functions-should-not-be-used) + - [L-6: Unsafe ERC20 Operations should not be used](#l-6-unsafe-erc20-operations-should-not-be-used) + - [L-7: Solidity pragma should be specific, not wide](#l-7-solidity-pragma-should-be-specific-not-wide) + - [L-8: Missing checks for `address(0)` when assigning values to address state variables](#l-8-missing-checks-for-address0-when-assigning-values-to-address-state-variables) + - [L-9: `public` functions not used internally could be marked `external`](#l-9-public-functions-not-used-internally-could-be-marked-external) + - [L-10: Define and use `constant` variables instead of using literals](#l-10-define-and-use-constant-variables-instead-of-using-literals) + - [L-11: Event is missing `indexed` fields](#l-11-event-is-missing-indexed-fields) + - [L-12: Empty `require()` / `revert()` statements](#l-12-empty-require--revert-statements) + - [L-13: The `nonReentrant` `modifier` should occur before all other modifiers](#l-13-the-nonreentrant-modifier-should-occur-before-all-other-modifiers) + - [L-14: Using `block.timestamp` for swap deadline offers no protection](#l-14-using-blocktimestamp-for-swap-deadline-offers-no-protection) + - [L-15: Using `ERC721::_mint()` can be dangerous](#l-15-using-erc721mint-can-be-dangerous) + - [L-16: PUSH0 is not supported by all chains](#l-16-push0-is-not-supported-by-all-chains) + - [L-17: Modifiers invoked only once can be shoe-horned into the function](#l-17-modifiers-invoked-only-once-can-be-shoe-horned-into-the-function) + - [L-18: Empty Block](#l-18-empty-block) + - [L-19: Large literal values multiples of 10000 can be replaced with scientific notation](#l-19-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) + - [L-20: Internal functions called only once can be inlined](#l-20-internal-functions-called-only-once-can-be-inlined) + - [L-21: Contract still has TODOs](#l-21-contract-still-has-todos) + - [L-22: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256).](#l-22-inconsistency-in-declaring-uint256uint-or-int256int-variables-within-a-contract-use-explicit-size-declarations-uint256-or-int256) + - [L-23: Unused Custom Error](#l-23-unused-custom-error) + - [L-24: Loop contains `require`/`revert` statements](#l-24-loop-contains-requirerevert-statements) + - [L-25: Incorrect Order of Division and Multiplication](#l-25-incorrect-order-of-division-and-multiplication) + - [L-26: Redundant statements have no effect.](#l-26-redundant-statements-have-no-effect) + - [L-27: Public variables of a contract read in an external context (using `this`).](#l-27-public-variables-of-a-contract-read-in-an-external-context-using-this) + - [L-28: Potentially unused `private` / `internal` state variables found.](#l-28-potentially-unused-private--internal-state-variables-found) + - [L-29: Functions declared `pure` / `view` but contains assembly](#l-29-functions-declared-pure--view-but-contains-assembly) + - [L-30: Boolean equality is not required.](#l-30-boolean-equality-is-not-required) + - [L-31: Local variable shadows state variables in the contract hirearchy](#l-31-local-variable-shadows-state-variables-in-the-contract-hirearchy) + - [L-32: Uninitialized local variables.](#l-32-uninitialized-local-variables) + - [L-33: Return Bomb](#l-33-return-bomb) + - [L-34: Function initializing state.](#l-34-function-initializing-state) + - [L-35: Dead Code](#l-35-dead-code) + - [L-36: Loop condition contains `state_variable.length` that could be cached outside.](#l-36-loop-condition-contains-statevariablelength-that-could-be-cached-outside) + - [L-37: Incorrect use of `assert()`](#l-37-incorrect-use-of-assert) + - [L-38: Costly operations inside loops.](#l-38-costly-operations-inside-loops) + - [L-39: Builtin Symbol Shadowing](#l-39-builtin-symbol-shadowing) + - [L-40: Void constructor](#l-40-void-constructor) + - [L-41: Potentially missing inheritance for contract.](#l-41-potentially-missing-inheritance-for-contract) + - [L-42: Unused Imports](#l-42-unused-imports) + - [L-43: Function pointers used in constructors.](#l-43-function-pointers-used-in-constructors) + - [L-44: State variable could be declared constant](#l-44-state-variable-could-be-declared-constant) + - [L-45: State variable changes but no event is emitted.](#l-45-state-variable-changes-but-no-event-is-emitted) + - [L-46: State variable could be declared immutable](#l-46-state-variable-could-be-declared-immutable) + - [L-47: Modifier has multiple placeholders.](#l-47-modifier-has-multiple-placeholders) + - [L-48: Return value of the function call is not checked.](#l-48-return-value-of-the-function-call-is-not-checked) # Summary @@ -230,30 +230,13 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 39 | -| Low | 47 | +| High | 38 | +| Low | 48 | # High Issues -## H-1: Using `delegatecall` in loop - -When calling `delegatecall` the same `msg.value` amount will be accredited multiple times. - -
1 Found Instances - - -- Found in src/inheritance/ExtendedInheritance.sol [Line: 15](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L15) - - ```solidity - for (uint256 i = 0; i < 3; i++) { - ``` - -
- - - -## H-2: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()` +## H-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()` 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). If all arguments are strings and or bytes, `bytes.concat()` should be used instead. @@ -283,7 +266,7 @@ If all arguments are strings and or bytes, `bytes.concat()` should be used inste -## H-3: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`) +## H-2: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`) Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. @@ -330,7 +313,7 @@ Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) ca -## H-4: Unprotected initializer +## H-3: Unprotected initializer Consider protecting the initializer functions with modifiers. @@ -347,7 +330,7 @@ Consider protecting the initializer functions with modifiers. -## H-5: Unsafe Casting +## H-4: Unsafe Casting Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior.When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type,leading to truncation and loss of significant digits. Use OpenZeppelin's SafeCast library to safely downcast integers. @@ -922,7 +905,7 @@ Downcasting int/uints in Solidity can be unsafe due to the potential for data lo -## H-6: EnumerableSet.remove in loop corrupts the set order. +## H-5: EnumerableSet.remove in loop corrupts the set order. If the order of an EnumerableSet is required, removing items in a loop using `at` and `remove` corrupts this order. Consider using a different data structure or removing items by collecting them during the loop, then removing after the loop. @@ -964,7 +947,7 @@ Consider using a different data structure or removing items by collecting them d -## H-7: Experimental ABI Encoder +## H-6: Experimental ABI Encoder Experimental encoders should not be used in production. There are multiple known compiler bugs that are caused by the experimental encoder. Upgrade your solidity version to remove the need for experimental features. @@ -981,7 +964,7 @@ Experimental encoders should not be used in production. There are multiple known -## H-8: Incorrect Assembly Shift Parameter Order +## H-7: Incorrect Assembly Shift Parameter Order Example: `shl(shifted, 4)` will shift the right constant `4` by `a` bits. The correct order is `shl(4, shifted)`. @@ -1004,7 +987,7 @@ Example: `shl(shifted, 4)` will shift the right constant `4` by `a` bits. The co -## H-9: Storage Array Edited with Memory +## H-8: Storage Array Edited with Memory Storage reference is passed to a function with a memory parameter. This will not update the storage variable as expected. Consider using storage parameters instead. @@ -1021,7 +1004,7 @@ Storage reference is passed to a function with a memory parameter. This will not -## H-10: Contract Has Multiple Constructors +## H-9: Contract Has Multiple Constructors In some versions of Solidity, contracts compile with multiple constructors. The first constructor takes precedence. This can lead to unexpected behavior. @@ -1038,7 +1021,7 @@ In some versions of Solidity, contracts compile with multiple constructors. The -## H-11: Contract Name Reused in Different Files +## H-10: Contract Name Reused in Different Files When compiling contracts with certain development frameworks (for example: Truffle), having contracts with the same name across different files can lead to one being overwritten. @@ -1073,7 +1056,7 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-12: Nested Structs in Mappings pre-0.5.0 +## H-11: Nested Structs in Mappings pre-0.5.0 Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled, but produced incorrect values. Refrain from using these, or update to a more recent version of Solidity. @@ -1090,7 +1073,7 @@ Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled -## H-13: Depracated EVM Instruction for `selfdestruct` should not be used. +## H-12: Depracated EVM Instruction for `selfdestruct` should not be used. @@ -1107,7 +1090,7 @@ Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled -## H-14: Array length value has a direct assignment. +## H-13: Array length value has a direct assignment. If the length of a dynamic array (storage variable) directly assigned to, it may allow access to other storage slots by tweaking it's value. This practice has been depracated in newer Solidity versions @@ -1148,7 +1131,7 @@ If the length of a dynamic array (storage variable) directly assigned to, it may -## H-15: Incorrect use of caret operator on a non hexadcimal constant +## H-14: Incorrect use of caret operator on a non hexadcimal constant The caret operator is usually mistakenly thought of as an exponentiation operator but actually, it's a bitwise xor operator. @@ -1189,7 +1172,7 @@ The caret operator is usually mistakenly thought of as an exponentiation operato -## H-16: Yul block contains `return` function call. +## H-15: Yul block contains `return` function call. Remove this, as this causes execution to halt. Nothing after that call will execute, including code following the assembly block. @@ -1206,7 +1189,7 @@ Remove this, as this causes execution to halt. Nothing after that call will exec -## H-17: Shadowed State Variables in Inheritance Hierarchy +## H-16: Shadowed State Variables in Inheritance Hierarchy This vulnerability arises when a derived contract unintentionally shadows a state variable from a parent contract by declaring a variable with the same name. This can be misleading. To prevent this, ensure variable names are unique across the inheritance hierarchy or use proper visibility and scope controls. @@ -1223,7 +1206,7 @@ This vulnerability arises when a derived contract unintentionally shadows a stat -## H-18: Unchecked `bool success` value for send call. +## H-17: Unchecked `bool success` value for send call. The transaction `address(payable?).send(address)` may fail because of reasons like out-of-gas, invalid receipient address or revert from the recipient. Therefore, the boolean returned by this function call must be checked to be `true` in order to verify that the transaction was successful @@ -1240,7 +1223,7 @@ The transaction `address(payable?).send(address)` may fail because of reasons li -## H-19: Misused boolean with logical operators +## H-18: Misused boolean with logical operators The patterns `if (… || true)` and `if (.. && false)` will always evaluate to true and false respectively. @@ -1311,7 +1294,7 @@ The patterns `if (… || true)` and `if (.. && false)` will always evaluate to t -## H-20: Functions send eth away from contract but performs no checks on any address. +## H-19: Functions send eth away from contract but performs no checks on any address. Consider introducing checks for `msg.sender` to ensure the recipient of the money is as intended. @@ -1442,7 +1425,7 @@ Consider introducing checks for `msg.sender` to ensure the recipient of the mone -## H-21: Delegatecall made by the function without checks on any address. +## H-20: Delegatecall made by the function without checks on any address. Introduce checks on the address @@ -1483,7 +1466,7 @@ Introduce checks on the address -## H-22: Tautological comparison. +## H-21: Tautological comparison. The left hand side and the right hand side of the binary operation has the same value. This makes the condition always true or always false. @@ -1518,7 +1501,7 @@ The left hand side and the right hand side of the binary operation has the same -## H-23: RTLO character detected in file. \u{202e} +## H-22: RTLO character detected in file. \u{202e} Right to left override character may be misledaing and cause potential attacks by visually misordering method arguments! @@ -1535,7 +1518,7 @@ Right to left override character may be misledaing and cause potential attacks b -## H-24: Dangerous unary operator found in assignment. +## H-23: Dangerous unary operator found in assignment. Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in between. @@ -1558,7 +1541,7 @@ Potentially mistakened `=+` for `+=` or `=-` for `-=`. Please include a space in -## H-25: Tautology or Contradiction in comparison. +## H-24: Tautology or Contradiction in comparison. The condition has been determined to be either always true or always false due to the integer range in which we're operating. @@ -1581,7 +1564,7 @@ The condition has been determined to be either always true or always false due t -## H-26: Dangerous strict equality checks on contract balances. +## H-25: Dangerous strict equality checks on contract balances. A contract's balance can be forcibly manipulated by another selfdestructing contract. Therefore, it's recommended to use >, <, >= or <= instead of strict equality. @@ -1610,7 +1593,7 @@ A contract's balance can be forcibly manipulated by another selfdestructing cont -## H-27: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` +## H-26: Compiler Bug: Signed array in storage detected for compiler version `<0.5.10` If you want to leverage signed arrays in storage by assigning a literal array with at least one negative number, then you mus use solidity version 0.5.10 or above. This is because of a bug in older compilers. @@ -1627,7 +1610,7 @@ If you want to leverage signed arrays in storage by assigning a literal array wi -## H-28: Weak Randomness +## H-27: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -1692,7 +1675,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-29: Usage of variable before declaration. +## H-28: Usage of variable before declaration. This is a bad practice that may lead to unintended consequences. Please declare the variable before using it. @@ -1709,7 +1692,7 @@ This is a bad practice that may lead to unintended consequences. Please declare -## H-30: Deletion from a nested mappping. +## H-29: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -1726,7 +1709,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-31: Potential use of `tx.origin` for authentication. +## H-30: Potential use of `tx.origin` for authentication. Using `tx.origin` may lead to problems when users are interacting via smart contract with your protocol. It is recommended to use `msg.sender` for authentication. @@ -1755,7 +1738,7 @@ Using `tx.origin` may lead to problems when users are interacting via smart cont -## H-32: Loop contains `msg.value`. +## H-31: Loop contains `msg.value`. Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. @@ -1790,7 +1773,7 @@ Provide an explicit array of amounts alongside the receivers array, and check th -## H-33: Contract locks Ether without a withdraw function. +## H-32: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. @@ -1861,7 +1844,7 @@ It appears that the contract includes a payable function to accept Ether but lac -## H-34: Incorrect ERC721 interface. +## H-33: Incorrect ERC721 interface. Incorrect return values for ERC721 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC721 functions. @@ -1920,7 +1903,7 @@ Incorrect return values for ERC721 functions. A contract compiled with Solidity -## H-35: Incorrect ERC20 interface. +## H-34: Incorrect ERC20 interface. Incorrect return values for ERC20 functions. A contract compiled with Solidity > 0.4.22 interacting with these functions will fail to execute them, as the return value is missing. Set the appropriate return values and types for the defined ERC20 functions. @@ -1961,7 +1944,7 @@ Incorrect return values for ERC20 functions. A contract compiled with Solidity > -## H-36: Out of order retryable transactions. +## H-35: Out of order retryable transactions. Do not rely on the order or successful execution of retryable tickets. Functions like createRetryableTicket, outboundTransferCustomRefund, unsafeCreateRetryableTicket are free to be re-tried in any order if they fail in the first go. Since this operation happens off chain, the sequencer is in control of the @@ -1986,7 +1969,7 @@ Do not rely on the order or successful execution of retryable tickets. Functions -## H-37: Constant functions changing state +## H-36: Constant functions changing state Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct. @@ -2003,7 +1986,7 @@ Function is declared constant/view but it changes state. Ensure that the attribu -## H-38: Function selector collides with other functions +## H-37: Function selector collides with other functions Function selector collides with other functions. This may cause the solidity function dispatcher to invoke the wrong function if the functions happen to be included in the same contract through an inheritance hirearchy later down the line. It is recommended to rename this function or change its parameters. @@ -2028,7 +2011,7 @@ Function selector collides with other functions. This may cause the solidity fun -## H-39: Unchecked Low level calls +## H-38: Unchecked Low level calls The return value of the low-level call is not checked, so if the call fails, the Ether will be locked in the contract. If the low level is used to prevent blocking operations, consider logging failed calls. Ensure that the return value of a low-level call is checked or logged. @@ -2125,7 +2108,24 @@ The return value of the low-level call is not checked, so if the call fails, the # Low Issues -## L-1: Centralization Risk for trusted owners +## L-1: Using `delegatecall` in loop may consume excessive gas + +Using `delegatecall` in loop may consume excessive gas + +
1 Found Instances + + +- Found in src/inheritance/ExtendedInheritance.sol [Line: 15](../tests/contract-playground/src/inheritance/ExtendedInheritance.sol#L15) + + ```solidity + for (uint256 i = 0; i < 3; i++) { + ``` + +
+ + + +## L-2: Centralization Risk for trusted owners Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds. @@ -2238,7 +2238,7 @@ Contracts have owners with privileged rights to perform admin tasks and need to -## L-2: Solmate's SafeTransferLib does not check for token contract's existence +## L-3: Solmate's SafeTransferLib does not check for token contract's existence There is a subtle difference between the implementation of solmate's SafeTransferLib and OZ's SafeERC20: OZ's SafeERC20 checks if the token is a contract or not, solmate's SafeTransferLib does not. https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol#L9 @@ -2258,7 +2258,7 @@ https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.s -## L-3: `ecrecover` is susceptible to signature malleability +## L-4: `ecrecover` is susceptible to signature malleability 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. @@ -2275,7 +2275,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th -## L-4: Deprecated OpenZeppelin functions should not be used +## L-5: Deprecated OpenZeppelin functions should not be used Openzeppelin has deprecated several functions and replaced with newer versions. Please consult https://docs.openzeppelin.com/ @@ -2298,7 +2298,7 @@ Openzeppelin has deprecated several functions and replaced with newer versions. -## L-5: Unsafe ERC20 Operations should not be used +## L-6: Unsafe ERC20 Operations should not be used ERC20 functions may not behave as expected. For example: return values are not always meaningful. It is recommended to use OpenZeppelin's SafeERC20 library. @@ -2423,7 +2423,7 @@ ERC20 functions may not behave as expected. For example: return values are not a -## L-6: Solidity pragma should be specific, not wide +## L-7: Solidity pragma should be specific, not wide 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;` @@ -2644,7 +2644,7 @@ Consider using a specific version of Solidity in your contracts instead of a wid -## L-7: Missing checks for `address(0)` when assigning values to address state variables +## L-8: Missing checks for `address(0)` when assigning values to address state variables Check for `address(0)` when assigning values to address state variables. @@ -2715,7 +2715,7 @@ Check for `address(0)` when assigning values to address state variables. -## L-8: `public` functions not used internally could be marked `external` +## L-9: `public` functions not used internally could be marked `external` Instead of marking a function as `public`, consider marking it as `external` if it is not used internally. @@ -3104,7 +3104,7 @@ Instead of marking a function as `public`, consider marking it as `external` if -## L-9: Define and use `constant` variables instead of using literals +## L-10: Define and use `constant` variables instead of using literals If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract. @@ -3559,7 +3559,7 @@ If the same constant literal value is used multiple times, create a constant sta -## L-10: Event is missing `indexed` fields +## L-11: Event is missing `indexed` fields Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed. @@ -3714,7 +3714,7 @@ Index event fields make the field more quickly accessible to off-chain tools tha -## L-11: Empty `require()` / `revert()` statements +## L-12: Empty `require()` / `revert()` statements Use descriptive reason strings or custom errors for revert paths. @@ -3863,7 +3863,7 @@ Use descriptive reason strings or custom errors for revert paths. -## L-12: The `nonReentrant` `modifier` should occur before all other modifiers +## L-13: The `nonReentrant` `modifier` should occur before all other modifiers This is a best-practice to protect against reentrancy in other modifiers. @@ -3886,7 +3886,7 @@ This is a best-practice to protect against reentrancy in other modifiers. -## L-13: Using `block.timestamp` for swap deadline offers no protection +## L-14: Using `block.timestamp` for swap deadline offers no protection 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. @@ -3999,7 +3999,7 @@ In the PoS model, proposers know well in advance if they will propose one or con -## L-14: Using `ERC721::_mint()` can be dangerous +## L-15: Using `ERC721::_mint()` can be dangerous Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support ERC721 tokens. Use `_safeMint()` instead of `_mint()` for ERC721. @@ -4016,7 +4016,7 @@ Using `ERC721::_mint()` can mint ERC721 tokens to addresses which don't support -## L-15: PUSH0 is not supported by all chains +## L-16: PUSH0 is not supported by all chains 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. @@ -4285,7 +4285,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-16: Modifiers invoked only once can be shoe-horned into the function +## L-17: Modifiers invoked only once can be shoe-horned into the function @@ -4380,7 +4380,7 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-17: Empty Block +## L-18: Empty Block Consider removing empty blocks. @@ -4601,7 +4601,7 @@ Consider removing empty blocks. -## L-18: Large literal values multiples of 10000 can be replaced with scientific notation +## L-19: Large literal values multiples of 10000 can be replaced with scientific notation Use `e` notation, for example: `1e18`, instead of its full numeric value. @@ -4774,7 +4774,7 @@ Use `e` notation, for example: `1e18`, instead of its full numeric value. -## L-19: Internal functions called only once can be inlined +## L-20: Internal functions called only once can be inlined 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. @@ -4899,7 +4899,7 @@ Instead of separating the logic into a separate function, consider inlining the -## L-20: Contract still has TODOs +## L-21: Contract still has TODOs Contract contains comments with TODOS @@ -4922,7 +4922,7 @@ Contract contains comments with TODOS -## L-21: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). +## L-22: Inconsistency in declaring uint256/uint (or) int256/int variables within a contract. Use explicit size declarations (uint256 or int256). Consider keeping the naming convention consistent in a given contract. Explicit size declarations are preferred (uint256, int256) over implicit ones (uint, int) to avoid confusion. @@ -5107,7 +5107,7 @@ Consider keeping the naming convention consistent in a given contract. Explicit -## L-22: Unused Custom Error +## L-23: Unused Custom Error it is recommended that the definition be removed when custom error is unused @@ -5136,7 +5136,7 @@ it is recommended that the definition be removed when custom error is unused -## L-23: Loop contains `require`/`revert` statements +## L-24: Loop contains `require`/`revert` statements Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop @@ -5159,7 +5159,7 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca -## L-24: Incorrect Order of Division and Multiplication +## L-25: Incorrect Order of Division and Multiplication Division operations followed directly by multiplication operations can lead to precision loss due to the way integer arithmetic is handled in Solidity. @@ -5194,7 +5194,7 @@ Division operations followed directly by multiplication operations can lead to p -## L-25: Redundant statements have no effect. +## L-26: Redundant statements have no effect. Remove the redundant statements because no code will be generated and it just congests the codebase. @@ -5241,7 +5241,7 @@ Remove the redundant statements because no code will be generated and it just co -## L-26: Public variables of a contract read in an external context (using `this`). +## L-27: Public variables of a contract read in an external context (using `this`). The contract reads it's own variable using `this` which adds an unnecessary STATICCALL. Remove `this` and access the variable like storage. @@ -5276,7 +5276,7 @@ The contract reads it's own variable using `this` which adds an unnecessary STAT -## L-27: Potentially unused `private` / `internal` state variables found. +## L-28: Potentially unused `private` / `internal` state variables found. State variable appears to be unused. No analysis has been performed to see if any inilne assembly references it. So if that's not the case, consider removing this unused variable. @@ -5449,7 +5449,7 @@ State variable appears to be unused. No analysis has been performed to see if an -## L-28: Functions declared `pure` / `view` but contains assembly +## L-29: Functions declared `pure` / `view` but contains assembly If the assembly code contains bugs or unintended side effects, it can lead to incorrect results or vulnerabilities, which are hard to debug and resolve, especially when the function is meant to be simple and predictable. @@ -5478,7 +5478,7 @@ If the assembly code contains bugs or unintended side effects, it can lead to in -## L-29: Boolean equality is not required. +## L-30: Boolean equality is not required. 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. @@ -5513,7 +5513,7 @@ If `x` is a boolean, there is no need to do `if(x == true)` or `if(x == false)`. -## L-30: Local variable shadows state variables in the contract hirearchy +## L-31: Local variable shadows state variables in the contract hirearchy Rename the local variables that shadow another component. @@ -5572,7 +5572,7 @@ Rename the local variables that shadow another component. -## L-31: Uninitialized local variables. +## L-32: Uninitialized local variables. Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability. @@ -5673,7 +5673,7 @@ Initialize all the variables. If a variable is meant to be initialized to zero, -## L-32: Return Bomb +## L-33: Return Bomb A low level callee may consume all callers gas unexpectedly. Avoid unlimited implicit decoding of returndata on calls to unchecked addresses. You can limit the gas by passing a gas limit as an option to the call. For example, `unknownAdress.call{gas: gasLimitHere}("calldata")` That would act as a safety net from OOG errors. @@ -5691,7 +5691,7 @@ A low level callee may consume all callers gas unexpectedly. Avoid unlimited imp -## L-33: Function initializing state. +## L-34: Function initializing state. Detects the immediate initialization of state variables through function calls that are not pure/constant, or that use non-constant state variable. Remove any initialization of state variables via non-constant state variables or function calls. If variables must be set upon contract deployment, locate initialization in the constructor instead. @@ -5720,7 +5720,7 @@ Detects the immediate initialization of state variables through function calls t -## L-34: Dead Code +## L-35: Dead Code Functions that are not used. Consider removing them. @@ -5803,7 +5803,7 @@ Functions that are not used. Consider removing them. -## L-35: Loop condition contains `state_variable.length` that could be cached outside. +## L-36: Loop condition contains `state_variable.length` that could be cached outside. Cache the lengths of storage arrays if they are used and not modified in for loops. @@ -5832,7 +5832,7 @@ Cache the lengths of storage arrays if they are used and not modified in for loo -## L-36: Incorrect use of `assert()` +## L-37: Incorrect use of `assert()` Argument to `assert()` modifies the state. Use `require` for invariants modifying state. @@ -5849,7 +5849,7 @@ Argument to `assert()` modifies the state. Use `require` for invariants modifyin -## L-37: Costly operations inside loops. +## L-38: Costly operations inside loops. Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local variable to hold the loop computation result. @@ -5944,7 +5944,7 @@ Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local -## L-38: Builtin Symbol Shadowing +## L-39: Builtin Symbol Shadowing Name clashes with a built-in-symbol. Consider renaming it. @@ -5979,7 +5979,7 @@ Name clashes with a built-in-symbol. Consider renaming it. -## L-39: Void constructor +## L-40: Void constructor Call to a constructor that is not implemented. @@ -5996,7 +5996,7 @@ Call to a constructor that is not implemented. -## L-40: Potentially missing inheritance for contract. +## L-41: Potentially missing inheritance for contract. There is an interface / abstract contract that is potentially missing (not included in) the inheritance of this contract. @@ -6028,7 +6028,7 @@ There is an interface / abstract contract that is potentially missing (not inclu -## L-41: Unused Imports +## L-42: Unused Imports Redundant import statement. Consider removing it. @@ -6057,7 +6057,7 @@ Redundant import statement. Consider removing it. -## L-42: Function pointers used in constructors. +## L-43: Function pointers used in constructors. solc versions below 0.5.9 contain a compiler bug leading to unexpected behavior when calling uninitialized function pointers in constructors. It is recommended to not use function pointers in constructors. @@ -6074,7 +6074,7 @@ solc versions below 0.5.9 contain a compiler bug leading to unexpected behavior -## L-43: State variable could be declared constant +## L-44: State variable could be declared constant State variables that are not updated following deployment should be declared constant to save gas. Add the `constant` attribute to state variables that never change. @@ -6355,7 +6355,7 @@ State variables that are not updated following deployment should be declared con -## L-44: State variable changes but no event is emitted. +## L-45: State variable changes but no event is emitted. State variable changes in this function but no event is emitted. @@ -6966,7 +6966,7 @@ State variable changes in this function but no event is emitted. -## L-45: State variable could be declared immutable +## L-46: 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 @@ -7163,7 +7163,7 @@ State variables that are should be declared immutable to save gas. Add the `immu -## L-46: Modifier has multiple placeholders. +## L-47: Modifier has multiple placeholders. Design the modifier to only contain 1 placeholder statement. If it's not possible, split the logic into multiple modifiers. @@ -7180,7 +7180,7 @@ Design the modifier to only contain 1 placeholder statement. If it's not possibl -## L-47: Return value of the function call is not checked. +## L-48: Return value of the function call is not checked. Function returns a value but it is ignored. diff --git a/reports/report.sarif b/reports/report.sarif index 1ae344c2b..d574395ea 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -4,26 +4,6 @@ "runs": [ { "results": [ - { - "level": "warning", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/inheritance/ExtendedInheritance.sol" - }, - "region": { - "byteLength": 96, - "byteOffset": 474 - } - } - } - ], - "message": { - "text": "When calling `delegatecall` the same `msg.value` amount will be accredited multiple times." - }, - "ruleId": "delegate-call-in-loop" - }, { "level": "warning", "locations": [ @@ -3023,6 +3003,26 @@ }, "ruleId": "unchecked-low-level-call" }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/inheritance/ExtendedInheritance.sol" + }, + "region": { + "byteLength": 96, + "byteOffset": 474 + } + } + } + ], + "message": { + "text": "Using `delegatecall` in loop may consume excessive gas" + }, + "ruleId": "delegate-call-in-loop" + }, { "level": "note", "locations": [ From 1acd400d897b546170acc20901013a385265a213 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 23:09:42 +0530 Subject: [PATCH 22/25] Update `.is_constructor` -> `.kind() == &FunctionKind::Constructor` for broader version compatiblity (#773) --- aderyn_core/src/detect/high/multiple_constructors.rs | 4 ++-- aderyn_core/src/detect/low/dead_code.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aderyn_core/src/detect/high/multiple_constructors.rs b/aderyn_core/src/detect/high/multiple_constructors.rs index e24d576e7..2dbfd79d9 100644 --- a/aderyn_core/src/detect/high/multiple_constructors.rs +++ b/aderyn_core/src/detect/high/multiple_constructors.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, error::Error}; -use crate::ast::NodeID; +use crate::ast::{FunctionKind, NodeID}; use crate::{ capture, @@ -25,7 +25,7 @@ impl IssueDetector for MultipleConstructorsDetector { ExtractFunctionDefinitions::from(contract) .extracted .iter() - .filter(|function| function.is_constructor) + .filter(|function| function.kind() == &FunctionKind::Constructor) .count() > 1 }) diff --git a/aderyn_core/src/detect/low/dead_code.rs b/aderyn_core/src/detect/low/dead_code.rs index 5cc428d8b..7297edc4a 100644 --- a/aderyn_core/src/detect/low/dead_code.rs +++ b/aderyn_core/src/detect/low/dead_code.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, error::Error}; -use crate::ast::{ASTNode, ContractKind, NodeID, NodeType, Visibility}; +use crate::ast::{ASTNode, ContractKind, FunctionKind, NodeID, NodeType, Visibility}; use crate::{ capture, @@ -34,7 +34,7 @@ impl IssueDetector for DeadCodeDetector { f.overrides.is_none() && f.implemented && f.visibility == Visibility::Internal - && !f.is_constructor + && f.kind() != &FunctionKind::Constructor }) .filter(|&f| { if let Some(ASTNode::ContractDefinition(contract)) = From 6dfe3a15c7fcdfc99f4ecf36658fe6a36a3e7319 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Fri, 18 Oct 2024 23:49:37 +0530 Subject: [PATCH 23/25] Fix: Account for revert statements not just revert functions in require/revert in loop detector (#774) --- .../low/reverts_and_requries_in_loops.rs | 65 ++++++++---- reports/ccip-functions-report.md | 98 ++++++++++++++++++- reports/prb-math-report.md | 30 +++++- reports/report.json | 8 +- reports/report.sarif | 8 +- reports/sablier-aderyn-toml-nested-root.md | 42 +++++++- reports/templegold-report.md | 32 +++++- 7 files changed, 249 insertions(+), 34 deletions(-) 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 b46e6e11f..e648ae1ab 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,11 +1,18 @@ use std::{collections::BTreeMap, error::Error}; -use crate::ast::{NodeID, NodeType}; +use crate::ast::NodeID; use crate::{ capture, - context::{browser::GetClosestAncestorOfTypeX, workspace_context::WorkspaceContext}, - detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + context::{ + browser::{ExtractIdentifiers, ExtractRevertStatements}, + graph::{CallGraph, CallGraphDirection, CallGraphVisitor}, + workspace_context::WorkspaceContext, + }, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::get_explore_centers_of_loops, + }, }; use eyre::Result; @@ -18,21 +25,15 @@ pub struct RevertsAndRequiresInLoopsDetector { impl IssueDetector for RevertsAndRequiresInLoopsDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { - // Collect all require statements - let requires_and_reverts = context - .identifiers() - .into_iter() - .filter(|&id| id.name == "revert" || id.name == "require") - .collect::>(); - - for item in requires_and_reverts { - if let Some(for_loop) = item.closest_ancestor_of_type(context, NodeType::ForStatement) { - capture!(self, context, for_loop); - } - if let Some(while_loop) = - item.closest_ancestor_of_type(context, NodeType::WhileStatement) - { - capture!(self, context, while_loop); + let loop_explore_centers = get_explore_centers_of_loops(context); + + for l in loop_explore_centers { + let callgraph = CallGraph::new(context, &[l], CallGraphDirection::Inward)?; + let mut tracker = RevertAndRequireTracker::default(); + callgraph.accept(context, &mut tracker)?; + + if tracker.has_require_or_revert || tracker.has_revert_statement { + capture!(self, context, l); } } @@ -60,6 +61,34 @@ impl IssueDetector for RevertsAndRequiresInLoopsDetector { } } +#[derive(Default)] +struct RevertAndRequireTracker { + has_require_or_revert: bool, + has_revert_statement: bool, +} + +impl CallGraphVisitor for RevertAndRequireTracker { + fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> { + // Check for revert() and require() calls + let identifiers = ExtractIdentifiers::from(node).extracted; + + let requires_and_reverts_are_present = + identifiers.iter().any(|id| id.name == "revert" || id.name == "require"); + + if requires_and_reverts_are_present { + self.has_require_or_revert = true; + } + + // Check for revert statements + let revert_statements = ExtractRevertStatements::from(node).extracted; + + if !revert_statements.is_empty() { + self.has_revert_statement = true; + } + Ok(()) + } +} + #[cfg(test)] mod reevrts_and_requires_in_loops { use serial_test::serial; diff --git a/reports/ccip-functions-report.md b/reports/ccip-functions-report.md index 063975477..f25a7bad6 100644 --- a/reports/ccip-functions-report.md +++ b/reports/ccip-functions-report.md @@ -2340,8 +2340,62 @@ it is recommended that the definition be removed when custom error is unused Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop -
2 Found Instances +
18 Found Instances + + +- Found in src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol [Line: 194](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol#L194) + + ```solidity + for (uint256 i = 0; i < numberOfFulfillments; ++i) { + ``` + +- Found in src/v0.8/functions/dev/v1_X/FunctionsRouter.sol [Line: 518](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol#L518) + + ```solidity + for (uint256 i = 0; i < idsArrayLength; ++i) { + ``` + +- Found in src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol [Line: 503](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol#L503) + + ```solidity + for (uint256 i = 0; i < requestsToTimeoutByCommitment.length; ++i) { + ``` + +- Found in src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol [Line: 52](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol#L52) + + ```solidity + for (uint256 j = 0; j < initialBlockedSenders.length; ++j) { + ``` + +- Found in src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol [Line: 147](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol#L147) + + ```solidity + for (uint256 i = 0; i < args.signers.length; i++) { + ``` + +- Found in src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol [Line: 345](../tests/ccip-contracts/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol#L345) + + ```solidity + for (uint256 i = 0; i < rs.length; ++i) { + ``` + +- Found in src/v0.8/functions/v1_0_0/FunctionsCoordinator.sol [Line: 155](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/FunctionsCoordinator.sol#L155) + + ```solidity + for (uint256 i = 0; i < requestIds.length; ++i) { + ``` + +- Found in src/v0.8/functions/v1_0_0/FunctionsRouter.sol [Line: 516](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol#L516) + + ```solidity + for (uint256 i = 0; i < idsArrayLength; ++i) { + ``` +- Found in src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol [Line: 503](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol#L503) + + ```solidity + for (uint256 i = 0; i < requestsToTimeoutByCommitment.length; ++i) { + ``` - Found in src/v0.8/functions/v1_0_0/ocr/OCR2Base.sol [Line: 145](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_0_0/ocr/OCR2Base.sol#L145) @@ -2355,6 +2409,48 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca for (uint256 i = 0; i < rs.length; ++i) { ``` +- Found in src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol [Line: 155](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol#L155) + + ```solidity + for (uint256 i = 0; i < numberOfFulfillments; ++i) { + ``` + +- Found in src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol [Line: 139](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol#L139) + + ```solidity + for (uint256 i = 0; i < args.signers.length; i++) { + ``` + +- Found in src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol [Line: 339](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol#L339) + + ```solidity + for (uint256 i = 0; i < rs.length; ++i) { + ``` + +- Found in src/v0.8/functions/v1_3_0/FunctionsCoordinator.sol [Line: 195](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_3_0/FunctionsCoordinator.sol#L195) + + ```solidity + for (uint256 i = 0; i < numberOfFulfillments; ++i) { + ``` + +- Found in src/v0.8/functions/v1_3_0/accessControl/TermsOfServiceAllowList.sol [Line: 53](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_3_0/accessControl/TermsOfServiceAllowList.sol#L53) + + ```solidity + for (uint256 j = 0; j < initialBlockedSenders.length; ++j) { + ``` + +- Found in src/v0.8/functions/v1_3_0/ocr/OCR2Base.sol [Line: 147](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_3_0/ocr/OCR2Base.sol#L147) + + ```solidity + for (uint256 i = 0; i < args.signers.length; i++) { + ``` + +- Found in src/v0.8/functions/v1_3_0/ocr/OCR2Base.sol [Line: 345](../tests/ccip-contracts/contracts/src/v0.8/functions/v1_3_0/ocr/OCR2Base.sol#L345) + + ```solidity + for (uint256 i = 0; i < rs.length; ++i) { + ``` +
diff --git a/reports/prb-math-report.md b/reports/prb-math-report.md index 1dd24ad1c..4773e7b2d 100644 --- a/reports/prb-math-report.md +++ b/reports/prb-math-report.md @@ -15,7 +15,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-2: PUSH0 is not supported by all chains](#l-2-push0-is-not-supported-by-all-chains) - [L-3: Large literal values multiples of 10000 can be replaced with scientific notation](#l-3-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) - [L-4: Internal functions called only once can be inlined](#l-4-internal-functions-called-only-once-can-be-inlined) - - [L-5: Unused Imports](#l-5-unused-imports) + - [L-5: Loop contains `require`/`revert` statements](#l-5-loop-contains-requirerevert-statements) + - [L-6: Unused Imports](#l-6-unused-imports) # Summary @@ -70,7 +71,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 2 | -| Low | 5 | +| Low | 6 | # High Issues @@ -563,7 +564,30 @@ Instead of separating the logic into a separate function, consider inlining the -## L-5: Unused Imports +## L-5: Loop contains `require`/`revert` statements + +Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop + +
2 Found Instances + + +- Found in src/sd59x18/Math.sol [Line: 656](../tests/prb-math/src/sd59x18/Math.sol#L656) + + ```solidity + for (yAux >>= 1; yAux > 0; yAux >>= 1) { + ``` + +- Found in src/ud60x18/Math.sol [Line: 537](../tests/prb-math/src/ud60x18/Math.sol#L537) + + ```solidity + for (y >>= 1; y > 0; y >>= 1) { + ``` + +
+ + + +## L-6: Unused Imports Redundant import statement. Consider removing it. diff --git a/reports/report.json b/reports/report.json index 2c49f44b9..e2a5bc25a 100644 --- a/reports/report.json +++ b/reports/report.json @@ -5144,14 +5144,14 @@ { "contract_path": "src/RevertsAndRequriesInLoops.sol", "line_no": 10, - "src": "227:129", - "src_char": "227:129" + "src": "263:93", + "src_char": "263:93" }, { "contract_path": "src/RevertsAndRequriesInLoops.sol", "line_no": 17, - "src": "416:150", - "src_char": "416:150" + "src": "452:114", + "src_char": "452:114" } ] }, diff --git a/reports/report.sarif b/reports/report.sarif index d574395ea..a58610af9 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -8345,8 +8345,8 @@ "uri": "src/RevertsAndRequriesInLoops.sol" }, "region": { - "byteLength": 129, - "byteOffset": 227 + "byteLength": 93, + "byteOffset": 263 } } }, @@ -8356,8 +8356,8 @@ "uri": "src/RevertsAndRequriesInLoops.sol" }, "region": { - "byteLength": 150, - "byteOffset": 416 + "byteLength": 114, + "byteOffset": 452 } } } diff --git a/reports/sablier-aderyn-toml-nested-root.md b/reports/sablier-aderyn-toml-nested-root.md index 4b3a0bcfe..823b6f5b7 100644 --- a/reports/sablier-aderyn-toml-nested-root.md +++ b/reports/sablier-aderyn-toml-nested-root.md @@ -15,7 +15,8 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-5: PUSH0 is not supported by all chains](#l-5-push0-is-not-supported-by-all-chains) - [L-6: Large literal values multiples of 10000 can be replaced with scientific notation](#l-6-large-literal-values-multiples-of-10000-can-be-replaced-with-scientific-notation) - [L-7: Internal functions called only once can be inlined](#l-7-internal-functions-called-only-once-can-be-inlined) - - [L-8: Costly operations inside loops.](#l-8-costly-operations-inside-loops) + - [L-8: Loop contains `require`/`revert` statements](#l-8-loop-contains-requirerevert-statements) + - [L-9: Costly operations inside loops.](#l-9-costly-operations-inside-loops) # Summary @@ -60,7 +61,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 0 | -| Low | 8 | +| Low | 9 | # Low Issues @@ -556,7 +557,42 @@ Instead of separating the logic into a separate function, consider inlining the -## L-8: Costly operations inside loops. +## L-8: Loop contains `require`/`revert` statements + +Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop + +
4 Found Instances + + +- Found in src/abstracts/SablierV2Lockup.sol [Line: 277](../tests/2024-05-Sablier/v2-core/src/abstracts/SablierV2Lockup.sol#L277) + + ```solidity + for (uint256 i = 0; i < count; ++i) { + ``` + +- Found in src/abstracts/SablierV2Lockup.sol [Line: 452](../tests/2024-05-Sablier/v2-core/src/abstracts/SablierV2Lockup.sol#L452) + + ```solidity + for (uint256 i = 0; i < streamIdsCount; ++i) { + ``` + +- Found in src/libraries/Helpers.sol [Line: 252](../tests/2024-05-Sablier/v2-core/src/libraries/Helpers.sol#L252) + + ```solidity + for (uint256 index = 0; index < count; ++index) { + ``` + +- Found in src/libraries/Helpers.sol [Line: 315](../tests/2024-05-Sablier/v2-core/src/libraries/Helpers.sol#L315) + + ```solidity + for (uint256 index = 0; index < count; ++index) { + ``` + +
+ + + +## L-9: Costly operations inside loops. Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local variable to hold the loop computation result. diff --git a/reports/templegold-report.md b/reports/templegold-report.md index 8309c215b..bad5265c0 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -8207,7 +8207,7 @@ it is recommended that the definition be removed when custom error is unused Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop -
6 Found Instances +
11 Found Instances - Found in contracts/admin/TempleTeamPayments.sol [Line: 38](../tests/2024-07-templegold/protocol/contracts/admin/TempleTeamPayments.sol#L38) @@ -8216,6 +8216,18 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca for (uint256 i = 0; i < _addresses.length; i++) { ``` +- Found in contracts/admin/TempleTeamPaymentsFactory.sol [Line: 128](../tests/2024-07-templegold/protocol/contracts/admin/TempleTeamPaymentsFactory.sol#L128) + + ```solidity + for (uint256 i; i < _dests.length; ) { + ``` + +- Found in contracts/admin/TempleTeamPaymentsV2.sol [Line: 50](../tests/2024-07-templegold/protocol/contracts/admin/TempleTeamPaymentsV2.sol#L50) + + ```solidity + for (uint256 i; i < _addresses.length; ) { + ``` + - Found in contracts/core/OpsManager.sol [Line: 97](../tests/2024-07-templegold/protocol/contracts/core/OpsManager.sol#L97) ```solidity @@ -8234,12 +8246,30 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca for (uint256 i = 0; i < vaults.length; i++) { ``` +- Found in contracts/fakes/UniswapV2Router02NoEth.sol [Line: 362](../tests/2024-07-templegold/protocol/contracts/fakes/UniswapV2Router02NoEth.sol#L362) + + ```solidity + for (uint i; i < path.length - 1; i++) { + ``` + - Found in contracts/util/ABDKMath64x64.sol [Line: 411](../tests/2024-07-templegold/protocol/contracts/util/ABDKMath64x64.sol#L411) ```solidity while (y != 0) { ``` +- Found in contracts/v2/TreasuryReservesVault.sol [Line: 187](../tests/2024-07-templegold/protocol/contracts/v2/TreasuryReservesVault.sol#L187) + + ```solidity + for (uint256 i; i < _length; ++i) { + ``` + +- Found in contracts/v2/TreasuryReservesVault.sol [Line: 216](../tests/2024-07-templegold/protocol/contracts/v2/TreasuryReservesVault.sol#L216) + + ```solidity + for (; i < _length; ++i) { + ``` + - Found in contracts/v2/safeGuards/SafeForked.sol [Line: 78](../tests/2024-07-templegold/protocol/contracts/v2/safeGuards/SafeForked.sol#L78) ```solidity From f85551233b782d92d80b1cf4e8aff122009f0fcd Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 19 Oct 2024 15:14:50 +0530 Subject: [PATCH 24/25] Fix: Catch only ERC20 functions when reporting unsafe transfer (#775) --- aderyn_core/src/detect/low/unsafe_erc20_functions.rs | 4 +++- reports/templegold-report.md | 8 +------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/aderyn_core/src/detect/low/unsafe_erc20_functions.rs b/aderyn_core/src/detect/low/unsafe_erc20_functions.rs index a1ce45845..b2d300773 100644 --- a/aderyn_core/src/detect/low/unsafe_erc20_functions.rs +++ b/aderyn_core/src/detect/low/unsafe_erc20_functions.rs @@ -18,7 +18,9 @@ pub struct UnsafeERC20FunctionsDetector { impl IssueDetector for UnsafeERC20FunctionsDetector { fn detect(&mut self, context: &WorkspaceContext) -> Result> { for member_access in context.member_accesses() { - if member_access.member_name == "transferFrom" + if member_access.expression.as_ref().type_descriptions().is_some_and(|desc| { + desc.type_string.as_ref().is_some_and(|type_string| type_string.contains("ERC20")) + }) && member_access.member_name == "transferFrom" || member_access.member_name == "approve" || member_access.member_name == "transfer" { diff --git a/reports/templegold-report.md b/reports/templegold-report.md index bad5265c0..212075829 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -937,7 +937,7 @@ The `ecrecover` function is susceptible to signature malleability. This means th ERC20 functions may not behave as expected. For example: return values are not always meaningful. It is recommended to use OpenZeppelin's SafeERC20 library. -
5 Found Instances +
4 Found Instances - Found in contracts/amo/Ramos.sol [Line: 195](../tests/2024-07-templegold/protocol/contracts/amo/Ramos.sol#L195) @@ -958,12 +958,6 @@ ERC20 functions may not behave as expected. For example: return values are not a quoteToken.approve(address(balancerVault), 0); ``` -- Found in contracts/fakes/UniswapV2Router02NoEth.sol [Line: 108](../tests/2024-07-templegold/protocol/contracts/fakes/UniswapV2Router02NoEth.sol#L108) - - ```solidity - IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair - ``` - - Found in contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol [Line: 394](../tests/2024-07-templegold/protocol/contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol#L394) ```solidity From 2cda3d3e5f7c50c467f7d0cf4d429c6d22c41bb3 Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 19 Oct 2024 17:09:18 +0530 Subject: [PATCH 25/25] Fix: New Strategy for ArbitraryTransferFrom Detector (#776) --- .../detect/high/arbitrary_transfer_from.rs | 112 +++++++++++------- reports/report.json | 52 ++++---- reports/report.md | 28 ++--- reports/report.sarif | 36 ++---- reports/templegold-report.md | 58 ++------- .../src/ArbitraryTransferFrom.sol | 12 +- 6 files changed, 120 insertions(+), 178 deletions(-) diff --git a/aderyn_core/src/detect/high/arbitrary_transfer_from.rs b/aderyn_core/src/detect/high/arbitrary_transfer_from.rs index 2588e59c5..8782bbab4 100644 --- a/aderyn_core/src/detect/high/arbitrary_transfer_from.rs +++ b/aderyn_core/src/detect/high/arbitrary_transfer_from.rs @@ -1,11 +1,13 @@ use std::{collections::BTreeMap, error::Error}; -use crate::ast::{Expression, FunctionCall, NodeID, TypeName}; - use crate::{ + ast::{Expression, Identifier, NodeID}, capture, - context::workspace_context::WorkspaceContext, - detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + context::{browser::ExtractFunctionCalls, workspace_context::WorkspaceContext}, + detect::{ + detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity}, + helpers::{get_implemented_external_and_public_functions, has_msg_sender_binary_operation}, + }, }; use eyre::Result; @@ -16,51 +18,69 @@ pub struct ArbitraryTransferFromDetector { found_instances: BTreeMap<(String, usize, String), NodeID>, } -// Check if the first argument of the function call is valid -// In function calls with 3 args, the first arg [0] is the `from` address -// In function calls with 4 args, the second arg [1] is the `from` address -fn check_argument_validity(function_call: &FunctionCall) -> bool { - let arg_index = if function_call.arguments.len() == 3 { - 0 - } else if function_call.arguments.len() == 4 { - 1 - } else { - return false; - }; - - match &function_call.arguments[arg_index] { - Expression::MemberAccess(arg_member_access) => { - !(arg_member_access.member_name == "sender" - && matches!(&*arg_member_access.expression, Expression::Identifier(identifier) if identifier.name == "msg")) - } - Expression::FunctionCall(arg_function_call) => { - !(matches!(&*arg_function_call.expression, Expression::ElementaryTypeNameExpression(arg_el_type_name_exp) if matches!(&arg_el_type_name_exp.type_name, TypeName::ElementaryTypeName(type_name) if type_name.name == "address")) - && matches!(arg_function_call.arguments.first(), Some(Expression::Identifier(arg_identifier)) if arg_identifier.name == "this")) - } - _ => true, - } -} - 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); - } - } - false + // Applying devtooligan's suggestion + // * Operate on public and external functions only + // * See that msg.sender is not checked + // * Check that the argument passed in is from the parameter list of the said function + + let suspected_functions = + get_implemented_external_and_public_functions(context).filter(|function_definition| { + !has_msg_sender_binary_operation(&((*function_definition).into())) + && function_definition.modifiers.is_empty() // If there are modifiers, assume + // the function is safe because + // sometime modifiers' definition + // may not be in scope }); - for item in transfer_from_function_calls { - capture!(self, context, item); + for func in suspected_functions { + let func_parameters_ids = + &func.parameters.parameters.iter().map(|f| f.id).collect::>(); + + let transfer_func_calls = ExtractFunctionCalls::from(func) + .extracted + .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 true; + } + } + false + }) + .collect::>(); + + for func in transfer_func_calls { + // Check if the first argument of the function call is valid + // In function calls with 3 args, the first arg [0] is the `from` address + // In function calls with 4 args, the second arg [1] is the `from` address + let arg_index = if func.arguments.len() == 3 { + 0 + } else if func.arguments.len() == 4 { + 1 + } else { + continue; + }; + + let arg = &func.arguments[arg_index]; + + if let Expression::Identifier(Identifier { + referenced_declaration: Some(referenced_id), + .. + }) = arg + { + if func_parameters_ids.iter().any(|r| r == referenced_id) { + capture!(self, context, func); + } + } + } } Ok(!self.found_instances.is_empty()) @@ -107,7 +127,7 @@ mod arbitrary_transfer_from_tests { // assert that the detector found an issue assert!(found); // assert that the detector found the correct number of instances - assert_eq!(detector.instances().len(), 4); + assert_eq!(detector.instances().len(), 2); // assert the severity is high assert_eq!(detector.severity(), crate::detect::detector::IssueSeverity::High); // assert the title is correct diff --git a/reports/report.json b/reports/report.json index e2a5bc25a..a3db849d3 100644 --- a/reports/report.json +++ b/reports/report.json @@ -491,29 +491,17 @@ "description": "Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. ", "detector_name": "arbitrary-transfer-from", "instances": [ - { - "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 16, - "src": "370:38", - "src_char": "370:38" - }, - { - "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 20, - "src": "496:42", - "src_char": "496:42" - }, { "contract_path": "src/ArbitraryTransferFrom.sol", "line_no": 24, - "src": "634:53", - "src_char": "634:53" + "src": "738:42", + "src_char": "738:42" }, { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 30, - "src": "864:44", - "src_char": "864:44" + "line_no": 29, + "src": "879:53", + "src_char": "879:53" }, { "contract_path": "src/DeprecatedOZFunctions.sol", @@ -2353,21 +2341,21 @@ "instances": [ { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 16, - "src": "370:20", - "src_char": "370:20" + "line_no": 19, + "src": "601:20", + "src_char": "601:20" }, { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 30, - "src": "864:20", - "src_char": "864:20" + "line_no": 34, + "src": "1046:20", + "src_char": "1046:20" }, { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 50, - "src": "1517:20", - "src_char": "1517:20" + "line_no": 54, + "src": "1699:20", + "src_char": "1699:20" }, { "contract_path": "src/ContractLocksEther.sol", @@ -2770,9 +2758,9 @@ }, { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 28, - "src": "772:5", - "src_char": "772:5" + "line_no": 32, + "src": "954:5", + "src_char": "954:5" }, { "contract_path": "src/AssemblyExample.sol", @@ -5682,9 +5670,9 @@ "instances": [ { "contract_path": "src/ArbitraryTransferFrom.sol", - "line_no": 15, - "src": "304:4", - "src_char": "304:4" + "line_no": 18, + "src": "535:4", + "src_char": "535:4" }, { "contract_path": "src/ContractLocksEther.sol", diff --git a/reports/report.md b/reports/report.md index 3a1567b62..3938a4408 100644 --- a/reports/report.md +++ b/reports/report.md @@ -270,33 +270,21 @@ If all arguments are strings and or bytes, `bytes.concat()` should be used inste Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. -
6 Found Instances - - -- Found in src/ArbitraryTransferFrom.sol [Line: 16](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L16) +
4 Found Instances - ```solidity - s_token.transferFrom(from, to, amount); - ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 20](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L20) +- Found in src/ArbitraryTransferFrom.sol [Line: 24](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L24) ```solidity s_token.safeTransferFrom(from, to, amount); ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 24](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L24) +- Found in src/ArbitraryTransferFrom.sol [Line: 29](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L29) ```solidity SafeERC20.safeTransferFrom(s_token, from, to, amount); ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 30](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L30) - - ```solidity - s_token.transferFrom(from_msgsender, to, am); - ``` - - Found in src/DeprecatedOZFunctions.sol [Line: 17](../tests/contract-playground/src/DeprecatedOZFunctions.sol#L17) ```solidity @@ -2305,19 +2293,19 @@ ERC20 functions may not behave as expected. For example: return values are not a
19 Found Instances -- Found in src/ArbitraryTransferFrom.sol [Line: 16](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L16) +- Found in src/ArbitraryTransferFrom.sol [Line: 19](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L19) ```solidity s_token.transferFrom(from, to, amount); ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 30](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L30) +- Found in src/ArbitraryTransferFrom.sol [Line: 34](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L34) ```solidity s_token.transferFrom(from_msgsender, to, am); ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 50](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L50) +- Found in src/ArbitraryTransferFrom.sol [Line: 54](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L54) ```solidity s_token.transferFrom(msg.sender, to, amount); @@ -2734,7 +2722,7 @@ Instead of marking a function as `public`, consider marking it as `external` if function f4() public { ``` -- Found in src/ArbitraryTransferFrom.sol [Line: 28](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L28) +- Found in src/ArbitraryTransferFrom.sol [Line: 32](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L32) ```solidity function good1(address to, uint256 am) public { @@ -5727,7 +5715,7 @@ Functions that are not used. Consider removing them.
12 Found Instances -- Found in src/ArbitraryTransferFrom.sol [Line: 15](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L15) +- Found in src/ArbitraryTransferFrom.sol [Line: 18](../tests/contract-playground/src/ArbitraryTransferFrom.sol#L18) ```solidity function bad1(address from, address to, uint256 amount) internal { diff --git a/reports/report.sarif b/reports/report.sarif index a58610af9..41e8e77b4 100644 --- a/reports/report.sarif +++ b/reports/report.sarif @@ -49,17 +49,6 @@ { "level": "warning", "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/ArbitraryTransferFrom.sol" - }, - "region": { - "byteLength": 38, - "byteOffset": 370 - } - } - }, { "physicalLocation": { "artifactLocation": { @@ -67,7 +56,7 @@ }, "region": { "byteLength": 42, - "byteOffset": 496 + "byteOffset": 738 } } }, @@ -78,18 +67,7 @@ }, "region": { "byteLength": 53, - "byteOffset": 634 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "src/ArbitraryTransferFrom.sol" - }, - "region": { - "byteLength": 44, - "byteOffset": 864 + "byteOffset": 879 } } }, @@ -3300,7 +3278,7 @@ }, "region": { "byteLength": 20, - "byteOffset": 370 + "byteOffset": 601 } } }, @@ -3311,7 +3289,7 @@ }, "region": { "byteLength": 20, - "byteOffset": 864 + "byteOffset": 1046 } } }, @@ -3322,7 +3300,7 @@ }, "region": { "byteLength": 20, - "byteOffset": 1517 + "byteOffset": 1699 } } }, @@ -4053,7 +4031,7 @@ }, "region": { "byteLength": 5, - "byteOffset": 772 + "byteOffset": 954 } } }, @@ -9292,7 +9270,7 @@ }, "region": { "byteLength": 4, - "byteOffset": 304 + "byteOffset": 535 } } }, diff --git a/reports/templegold-report.md b/reports/templegold-report.md index 212075829..b8377e2dd 100644 --- a/reports/templegold-report.md +++ b/reports/templegold-report.md @@ -8,12 +8,11 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [Files Details](#files-details) - [Issue Summary](#issue-summary) - [High Issues](#high-issues) - - [H-1: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`)](#h-1-arbitrary-from-passed-to-transferfrom-or-safetransferfrom) - - [H-2: Unsafe Casting](#h-2-unsafe-casting) - - [H-3: Contract Name Reused in Different Files](#h-3-contract-name-reused-in-different-files) - - [H-4: Weak Randomness](#h-4-weak-randomness) - - [H-5: Deletion from a nested mappping.](#h-5-deletion-from-a-nested-mappping) - - [H-6: Contract locks Ether without a withdraw function.](#h-6-contract-locks-ether-without-a-withdraw-function) + - [H-1: Unsafe Casting](#h-1-unsafe-casting) + - [H-2: Contract Name Reused in Different Files](#h-2-contract-name-reused-in-different-files) + - [H-3: Weak Randomness](#h-3-weak-randomness) + - [H-4: Deletion from a nested mappping.](#h-4-deletion-from-a-nested-mappping) + - [H-5: Contract locks Ether without a withdraw function.](#h-5-contract-locks-ether-without-a-withdraw-function) - [Low Issues](#low-issues) - [L-1: Centralization Risk for trusted owners](#l-1-centralization-risk-for-trusted-owners) - [L-2: `ecrecover` is susceptible to signature malleability](#l-2-ecrecover-is-susceptible-to-signature-malleability) @@ -195,48 +194,13 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | -| High | 6 | +| High | 5 | | Low | 28 | # High Issues -## H-1: Arbitrary `from` passed to `transferFrom` (or `safeTransferFrom`) - -Passing an arbitrary `from` address to `transferFrom` (or `safeTransferFrom`) can lead to loss of funds, because anyone can transfer tokens from the `from` address if an approval is made. - -
4 Found Instances - - -- Found in contracts/core/MultiOtcOffer.sol [Line: 306](../tests/2024-07-templegold/protocol/contracts/core/MultiOtcOffer.sol#L306) - - ```solidity - _userBuyToken.safeTransferFrom(_fundsOwner, msg.sender, buyTokenAmount); - ``` - -- Found in contracts/core/OtcOffer.sol [Line: 140](../tests/2024-07-templegold/protocol/contracts/core/OtcOffer.sol#L140) - - ```solidity - userBuyToken.safeTransferFrom(_fundsOwner, msg.sender, buyTokenAmount); - ``` - -- Found in contracts/v2/TreasuryReservesVault.sol [Line: 776](../tests/2024-07-templegold/protocol/contracts/v2/TreasuryReservesVault.sol#L776) - - ```solidity - token.safeTransferFrom(from, address(this), repayAmount); - ``` - -- Found in contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol [Line: 717](../tests/2024-07-templegold/protocol/contracts/v2/templeLineOfCredit/TempleLineOfCredit.sol#L717) - - ```solidity - daiToken.safeTransferFrom(_fromAccount, address(this), _repayAmount); - ``` - -
- - - -## H-2: Unsafe Casting +## H-1: Unsafe Casting Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior.When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type,leading to truncation and loss of significant digits. Use OpenZeppelin's SafeCast library to safely downcast integers. @@ -259,7 +223,7 @@ Downcasting int/uints in Solidity can be unsafe due to the potential for data lo -## H-3: Contract Name Reused in Different Files +## H-2: Contract Name Reused in Different Files When compiling contracts with certain development frameworks (for example: Truffle), having contracts with the same name across different files can lead to one being overwritten. @@ -294,7 +258,7 @@ When compiling contracts with certain development frameworks (for example: Truff -## H-4: Weak Randomness +## H-3: Weak Randomness The use of keccak256 hash functions on predictable values like block.timestamp, block.number, or similar data, including modulo operations on these values, should be avoided for generating randomness, as they are easily predictable and manipulable. The `PREVRANDAO` opcode also should not be used as a source of randomness. Instead, utilize Chainlink VRF for cryptographically secure and provably random values to ensure protocol integrity. @@ -311,7 +275,7 @@ The use of keccak256 hash functions on predictable values like block.timestamp, -## H-5: Deletion from a nested mappping. +## H-4: Deletion from a nested mappping. A deletion in a structure containing a mapping will not delete the mapping. The remaining data may be used to compromise the contract. @@ -328,7 +292,7 @@ A deletion in a structure containing a mapping will not delete the mapping. The -## H-6: Contract locks Ether without a withdraw function. +## H-5: Contract locks Ether without a withdraw function. It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract. diff --git a/tests/contract-playground/src/ArbitraryTransferFrom.sol b/tests/contract-playground/src/ArbitraryTransferFrom.sol index 2f0517550..39e2515fb 100644 --- a/tests/contract-playground/src/ArbitraryTransferFrom.sol +++ b/tests/contract-playground/src/ArbitraryTransferFrom.sol @@ -12,19 +12,23 @@ contract ArbitraryTransferFrom { s_token = token; } + // This maybe bad but it's not safe to conclude because it's an internal function + // Parameters of this function could be vetted before calling. @devtooligan suggested to stick + // to public & etxernal functions for now function bad1(address from, address to, uint256 amount) internal { s_token.transferFrom(from, to, amount); } + // BAD function bad2(address from, address to, uint256 amount) external { s_token.safeTransferFrom(from, to, amount); - } - + } + + // BAD function bad3(address from, address to, uint256 amount) external { SafeERC20.safeTransferFrom(s_token, from, to, amount); } - // ArbitraryTransferFromDetector has a false positive here function good1(address to, uint256 am) public { address from_msgsender = msg.sender; s_token.transferFrom(from_msgsender, to, am); @@ -49,5 +53,5 @@ contract ArbitraryTransferFrom { function good6(address to, uint256 amount) external { s_token.transferFrom(msg.sender, to, amount); } +} -} \ No newline at end of file