Skip to content

Commit

Permalink
fix: security fixes (#471)
Browse files Browse the repository at this point in the history
* fixing issues found during audit

* fix fallback check

* fix compilation issues

* fix tests

* fix tests

* disable fp slither issue

* bump slither version

Co-authored-by: david roon <david@roon.me>
  • Loading branch information
fforbeck and adridadou authored Jan 11, 2022
1 parent fc9473d commit 57f6d2c
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/slither.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Install Slither
run: |
python -m pip install --upgrade pip
pip3 install slither-analyzer==0.8.1 solc-select==0.2.1
pip3 install slither-analyzer==0.8.2 solc-select==0.2.1
- name: Summary of static analysis
run: |
Expand Down
16 changes: 12 additions & 4 deletions contracts/adapters/voting/OffchainVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,22 @@ contract OffchainVotingContract is
external
reentrancyGuard(dao)
onlyMember(dao)
reimbursable(dao)
{
// slither-disable-next-line timestamp
VotingState state = voteResult(dao, proposalId);
require(
state != VotingState.PASS &&
state != VotingState.NOT_PASS &&
state != VotingState.TIE,
"voting ended"
);

address memberAddr = dao.getAddressIfDelegated(msg.sender);
// slither-disable-next-line timestamp,incorrect-equality
require(
votes[address(dao)][proposalId].fallbackVotes[msg.sender] == false,
votes[address(dao)][proposalId].fallbackVotes[memberAddr] == false,
"fallback vote duplicate"
);
votes[address(dao)][proposalId].fallbackVotes[msg.sender] = true;
votes[address(dao)][proposalId].fallbackVotes[memberAddr] = true;
votes[address(dao)][proposalId].fallbackVotesCount += 1;

if (
Expand Down
4 changes: 4 additions & 0 deletions contracts/core/DaoRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ contract DaoRegistry is MemberGuard, AdapterGuard {
* @dev Sets the state of the dao to READY
*/
function finalizeDao() external {
require(
isActiveMember(this, msg.sender) || isAdapter(msg.sender),
"not allowed to finalize"
);
state = DaoState.READY;
}

Expand Down
5 changes: 3 additions & 2 deletions contracts/extensions/bank/Bank.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

contract BankExtension is AdapterGuard, IExtension, ERC165 {
contract BankExtension is IExtension, ERC165 {
using Address for address payable;
using SafeERC20 for IERC20;

Expand Down Expand Up @@ -95,11 +95,12 @@ contract BankExtension is AdapterGuard, IExtension, ERC165 {
/// @notice Clonable contract must have an empty constructor
constructor() {}

// slither-disable-next-line calls-loop
modifier hasExtensionAccess(AclFlag flag) {
require(
address(this) == msg.sender ||
address(dao) == msg.sender ||
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccessToExtension(
msg.sender,
address(this),
Expand Down
11 changes: 3 additions & 8 deletions contracts/extensions/erc1155/ERC1155TokenExtension.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
import "../../core/DaoRegistry.sol";
import "../../guards/AdapterGuard.sol";
import "../../guards/MemberGuard.sol";
import "../../helpers/DaoHelper.sol";
import "../IExtension.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
Expand Down Expand Up @@ -33,12 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

contract ERC1155TokenExtension is
AdapterGuard,
MemberGuard,
IExtension,
IERC1155Receiver
{
contract ERC1155TokenExtension is MemberGuard, IExtension, IERC1155Receiver {
using Address for address payable;
//LIBRARIES
using EnumerableSet for EnumerableSet.UintSet;
Expand Down Expand Up @@ -87,7 +82,7 @@ contract ERC1155TokenExtension is
//MODIFIERS
modifier hasExtensionAccess(IExtension extension, AclFlag flag) {
require(
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccessToExtension(
msg.sender,
address(extension),
Expand Down
5 changes: 3 additions & 2 deletions contracts/extensions/erc1271/ERC1271.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.0;

import "../../core/DaoRegistry.sol";
import "../IExtension.sol";
import "../../helpers/DaoHelper.sol";
import "../../guards/AdapterGuard.sol";
import "@openzeppelin/contracts/interfaces/IERC1271.sol";

Expand Down Expand Up @@ -34,7 +35,7 @@ SOFTWARE.
/**
* @dev Signs arbitrary messages and exposes ERC1271 interface
*/
contract ERC1271Extension is AdapterGuard, IExtension, IERC1271 {
contract ERC1271Extension is IExtension, IERC1271 {
using Address for address payable;

bool public initialized = false; // internally tracks deployment under eip-1167 proxy pattern
Expand All @@ -58,7 +59,7 @@ contract ERC1271Extension is AdapterGuard, IExtension, IERC1271 {
require(
address(this) == msg.sender ||
address(dao) == msg.sender ||
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccessToExtension(
msg.sender,
address(this),
Expand Down
5 changes: 2 additions & 3 deletions contracts/extensions/executor/Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity ^0.8.0;

import "../../core/DaoRegistry.sol";
import "../IExtension.sol";
import "../../guards/AdapterGuard.sol";

/**
MIT License
Expand Down Expand Up @@ -39,7 +38,7 @@ SOFTWARE.
* This contract was based on the OpenZeppelin Proxy contract:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol
*/
contract ExecutorExtension is AdapterGuard, IExtension {
contract ExecutorExtension is IExtension {
using Address for address payable;

bool public initialized = false; // internally tracks deployment under eip-1167 proxy pattern
Expand All @@ -56,7 +55,7 @@ contract ExecutorExtension is AdapterGuard, IExtension {
require(
address(this) == msg.sender ||
address(dao) == msg.sender ||
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccessToExtension(
msg.sender,
address(this),
Expand Down
5 changes: 2 additions & 3 deletions contracts/extensions/nft/NFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT

import "../../core/DaoRegistry.sol";
import "../../guards/AdapterGuard.sol";
import "../IExtension.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
Expand Down Expand Up @@ -34,7 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

contract NFTExtension is AdapterGuard, IExtension, IERC721Receiver {
contract NFTExtension is IExtension, IERC721Receiver {
using Address for address payable;
// Add the library methods
using EnumerableSet for EnumerableSet.UintSet;
Expand Down Expand Up @@ -69,7 +68,7 @@ contract NFTExtension is AdapterGuard, IExtension, IERC721Receiver {

modifier hasExtensionAccess(IExtension extension, AclFlag flag) {
require(
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccessToExtension(
msg.sender,
address(extension),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
import "../../../core/DaoRegistry.sol";
import "../../../extensions/IExtension.sol";
import "../../../helpers/DaoHelper.sol";

/**
MIT License
Expand Down Expand Up @@ -45,7 +46,7 @@ contract InternalTokenVestingExtension is IExtension {

modifier hasExtensionAccess(AclFlag flag) {
require(
_dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(_dao) ||
_dao.hasAdapterAccessToExtension(
msg.sender,
address(this),
Expand Down
16 changes: 4 additions & 12 deletions contracts/guards/AdapterGuard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT

import "../core/DaoRegistry.sol";
import "../extensions/IExtension.sol";
import "../helpers/DaoHelper.sol";

/**
MIT License
Expand Down Expand Up @@ -34,8 +34,8 @@ abstract contract AdapterGuard {
*/
modifier onlyAdapter(DaoRegistry dao) {
require(
(dao.state() == DaoRegistry.DaoState.CREATION &&
creationModeCheck(dao)) || dao.isAdapter(msg.sender),
dao.isAdapter(msg.sender) ||
DaoHelper.isInCreationModeAndHasAccess(dao),
"onlyAdapter"
);
_;
Expand All @@ -58,18 +58,10 @@ abstract contract AdapterGuard {

modifier hasAccess(DaoRegistry dao, DaoRegistry.AclFlag flag) {
require(
(dao.state() == DaoRegistry.DaoState.CREATION &&
creationModeCheck(dao)) ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
dao.hasAdapterAccess(msg.sender, flag),
"accessDenied"
);
_;
}

function creationModeCheck(DaoRegistry dao) internal view returns (bool) {
return
dao.getNbMembers() == 0 ||
dao.isMember(msg.sender) ||
dao.isAdapter(msg.sender);
}
}
19 changes: 19 additions & 0 deletions contracts/helpers/DaoHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,23 @@ library DaoHelper {
}
}
}

/**
* A DAO is in creation mode is the state of the DAO is equals to CREATION and
* 1. The number of members in the DAO is ZERO or,
* 2. The sender of the tx is a DAO member (usually the DAO owner) or,
* 3. The sender is an adapter.
*/
// slither-disable-next-line calls-loop
function isInCreationModeAndHasAccess(DaoRegistry dao)
internal
view
returns (bool)
{
return
dao.state() == DaoRegistry.DaoState.CREATION &&
(dao.getNbMembers() == 0 ||
dao.isMember(msg.sender) ||
dao.isAdapter(msg.sender));
}
}
4 changes: 2 additions & 2 deletions contracts/helpers/OffchainVotingHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ contract OffchainVotingHelperContract {
) external view returns (bool) {
return
fallbackVotesCount >
(dao.getNbMembers() * 100) /
dao.getConfiguration(FallbackThreshold);
(dao.getNbMembers() * dao.getConfiguration(FallbackThreshold)) /
100;
}

function isReadyToSubmitResult(
Expand Down
18 changes: 11 additions & 7 deletions test/extensions/vesting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ describe("Extension - Vesting", () => {
daoOwner,
UNITS,
1000,
Math.floor(now.getTime() / 1000)
Math.floor(now.getTime() / 1000),
{ from: daoOwner }
);

const v = await vesting.vesting(daoOwner, UNITS);
Expand Down Expand Up @@ -107,7 +108,8 @@ describe("Extension - Vesting", () => {
daoOwner,
UNITS,
100,
Math.floor(now.getTime() / 1000)
Math.floor(now.getTime() / 1000),
{ from: daoOwner }
);

let v = await vesting.vesting(daoOwner, UNITS);
Expand All @@ -126,7 +128,8 @@ describe("Extension - Vesting", () => {
daoOwner,
UNITS,
100,
Math.floor(now.getTime() / 1000)
Math.floor(now.getTime() / 1000),
{ from: daoOwner }
);

v = await vesting.vesting(daoOwner, UNITS);
Expand Down Expand Up @@ -184,7 +187,8 @@ describe("Extension - Vesting", () => {
daoOwner,
UNITS,
100,
Math.floor(now.getTime() / 1000)
Math.floor(now.getTime() / 1000),
{ from: daoOwner }
);

v = await vesting.vesting(daoOwner, UNITS);
Expand All @@ -203,7 +207,7 @@ describe("Extension - Vesting", () => {
minBalance.toString() === "75" || minBalance.toString() === "76"
).equal(true);

await vesting.removeVesting(daoOwner, UNITS, 50);
await vesting.removeVesting(daoOwner, UNITS, 50, { from: daoOwner });

minBalance = await vesting.getMinimumBalance(daoOwner, UNITS);
expect(
Expand All @@ -213,7 +217,7 @@ describe("Extension - Vesting", () => {

it("should not be possible to create a new vesting without the ACL permission", async () => {
// Finalize the DAO to be able to check the extension permissions
await this.dao.finalizeDao();
await this.dao.finalizeDao({ from: daoOwner });
const vesting = this.extensions.vestingExt;
const now = new Date();
await expectRevert(
Expand All @@ -230,7 +234,7 @@ describe("Extension - Vesting", () => {

it("should not be possible to removeVesting a vesting schedule the without ACL permission", async () => {
// Finalize the DAO to be able to check the extension permissions
await this.dao.finalizeDao();
await this.dao.finalizeDao({ from: daoOwner });
const vesting = this.extensions.vestingExt;
await expectRevert(
vesting.removeVesting(daoOwner, UNITS, 100, { from: daoOwner }),
Expand Down
4 changes: 2 additions & 2 deletions website/docs/tutorial/extensions/HowToCreateAnExtension.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Example:
// 2. Allowed if the DAO is calling the extension
address(dao) == msg.sender ||
// 3. Allowed if the DAO state is in CREATION mode
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
// 4. Allowed if the sender is a registered adapter
dao.hasAdapterAccessToExtension(
msg.sender,
Expand Down Expand Up @@ -167,7 +167,7 @@ contract MyExtension is DaoConstants, IExtension {
// 2. Allowed if the DAO is calling the extension
address(dao) == msg.sender ||
// 3. Allowed if the DAO state is in CREATION mode
dao.state() == DaoRegistry.DaoState.CREATION ||
DaoHelper.isInCreationModeAndHasAccess(dao) ||
// 4. Allowed if the sender is a registered adapter
dao.hasAdapterAccessToExtension(msg.sender, address(this), uint8(flag)),
// 5. Revert message
Expand Down

0 comments on commit 57f6d2c

Please sign in to comment.